##// END OF EJS Templates
diff: add --inverse option...
Yannick Gingras -
r9725:3f522d2f default
parent child Browse files
Show More
@@ -0,0 +1,24 b''
1 #!/bin/sh
2
3 hg init
4 cat > a <<EOF
5 a
6 b
7 c
8 EOF
9 hg ci -Am adda
10
11 cat > a <<EOF
12 d
13 e
14 f
15 EOF
16 hg ci -m moda
17
18 hg diff --inverse -r0 -r1
19
20 cat >> a <<EOF
21 g
22 h
23 EOF
24 hg diff --inverse --nodates
@@ -0,0 +1,20 b''
1 adding a
2 diff -r 2855cdcfcbb7 -r 8e1805a3cf6e a
3 --- a/a Thu Jan 01 00:00:00 1970 +0000
4 +++ b/a Thu Jan 01 00:00:00 1970 +0000
5 @@ -1,3 +1,3 @@
6 -d
7 -e
8 -f
9 +a
10 +b
11 +c
12 diff -r 2855cdcfcbb7 a
13 --- a/a
14 +++ b/a
15 @@ -1,5 +1,3 @@
16 d
17 e
18 f
19 -g
20 -h
@@ -1,2694 +1,2698 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details)::
17 Common tasks (use "hg help command" for more details)::
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25
25
26 add known patch to applied stack qpush
26 add known patch to applied stack qpush
27 remove patch from applied stack qpop
27 remove patch from applied stack qpop
28 refresh contents of top applied patch qrefresh
28 refresh contents of top applied patch qrefresh
29 '''
29 '''
30
30
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32 from mercurial.node import bin, hex, short, nullid, nullrev
32 from mercurial.node import bin, hex, short, nullid, nullrev
33 from mercurial.lock import release
33 from mercurial.lock import release
34 from mercurial import commands, cmdutil, hg, patch, util
34 from mercurial import commands, cmdutil, hg, patch, util
35 from mercurial import repair, extensions, url, error
35 from mercurial import repair, extensions, url, error
36 import os, sys, re, errno
36 import os, sys, re, errno
37
37
38 commands.norepo += " qclone"
38 commands.norepo += " qclone"
39
39
40 # Patch names looks like unix-file names.
40 # Patch names looks like unix-file names.
41 # They must be joinable with queue directory and result in the patch path.
41 # They must be joinable with queue directory and result in the patch path.
42 normname = util.normpath
42 normname = util.normpath
43
43
44 class statusentry(object):
44 class statusentry(object):
45 def __init__(self, rev, name=None):
45 def __init__(self, rev, name=None):
46 if not name:
46 if not name:
47 fields = rev.split(':', 1)
47 fields = rev.split(':', 1)
48 if len(fields) == 2:
48 if len(fields) == 2:
49 self.rev, self.name = fields
49 self.rev, self.name = fields
50 else:
50 else:
51 self.rev, self.name = None, None
51 self.rev, self.name = None, None
52 else:
52 else:
53 self.rev, self.name = rev, name
53 self.rev, self.name = rev, name
54
54
55 def __str__(self):
55 def __str__(self):
56 return self.rev + ':' + self.name
56 return self.rev + ':' + self.name
57
57
58 class patchheader(object):
58 class patchheader(object):
59 def __init__(self, pf):
59 def __init__(self, pf):
60 def eatdiff(lines):
60 def eatdiff(lines):
61 while lines:
61 while lines:
62 l = lines[-1]
62 l = lines[-1]
63 if (l.startswith("diff -") or
63 if (l.startswith("diff -") or
64 l.startswith("Index:") or
64 l.startswith("Index:") or
65 l.startswith("===========")):
65 l.startswith("===========")):
66 del lines[-1]
66 del lines[-1]
67 else:
67 else:
68 break
68 break
69 def eatempty(lines):
69 def eatempty(lines):
70 while lines:
70 while lines:
71 l = lines[-1]
71 l = lines[-1]
72 if re.match('\s*$', l):
72 if re.match('\s*$', l):
73 del lines[-1]
73 del lines[-1]
74 else:
74 else:
75 break
75 break
76
76
77 message = []
77 message = []
78 comments = []
78 comments = []
79 user = None
79 user = None
80 date = None
80 date = None
81 format = None
81 format = None
82 subject = None
82 subject = None
83 diffstart = 0
83 diffstart = 0
84
84
85 for line in file(pf):
85 for line in file(pf):
86 line = line.rstrip()
86 line = line.rstrip()
87 if line.startswith('diff --git'):
87 if line.startswith('diff --git'):
88 diffstart = 2
88 diffstart = 2
89 break
89 break
90 if diffstart:
90 if diffstart:
91 if line.startswith('+++ '):
91 if line.startswith('+++ '):
92 diffstart = 2
92 diffstart = 2
93 break
93 break
94 if line.startswith("--- "):
94 if line.startswith("--- "):
95 diffstart = 1
95 diffstart = 1
96 continue
96 continue
97 elif format == "hgpatch":
97 elif format == "hgpatch":
98 # parse values when importing the result of an hg export
98 # parse values when importing the result of an hg export
99 if line.startswith("# User "):
99 if line.startswith("# User "):
100 user = line[7:]
100 user = line[7:]
101 elif line.startswith("# Date "):
101 elif line.startswith("# Date "):
102 date = line[7:]
102 date = line[7:]
103 elif not line.startswith("# ") and line:
103 elif not line.startswith("# ") and line:
104 message.append(line)
104 message.append(line)
105 format = None
105 format = None
106 elif line == '# HG changeset patch':
106 elif line == '# HG changeset patch':
107 message = []
107 message = []
108 format = "hgpatch"
108 format = "hgpatch"
109 elif (format != "tagdone" and (line.startswith("Subject: ") or
109 elif (format != "tagdone" and (line.startswith("Subject: ") or
110 line.startswith("subject: "))):
110 line.startswith("subject: "))):
111 subject = line[9:]
111 subject = line[9:]
112 format = "tag"
112 format = "tag"
113 elif (format != "tagdone" and (line.startswith("From: ") or
113 elif (format != "tagdone" and (line.startswith("From: ") or
114 line.startswith("from: "))):
114 line.startswith("from: "))):
115 user = line[6:]
115 user = line[6:]
116 format = "tag"
116 format = "tag"
117 elif format == "tag" and line == "":
117 elif format == "tag" and line == "":
118 # when looking for tags (subject: from: etc) they
118 # when looking for tags (subject: from: etc) they
119 # end once you find a blank line in the source
119 # end once you find a blank line in the source
120 format = "tagdone"
120 format = "tagdone"
121 elif message or line:
121 elif message or line:
122 message.append(line)
122 message.append(line)
123 comments.append(line)
123 comments.append(line)
124
124
125 eatdiff(message)
125 eatdiff(message)
126 eatdiff(comments)
126 eatdiff(comments)
127 eatempty(message)
127 eatempty(message)
128 eatempty(comments)
128 eatempty(comments)
129
129
130 # make sure message isn't empty
130 # make sure message isn't empty
131 if format and format.startswith("tag") and subject:
131 if format and format.startswith("tag") and subject:
132 message.insert(0, "")
132 message.insert(0, "")
133 message.insert(0, subject)
133 message.insert(0, subject)
134
134
135 self.message = message
135 self.message = message
136 self.comments = comments
136 self.comments = comments
137 self.user = user
137 self.user = user
138 self.date = date
138 self.date = date
139 self.haspatch = diffstart > 1
139 self.haspatch = diffstart > 1
140
140
141 def setuser(self, user):
141 def setuser(self, user):
142 if not self.updateheader(['From: ', '# User '], user):
142 if not self.updateheader(['From: ', '# User '], user):
143 try:
143 try:
144 patchheaderat = self.comments.index('# HG changeset patch')
144 patchheaderat = self.comments.index('# HG changeset patch')
145 self.comments.insert(patchheaderat + 1, '# User ' + user)
145 self.comments.insert(patchheaderat + 1, '# User ' + user)
146 except ValueError:
146 except ValueError:
147 if self._hasheader(['Date: ']):
147 if self._hasheader(['Date: ']):
148 self.comments = ['From: ' + user] + self.comments
148 self.comments = ['From: ' + user] + self.comments
149 else:
149 else:
150 tmp = ['# HG changeset patch', '# User ' + user, '']
150 tmp = ['# HG changeset patch', '# User ' + user, '']
151 self.comments = tmp + self.comments
151 self.comments = tmp + self.comments
152 self.user = user
152 self.user = user
153
153
154 def setdate(self, date):
154 def setdate(self, date):
155 if not self.updateheader(['Date: ', '# Date '], date):
155 if not self.updateheader(['Date: ', '# Date '], date):
156 try:
156 try:
157 patchheaderat = self.comments.index('# HG changeset patch')
157 patchheaderat = self.comments.index('# HG changeset patch')
158 self.comments.insert(patchheaderat + 1, '# Date ' + date)
158 self.comments.insert(patchheaderat + 1, '# Date ' + date)
159 except ValueError:
159 except ValueError:
160 if self._hasheader(['From: ']):
160 if self._hasheader(['From: ']):
161 self.comments = ['Date: ' + date] + self.comments
161 self.comments = ['Date: ' + date] + self.comments
162 else:
162 else:
163 tmp = ['# HG changeset patch', '# Date ' + date, '']
163 tmp = ['# HG changeset patch', '# Date ' + date, '']
164 self.comments = tmp + self.comments
164 self.comments = tmp + self.comments
165 self.date = date
165 self.date = date
166
166
167 def setmessage(self, message):
167 def setmessage(self, message):
168 if self.comments:
168 if self.comments:
169 self._delmsg()
169 self._delmsg()
170 self.message = [message]
170 self.message = [message]
171 self.comments += self.message
171 self.comments += self.message
172
172
173 def updateheader(self, prefixes, new):
173 def updateheader(self, prefixes, new):
174 '''Update all references to a field in the patch header.
174 '''Update all references to a field in the patch header.
175 Return whether the field is present.'''
175 Return whether the field is present.'''
176 res = False
176 res = False
177 for prefix in prefixes:
177 for prefix in prefixes:
178 for i in xrange(len(self.comments)):
178 for i in xrange(len(self.comments)):
179 if self.comments[i].startswith(prefix):
179 if self.comments[i].startswith(prefix):
180 self.comments[i] = prefix + new
180 self.comments[i] = prefix + new
181 res = True
181 res = True
182 break
182 break
183 return res
183 return res
184
184
185 def _hasheader(self, prefixes):
185 def _hasheader(self, prefixes):
186 '''Check if a header starts with any of the given prefixes.'''
186 '''Check if a header starts with any of the given prefixes.'''
187 for prefix in prefixes:
187 for prefix in prefixes:
188 for comment in self.comments:
188 for comment in self.comments:
189 if comment.startswith(prefix):
189 if comment.startswith(prefix):
190 return True
190 return True
191 return False
191 return False
192
192
193 def __str__(self):
193 def __str__(self):
194 if not self.comments:
194 if not self.comments:
195 return ''
195 return ''
196 return '\n'.join(self.comments) + '\n\n'
196 return '\n'.join(self.comments) + '\n\n'
197
197
198 def _delmsg(self):
198 def _delmsg(self):
199 '''Remove existing message, keeping the rest of the comments fields.
199 '''Remove existing message, keeping the rest of the comments fields.
200 If comments contains 'subject: ', message will prepend
200 If comments contains 'subject: ', message will prepend
201 the field and a blank line.'''
201 the field and a blank line.'''
202 if self.message:
202 if self.message:
203 subj = 'subject: ' + self.message[0].lower()
203 subj = 'subject: ' + self.message[0].lower()
204 for i in xrange(len(self.comments)):
204 for i in xrange(len(self.comments)):
205 if subj == self.comments[i].lower():
205 if subj == self.comments[i].lower():
206 del self.comments[i]
206 del self.comments[i]
207 self.message = self.message[2:]
207 self.message = self.message[2:]
208 break
208 break
209 ci = 0
209 ci = 0
210 for mi in self.message:
210 for mi in self.message:
211 while mi != self.comments[ci]:
211 while mi != self.comments[ci]:
212 ci += 1
212 ci += 1
213 del self.comments[ci]
213 del self.comments[ci]
214
214
215 class queue(object):
215 class queue(object):
216 def __init__(self, ui, path, patchdir=None):
216 def __init__(self, ui, path, patchdir=None):
217 self.basepath = path
217 self.basepath = path
218 self.path = patchdir or os.path.join(path, "patches")
218 self.path = patchdir or os.path.join(path, "patches")
219 self.opener = util.opener(self.path)
219 self.opener = util.opener(self.path)
220 self.ui = ui
220 self.ui = ui
221 self.applied_dirty = 0
221 self.applied_dirty = 0
222 self.series_dirty = 0
222 self.series_dirty = 0
223 self.series_path = "series"
223 self.series_path = "series"
224 self.status_path = "status"
224 self.status_path = "status"
225 self.guards_path = "guards"
225 self.guards_path = "guards"
226 self.active_guards = None
226 self.active_guards = None
227 self.guards_dirty = False
227 self.guards_dirty = False
228 self._diffopts = None
228 self._diffopts = None
229
229
230 @util.propertycache
230 @util.propertycache
231 def applied(self):
231 def applied(self):
232 if os.path.exists(self.join(self.status_path)):
232 if os.path.exists(self.join(self.status_path)):
233 lines = self.opener(self.status_path).read().splitlines()
233 lines = self.opener(self.status_path).read().splitlines()
234 return [statusentry(l) for l in lines]
234 return [statusentry(l) for l in lines]
235 return []
235 return []
236
236
237 @util.propertycache
237 @util.propertycache
238 def full_series(self):
238 def full_series(self):
239 if os.path.exists(self.join(self.series_path)):
239 if os.path.exists(self.join(self.series_path)):
240 return self.opener(self.series_path).read().splitlines()
240 return self.opener(self.series_path).read().splitlines()
241 return []
241 return []
242
242
243 @util.propertycache
243 @util.propertycache
244 def series(self):
244 def series(self):
245 self.parse_series()
245 self.parse_series()
246 return self.series
246 return self.series
247
247
248 @util.propertycache
248 @util.propertycache
249 def series_guards(self):
249 def series_guards(self):
250 self.parse_series()
250 self.parse_series()
251 return self.series_guards
251 return self.series_guards
252
252
253 def invalidate(self):
253 def invalidate(self):
254 for a in 'applied full_series series series_guards'.split():
254 for a in 'applied full_series series series_guards'.split():
255 if a in self.__dict__:
255 if a in self.__dict__:
256 delattr(self, a)
256 delattr(self, a)
257 self.applied_dirty = 0
257 self.applied_dirty = 0
258 self.series_dirty = 0
258 self.series_dirty = 0
259 self.guards_dirty = False
259 self.guards_dirty = False
260 self.active_guards = None
260 self.active_guards = None
261
261
262 def diffopts(self):
262 def diffopts(self):
263 if self._diffopts is None:
263 if self._diffopts is None:
264 self._diffopts = patch.diffopts(self.ui)
264 self._diffopts = patch.diffopts(self.ui)
265 return self._diffopts
265 return self._diffopts
266
266
267 def join(self, *p):
267 def join(self, *p):
268 return os.path.join(self.path, *p)
268 return os.path.join(self.path, *p)
269
269
270 def find_series(self, patch):
270 def find_series(self, patch):
271 pre = re.compile("(\s*)([^#]+)")
271 pre = re.compile("(\s*)([^#]+)")
272 index = 0
272 index = 0
273 for l in self.full_series:
273 for l in self.full_series:
274 m = pre.match(l)
274 m = pre.match(l)
275 if m:
275 if m:
276 s = m.group(2)
276 s = m.group(2)
277 s = s.rstrip()
277 s = s.rstrip()
278 if s == patch:
278 if s == patch:
279 return index
279 return index
280 index += 1
280 index += 1
281 return None
281 return None
282
282
283 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
283 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
284
284
285 def parse_series(self):
285 def parse_series(self):
286 self.series = []
286 self.series = []
287 self.series_guards = []
287 self.series_guards = []
288 for l in self.full_series:
288 for l in self.full_series:
289 h = l.find('#')
289 h = l.find('#')
290 if h == -1:
290 if h == -1:
291 patch = l
291 patch = l
292 comment = ''
292 comment = ''
293 elif h == 0:
293 elif h == 0:
294 continue
294 continue
295 else:
295 else:
296 patch = l[:h]
296 patch = l[:h]
297 comment = l[h:]
297 comment = l[h:]
298 patch = patch.strip()
298 patch = patch.strip()
299 if patch:
299 if patch:
300 if patch in self.series:
300 if patch in self.series:
301 raise util.Abort(_('%s appears more than once in %s') %
301 raise util.Abort(_('%s appears more than once in %s') %
302 (patch, self.join(self.series_path)))
302 (patch, self.join(self.series_path)))
303 self.series.append(patch)
303 self.series.append(patch)
304 self.series_guards.append(self.guard_re.findall(comment))
304 self.series_guards.append(self.guard_re.findall(comment))
305
305
306 def check_guard(self, guard):
306 def check_guard(self, guard):
307 if not guard:
307 if not guard:
308 return _('guard cannot be an empty string')
308 return _('guard cannot be an empty string')
309 bad_chars = '# \t\r\n\f'
309 bad_chars = '# \t\r\n\f'
310 first = guard[0]
310 first = guard[0]
311 if first in '-+':
311 if first in '-+':
312 return (_('guard %r starts with invalid character: %r') %
312 return (_('guard %r starts with invalid character: %r') %
313 (guard, first))
313 (guard, first))
314 for c in bad_chars:
314 for c in bad_chars:
315 if c in guard:
315 if c in guard:
316 return _('invalid character in guard %r: %r') % (guard, c)
316 return _('invalid character in guard %r: %r') % (guard, c)
317
317
318 def set_active(self, guards):
318 def set_active(self, guards):
319 for guard in guards:
319 for guard in guards:
320 bad = self.check_guard(guard)
320 bad = self.check_guard(guard)
321 if bad:
321 if bad:
322 raise util.Abort(bad)
322 raise util.Abort(bad)
323 guards = sorted(set(guards))
323 guards = sorted(set(guards))
324 self.ui.debug('active guards: %s\n' % ' '.join(guards))
324 self.ui.debug('active guards: %s\n' % ' '.join(guards))
325 self.active_guards = guards
325 self.active_guards = guards
326 self.guards_dirty = True
326 self.guards_dirty = True
327
327
328 def active(self):
328 def active(self):
329 if self.active_guards is None:
329 if self.active_guards is None:
330 self.active_guards = []
330 self.active_guards = []
331 try:
331 try:
332 guards = self.opener(self.guards_path).read().split()
332 guards = self.opener(self.guards_path).read().split()
333 except IOError, err:
333 except IOError, err:
334 if err.errno != errno.ENOENT: raise
334 if err.errno != errno.ENOENT: raise
335 guards = []
335 guards = []
336 for i, guard in enumerate(guards):
336 for i, guard in enumerate(guards):
337 bad = self.check_guard(guard)
337 bad = self.check_guard(guard)
338 if bad:
338 if bad:
339 self.ui.warn('%s:%d: %s\n' %
339 self.ui.warn('%s:%d: %s\n' %
340 (self.join(self.guards_path), i + 1, bad))
340 (self.join(self.guards_path), i + 1, bad))
341 else:
341 else:
342 self.active_guards.append(guard)
342 self.active_guards.append(guard)
343 return self.active_guards
343 return self.active_guards
344
344
345 def set_guards(self, idx, guards):
345 def set_guards(self, idx, guards):
346 for g in guards:
346 for g in guards:
347 if len(g) < 2:
347 if len(g) < 2:
348 raise util.Abort(_('guard %r too short') % g)
348 raise util.Abort(_('guard %r too short') % g)
349 if g[0] not in '-+':
349 if g[0] not in '-+':
350 raise util.Abort(_('guard %r starts with invalid char') % g)
350 raise util.Abort(_('guard %r starts with invalid char') % g)
351 bad = self.check_guard(g[1:])
351 bad = self.check_guard(g[1:])
352 if bad:
352 if bad:
353 raise util.Abort(bad)
353 raise util.Abort(bad)
354 drop = self.guard_re.sub('', self.full_series[idx])
354 drop = self.guard_re.sub('', self.full_series[idx])
355 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
355 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
356 self.parse_series()
356 self.parse_series()
357 self.series_dirty = True
357 self.series_dirty = True
358
358
359 def pushable(self, idx):
359 def pushable(self, idx):
360 if isinstance(idx, str):
360 if isinstance(idx, str):
361 idx = self.series.index(idx)
361 idx = self.series.index(idx)
362 patchguards = self.series_guards[idx]
362 patchguards = self.series_guards[idx]
363 if not patchguards:
363 if not patchguards:
364 return True, None
364 return True, None
365 guards = self.active()
365 guards = self.active()
366 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
366 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
367 if exactneg:
367 if exactneg:
368 return False, exactneg[0]
368 return False, exactneg[0]
369 pos = [g for g in patchguards if g[0] == '+']
369 pos = [g for g in patchguards if g[0] == '+']
370 exactpos = [g for g in pos if g[1:] in guards]
370 exactpos = [g for g in pos if g[1:] in guards]
371 if pos:
371 if pos:
372 if exactpos:
372 if exactpos:
373 return True, exactpos[0]
373 return True, exactpos[0]
374 return False, pos
374 return False, pos
375 return True, ''
375 return True, ''
376
376
377 def explain_pushable(self, idx, all_patches=False):
377 def explain_pushable(self, idx, all_patches=False):
378 write = all_patches and self.ui.write or self.ui.warn
378 write = all_patches and self.ui.write or self.ui.warn
379 if all_patches or self.ui.verbose:
379 if all_patches or self.ui.verbose:
380 if isinstance(idx, str):
380 if isinstance(idx, str):
381 idx = self.series.index(idx)
381 idx = self.series.index(idx)
382 pushable, why = self.pushable(idx)
382 pushable, why = self.pushable(idx)
383 if all_patches and pushable:
383 if all_patches and pushable:
384 if why is None:
384 if why is None:
385 write(_('allowing %s - no guards in effect\n') %
385 write(_('allowing %s - no guards in effect\n') %
386 self.series[idx])
386 self.series[idx])
387 else:
387 else:
388 if not why:
388 if not why:
389 write(_('allowing %s - no matching negative guards\n') %
389 write(_('allowing %s - no matching negative guards\n') %
390 self.series[idx])
390 self.series[idx])
391 else:
391 else:
392 write(_('allowing %s - guarded by %r\n') %
392 write(_('allowing %s - guarded by %r\n') %
393 (self.series[idx], why))
393 (self.series[idx], why))
394 if not pushable:
394 if not pushable:
395 if why:
395 if why:
396 write(_('skipping %s - guarded by %r\n') %
396 write(_('skipping %s - guarded by %r\n') %
397 (self.series[idx], why))
397 (self.series[idx], why))
398 else:
398 else:
399 write(_('skipping %s - no matching guards\n') %
399 write(_('skipping %s - no matching guards\n') %
400 self.series[idx])
400 self.series[idx])
401
401
402 def save_dirty(self):
402 def save_dirty(self):
403 def write_list(items, path):
403 def write_list(items, path):
404 fp = self.opener(path, 'w')
404 fp = self.opener(path, 'w')
405 for i in items:
405 for i in items:
406 fp.write("%s\n" % i)
406 fp.write("%s\n" % i)
407 fp.close()
407 fp.close()
408 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
408 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
409 if self.series_dirty: write_list(self.full_series, self.series_path)
409 if self.series_dirty: write_list(self.full_series, self.series_path)
410 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
410 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
411
411
412 def removeundo(self, repo):
412 def removeundo(self, repo):
413 undo = repo.sjoin('undo')
413 undo = repo.sjoin('undo')
414 if not os.path.exists(undo):
414 if not os.path.exists(undo):
415 return
415 return
416 try:
416 try:
417 os.unlink(undo)
417 os.unlink(undo)
418 except OSError, inst:
418 except OSError, inst:
419 self.ui.warn(_('error removing undo: %s\n') % str(inst))
419 self.ui.warn(_('error removing undo: %s\n') % str(inst))
420
420
421 def printdiff(self, repo, node1, node2=None, files=None,
421 def printdiff(self, repo, node1, node2=None, files=None,
422 fp=None, changes=None, opts={}):
422 fp=None, changes=None, opts={}):
423 stat = opts.get('stat')
423 stat = opts.get('stat')
424 if stat:
424 if stat:
425 opts['unified'] = '0'
425 opts['unified'] = '0'
426
426
427 m = cmdutil.match(repo, files, opts)
427 m = cmdutil.match(repo, files, opts)
428 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
428 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
429 write = fp is None and repo.ui.write or fp.write
429 write = fp is None and repo.ui.write or fp.write
430 if stat:
430 if stat:
431 width = self.ui.interactive() and util.termwidth() or 80
431 width = self.ui.interactive() and util.termwidth() or 80
432 write(patch.diffstat(util.iterlines(chunks), width=width,
432 write(patch.diffstat(util.iterlines(chunks), width=width,
433 git=self.diffopts().git))
433 git=self.diffopts().git))
434 else:
434 else:
435 for chunk in chunks:
435 for chunk in chunks:
436 write(chunk)
436 write(chunk)
437
437
438 def mergeone(self, repo, mergeq, head, patch, rev):
438 def mergeone(self, repo, mergeq, head, patch, rev):
439 # first try just applying the patch
439 # first try just applying the patch
440 (err, n) = self.apply(repo, [ patch ], update_status=False,
440 (err, n) = self.apply(repo, [ patch ], update_status=False,
441 strict=True, merge=rev)
441 strict=True, merge=rev)
442
442
443 if err == 0:
443 if err == 0:
444 return (err, n)
444 return (err, n)
445
445
446 if n is None:
446 if n is None:
447 raise util.Abort(_("apply failed for patch %s") % patch)
447 raise util.Abort(_("apply failed for patch %s") % patch)
448
448
449 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
449 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
450
450
451 # apply failed, strip away that rev and merge.
451 # apply failed, strip away that rev and merge.
452 hg.clean(repo, head)
452 hg.clean(repo, head)
453 self.strip(repo, n, update=False, backup='strip')
453 self.strip(repo, n, update=False, backup='strip')
454
454
455 ctx = repo[rev]
455 ctx = repo[rev]
456 ret = hg.merge(repo, rev)
456 ret = hg.merge(repo, rev)
457 if ret:
457 if ret:
458 raise util.Abort(_("update returned %d") % ret)
458 raise util.Abort(_("update returned %d") % ret)
459 n = repo.commit(ctx.description(), ctx.user(), force=True)
459 n = repo.commit(ctx.description(), ctx.user(), force=True)
460 if n is None:
460 if n is None:
461 raise util.Abort(_("repo commit failed"))
461 raise util.Abort(_("repo commit failed"))
462 try:
462 try:
463 ph = patchheader(mergeq.join(patch))
463 ph = patchheader(mergeq.join(patch))
464 except:
464 except:
465 raise util.Abort(_("unable to read %s") % patch)
465 raise util.Abort(_("unable to read %s") % patch)
466
466
467 patchf = self.opener(patch, "w")
467 patchf = self.opener(patch, "w")
468 comments = str(ph)
468 comments = str(ph)
469 if comments:
469 if comments:
470 patchf.write(comments)
470 patchf.write(comments)
471 self.printdiff(repo, head, n, fp=patchf)
471 self.printdiff(repo, head, n, fp=patchf)
472 patchf.close()
472 patchf.close()
473 self.removeundo(repo)
473 self.removeundo(repo)
474 return (0, n)
474 return (0, n)
475
475
476 def qparents(self, repo, rev=None):
476 def qparents(self, repo, rev=None):
477 if rev is None:
477 if rev is None:
478 (p1, p2) = repo.dirstate.parents()
478 (p1, p2) = repo.dirstate.parents()
479 if p2 == nullid:
479 if p2 == nullid:
480 return p1
480 return p1
481 if len(self.applied) == 0:
481 if len(self.applied) == 0:
482 return None
482 return None
483 return bin(self.applied[-1].rev)
483 return bin(self.applied[-1].rev)
484 pp = repo.changelog.parents(rev)
484 pp = repo.changelog.parents(rev)
485 if pp[1] != nullid:
485 if pp[1] != nullid:
486 arevs = [ x.rev for x in self.applied ]
486 arevs = [ x.rev for x in self.applied ]
487 p0 = hex(pp[0])
487 p0 = hex(pp[0])
488 p1 = hex(pp[1])
488 p1 = hex(pp[1])
489 if p0 in arevs:
489 if p0 in arevs:
490 return pp[0]
490 return pp[0]
491 if p1 in arevs:
491 if p1 in arevs:
492 return pp[1]
492 return pp[1]
493 return pp[0]
493 return pp[0]
494
494
495 def mergepatch(self, repo, mergeq, series):
495 def mergepatch(self, repo, mergeq, series):
496 if len(self.applied) == 0:
496 if len(self.applied) == 0:
497 # each of the patches merged in will have two parents. This
497 # each of the patches merged in will have two parents. This
498 # can confuse the qrefresh, qdiff, and strip code because it
498 # can confuse the qrefresh, qdiff, and strip code because it
499 # needs to know which parent is actually in the patch queue.
499 # needs to know which parent is actually in the patch queue.
500 # so, we insert a merge marker with only one parent. This way
500 # so, we insert a merge marker with only one parent. This way
501 # the first patch in the queue is never a merge patch
501 # the first patch in the queue is never a merge patch
502 #
502 #
503 pname = ".hg.patches.merge.marker"
503 pname = ".hg.patches.merge.marker"
504 n = repo.commit('[mq]: merge marker', force=True)
504 n = repo.commit('[mq]: merge marker', force=True)
505 self.removeundo(repo)
505 self.removeundo(repo)
506 self.applied.append(statusentry(hex(n), pname))
506 self.applied.append(statusentry(hex(n), pname))
507 self.applied_dirty = 1
507 self.applied_dirty = 1
508
508
509 head = self.qparents(repo)
509 head = self.qparents(repo)
510
510
511 for patch in series:
511 for patch in series:
512 patch = mergeq.lookup(patch, strict=True)
512 patch = mergeq.lookup(patch, strict=True)
513 if not patch:
513 if not patch:
514 self.ui.warn(_("patch %s does not exist\n") % patch)
514 self.ui.warn(_("patch %s does not exist\n") % patch)
515 return (1, None)
515 return (1, None)
516 pushable, reason = self.pushable(patch)
516 pushable, reason = self.pushable(patch)
517 if not pushable:
517 if not pushable:
518 self.explain_pushable(patch, all_patches=True)
518 self.explain_pushable(patch, all_patches=True)
519 continue
519 continue
520 info = mergeq.isapplied(patch)
520 info = mergeq.isapplied(patch)
521 if not info:
521 if not info:
522 self.ui.warn(_("patch %s is not applied\n") % patch)
522 self.ui.warn(_("patch %s is not applied\n") % patch)
523 return (1, None)
523 return (1, None)
524 rev = bin(info[1])
524 rev = bin(info[1])
525 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
525 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
526 if head:
526 if head:
527 self.applied.append(statusentry(hex(head), patch))
527 self.applied.append(statusentry(hex(head), patch))
528 self.applied_dirty = 1
528 self.applied_dirty = 1
529 if err:
529 if err:
530 return (err, head)
530 return (err, head)
531 self.save_dirty()
531 self.save_dirty()
532 return (0, head)
532 return (0, head)
533
533
534 def patch(self, repo, patchfile):
534 def patch(self, repo, patchfile):
535 '''Apply patchfile to the working directory.
535 '''Apply patchfile to the working directory.
536 patchfile: name of patch file'''
536 patchfile: name of patch file'''
537 files = {}
537 files = {}
538 try:
538 try:
539 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
539 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
540 files=files, eolmode=None)
540 files=files, eolmode=None)
541 except Exception, inst:
541 except Exception, inst:
542 self.ui.note(str(inst) + '\n')
542 self.ui.note(str(inst) + '\n')
543 if not self.ui.verbose:
543 if not self.ui.verbose:
544 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
544 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
545 return (False, files, False)
545 return (False, files, False)
546
546
547 return (True, files, fuzz)
547 return (True, files, fuzz)
548
548
549 def apply(self, repo, series, list=False, update_status=True,
549 def apply(self, repo, series, list=False, update_status=True,
550 strict=False, patchdir=None, merge=None, all_files={}):
550 strict=False, patchdir=None, merge=None, all_files={}):
551 wlock = lock = tr = None
551 wlock = lock = tr = None
552 try:
552 try:
553 wlock = repo.wlock()
553 wlock = repo.wlock()
554 lock = repo.lock()
554 lock = repo.lock()
555 tr = repo.transaction()
555 tr = repo.transaction()
556 try:
556 try:
557 ret = self._apply(repo, series, list, update_status,
557 ret = self._apply(repo, series, list, update_status,
558 strict, patchdir, merge, all_files=all_files)
558 strict, patchdir, merge, all_files=all_files)
559 tr.close()
559 tr.close()
560 self.save_dirty()
560 self.save_dirty()
561 return ret
561 return ret
562 except:
562 except:
563 try:
563 try:
564 tr.abort()
564 tr.abort()
565 finally:
565 finally:
566 repo.invalidate()
566 repo.invalidate()
567 repo.dirstate.invalidate()
567 repo.dirstate.invalidate()
568 raise
568 raise
569 finally:
569 finally:
570 del tr
570 del tr
571 release(lock, wlock)
571 release(lock, wlock)
572 self.removeundo(repo)
572 self.removeundo(repo)
573
573
574 def _apply(self, repo, series, list=False, update_status=True,
574 def _apply(self, repo, series, list=False, update_status=True,
575 strict=False, patchdir=None, merge=None, all_files={}):
575 strict=False, patchdir=None, merge=None, all_files={}):
576 '''returns (error, hash)
576 '''returns (error, hash)
577 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
577 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
578 # TODO unify with commands.py
578 # TODO unify with commands.py
579 if not patchdir:
579 if not patchdir:
580 patchdir = self.path
580 patchdir = self.path
581 err = 0
581 err = 0
582 n = None
582 n = None
583 for patchname in series:
583 for patchname in series:
584 pushable, reason = self.pushable(patchname)
584 pushable, reason = self.pushable(patchname)
585 if not pushable:
585 if not pushable:
586 self.explain_pushable(patchname, all_patches=True)
586 self.explain_pushable(patchname, all_patches=True)
587 continue
587 continue
588 self.ui.status(_("applying %s\n") % patchname)
588 self.ui.status(_("applying %s\n") % patchname)
589 pf = os.path.join(patchdir, patchname)
589 pf = os.path.join(patchdir, patchname)
590
590
591 try:
591 try:
592 ph = patchheader(self.join(patchname))
592 ph = patchheader(self.join(patchname))
593 except:
593 except:
594 self.ui.warn(_("unable to read %s\n") % patchname)
594 self.ui.warn(_("unable to read %s\n") % patchname)
595 err = 1
595 err = 1
596 break
596 break
597
597
598 message = ph.message
598 message = ph.message
599 if not message:
599 if not message:
600 message = _("imported patch %s\n") % patchname
600 message = _("imported patch %s\n") % patchname
601 else:
601 else:
602 if list:
602 if list:
603 message.append(_("\nimported patch %s") % patchname)
603 message.append(_("\nimported patch %s") % patchname)
604 message = '\n'.join(message)
604 message = '\n'.join(message)
605
605
606 if ph.haspatch:
606 if ph.haspatch:
607 (patcherr, files, fuzz) = self.patch(repo, pf)
607 (patcherr, files, fuzz) = self.patch(repo, pf)
608 all_files.update(files)
608 all_files.update(files)
609 patcherr = not patcherr
609 patcherr = not patcherr
610 else:
610 else:
611 self.ui.warn(_("patch %s is empty\n") % patchname)
611 self.ui.warn(_("patch %s is empty\n") % patchname)
612 patcherr, files, fuzz = 0, [], 0
612 patcherr, files, fuzz = 0, [], 0
613
613
614 if merge and files:
614 if merge and files:
615 # Mark as removed/merged and update dirstate parent info
615 # Mark as removed/merged and update dirstate parent info
616 removed = []
616 removed = []
617 merged = []
617 merged = []
618 for f in files:
618 for f in files:
619 if os.path.exists(repo.wjoin(f)):
619 if os.path.exists(repo.wjoin(f)):
620 merged.append(f)
620 merged.append(f)
621 else:
621 else:
622 removed.append(f)
622 removed.append(f)
623 for f in removed:
623 for f in removed:
624 repo.dirstate.remove(f)
624 repo.dirstate.remove(f)
625 for f in merged:
625 for f in merged:
626 repo.dirstate.merge(f)
626 repo.dirstate.merge(f)
627 p1, p2 = repo.dirstate.parents()
627 p1, p2 = repo.dirstate.parents()
628 repo.dirstate.setparents(p1, merge)
628 repo.dirstate.setparents(p1, merge)
629
629
630 files = patch.updatedir(self.ui, repo, files)
630 files = patch.updatedir(self.ui, repo, files)
631 match = cmdutil.matchfiles(repo, files or [])
631 match = cmdutil.matchfiles(repo, files or [])
632 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
632 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
633
633
634 if n is None:
634 if n is None:
635 raise util.Abort(_("repo commit failed"))
635 raise util.Abort(_("repo commit failed"))
636
636
637 if update_status:
637 if update_status:
638 self.applied.append(statusentry(hex(n), patchname))
638 self.applied.append(statusentry(hex(n), patchname))
639
639
640 if patcherr:
640 if patcherr:
641 self.ui.warn(_("patch failed, rejects left in working dir\n"))
641 self.ui.warn(_("patch failed, rejects left in working dir\n"))
642 err = 2
642 err = 2
643 break
643 break
644
644
645 if fuzz and strict:
645 if fuzz and strict:
646 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
646 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
647 err = 3
647 err = 3
648 break
648 break
649 return (err, n)
649 return (err, n)
650
650
651 def _cleanup(self, patches, numrevs, keep=False):
651 def _cleanup(self, patches, numrevs, keep=False):
652 if not keep:
652 if not keep:
653 r = self.qrepo()
653 r = self.qrepo()
654 if r:
654 if r:
655 r.remove(patches, True)
655 r.remove(patches, True)
656 else:
656 else:
657 for p in patches:
657 for p in patches:
658 os.unlink(self.join(p))
658 os.unlink(self.join(p))
659
659
660 if numrevs:
660 if numrevs:
661 del self.applied[:numrevs]
661 del self.applied[:numrevs]
662 self.applied_dirty = 1
662 self.applied_dirty = 1
663
663
664 for i in sorted([self.find_series(p) for p in patches], reverse=True):
664 for i in sorted([self.find_series(p) for p in patches], reverse=True):
665 del self.full_series[i]
665 del self.full_series[i]
666 self.parse_series()
666 self.parse_series()
667 self.series_dirty = 1
667 self.series_dirty = 1
668
668
669 def _revpatches(self, repo, revs):
669 def _revpatches(self, repo, revs):
670 firstrev = repo[self.applied[0].rev].rev()
670 firstrev = repo[self.applied[0].rev].rev()
671 patches = []
671 patches = []
672 for i, rev in enumerate(revs):
672 for i, rev in enumerate(revs):
673
673
674 if rev < firstrev:
674 if rev < firstrev:
675 raise util.Abort(_('revision %d is not managed') % rev)
675 raise util.Abort(_('revision %d is not managed') % rev)
676
676
677 ctx = repo[rev]
677 ctx = repo[rev]
678 base = bin(self.applied[i].rev)
678 base = bin(self.applied[i].rev)
679 if ctx.node() != base:
679 if ctx.node() != base:
680 msg = _('cannot delete revision %d above applied patches')
680 msg = _('cannot delete revision %d above applied patches')
681 raise util.Abort(msg % rev)
681 raise util.Abort(msg % rev)
682
682
683 patch = self.applied[i].name
683 patch = self.applied[i].name
684 for fmt in ('[mq]: %s', 'imported patch %s'):
684 for fmt in ('[mq]: %s', 'imported patch %s'):
685 if ctx.description() == fmt % patch:
685 if ctx.description() == fmt % patch:
686 msg = _('patch %s finalized without changeset message\n')
686 msg = _('patch %s finalized without changeset message\n')
687 repo.ui.status(msg % patch)
687 repo.ui.status(msg % patch)
688 break
688 break
689
689
690 patches.append(patch)
690 patches.append(patch)
691 return patches
691 return patches
692
692
693 def finish(self, repo, revs):
693 def finish(self, repo, revs):
694 patches = self._revpatches(repo, sorted(revs))
694 patches = self._revpatches(repo, sorted(revs))
695 self._cleanup(patches, len(patches))
695 self._cleanup(patches, len(patches))
696
696
697 def delete(self, repo, patches, opts):
697 def delete(self, repo, patches, opts):
698 if not patches and not opts.get('rev'):
698 if not patches and not opts.get('rev'):
699 raise util.Abort(_('qdelete requires at least one revision or '
699 raise util.Abort(_('qdelete requires at least one revision or '
700 'patch name'))
700 'patch name'))
701
701
702 realpatches = []
702 realpatches = []
703 for patch in patches:
703 for patch in patches:
704 patch = self.lookup(patch, strict=True)
704 patch = self.lookup(patch, strict=True)
705 info = self.isapplied(patch)
705 info = self.isapplied(patch)
706 if info:
706 if info:
707 raise util.Abort(_("cannot delete applied patch %s") % patch)
707 raise util.Abort(_("cannot delete applied patch %s") % patch)
708 if patch not in self.series:
708 if patch not in self.series:
709 raise util.Abort(_("patch %s not in series file") % patch)
709 raise util.Abort(_("patch %s not in series file") % patch)
710 realpatches.append(patch)
710 realpatches.append(patch)
711
711
712 numrevs = 0
712 numrevs = 0
713 if opts.get('rev'):
713 if opts.get('rev'):
714 if not self.applied:
714 if not self.applied:
715 raise util.Abort(_('no patches applied'))
715 raise util.Abort(_('no patches applied'))
716 revs = cmdutil.revrange(repo, opts['rev'])
716 revs = cmdutil.revrange(repo, opts['rev'])
717 if len(revs) > 1 and revs[0] > revs[1]:
717 if len(revs) > 1 and revs[0] > revs[1]:
718 revs.reverse()
718 revs.reverse()
719 revpatches = self._revpatches(repo, revs)
719 revpatches = self._revpatches(repo, revs)
720 realpatches += revpatches
720 realpatches += revpatches
721 numrevs = len(revpatches)
721 numrevs = len(revpatches)
722
722
723 self._cleanup(realpatches, numrevs, opts.get('keep'))
723 self._cleanup(realpatches, numrevs, opts.get('keep'))
724
724
725 def check_toppatch(self, repo):
725 def check_toppatch(self, repo):
726 if len(self.applied) > 0:
726 if len(self.applied) > 0:
727 top = bin(self.applied[-1].rev)
727 top = bin(self.applied[-1].rev)
728 pp = repo.dirstate.parents()
728 pp = repo.dirstate.parents()
729 if top not in pp:
729 if top not in pp:
730 raise util.Abort(_("working directory revision is not qtip"))
730 raise util.Abort(_("working directory revision is not qtip"))
731 return top
731 return top
732 return None
732 return None
733 def check_localchanges(self, repo, force=False, refresh=True):
733 def check_localchanges(self, repo, force=False, refresh=True):
734 m, a, r, d = repo.status()[:4]
734 m, a, r, d = repo.status()[:4]
735 if m or a or r or d:
735 if m or a or r or d:
736 if not force:
736 if not force:
737 if refresh:
737 if refresh:
738 raise util.Abort(_("local changes found, refresh first"))
738 raise util.Abort(_("local changes found, refresh first"))
739 else:
739 else:
740 raise util.Abort(_("local changes found"))
740 raise util.Abort(_("local changes found"))
741 return m, a, r, d
741 return m, a, r, d
742
742
743 _reserved = ('series', 'status', 'guards')
743 _reserved = ('series', 'status', 'guards')
744 def check_reserved_name(self, name):
744 def check_reserved_name(self, name):
745 if (name in self._reserved or name.startswith('.hg')
745 if (name in self._reserved or name.startswith('.hg')
746 or name.startswith('.mq')):
746 or name.startswith('.mq')):
747 raise util.Abort(_('"%s" cannot be used as the name of a patch')
747 raise util.Abort(_('"%s" cannot be used as the name of a patch')
748 % name)
748 % name)
749
749
750 def new(self, repo, patchfn, *pats, **opts):
750 def new(self, repo, patchfn, *pats, **opts):
751 """options:
751 """options:
752 msg: a string or a no-argument function returning a string
752 msg: a string or a no-argument function returning a string
753 """
753 """
754 msg = opts.get('msg')
754 msg = opts.get('msg')
755 force = opts.get('force')
755 force = opts.get('force')
756 user = opts.get('user')
756 user = opts.get('user')
757 date = opts.get('date')
757 date = opts.get('date')
758 if date:
758 if date:
759 date = util.parsedate(date)
759 date = util.parsedate(date)
760 self.check_reserved_name(patchfn)
760 self.check_reserved_name(patchfn)
761 if os.path.exists(self.join(patchfn)):
761 if os.path.exists(self.join(patchfn)):
762 raise util.Abort(_('patch "%s" already exists') % patchfn)
762 raise util.Abort(_('patch "%s" already exists') % patchfn)
763 if opts.get('include') or opts.get('exclude') or pats:
763 if opts.get('include') or opts.get('exclude') or pats:
764 match = cmdutil.match(repo, pats, opts)
764 match = cmdutil.match(repo, pats, opts)
765 # detect missing files in pats
765 # detect missing files in pats
766 def badfn(f, msg):
766 def badfn(f, msg):
767 raise util.Abort('%s: %s' % (f, msg))
767 raise util.Abort('%s: %s' % (f, msg))
768 match.bad = badfn
768 match.bad = badfn
769 m, a, r, d = repo.status(match=match)[:4]
769 m, a, r, d = repo.status(match=match)[:4]
770 else:
770 else:
771 m, a, r, d = self.check_localchanges(repo, force)
771 m, a, r, d = self.check_localchanges(repo, force)
772 match = cmdutil.matchfiles(repo, m + a + r)
772 match = cmdutil.matchfiles(repo, m + a + r)
773 commitfiles = m + a + r
773 commitfiles = m + a + r
774 self.check_toppatch(repo)
774 self.check_toppatch(repo)
775 insert = self.full_series_end()
775 insert = self.full_series_end()
776 wlock = repo.wlock()
776 wlock = repo.wlock()
777 try:
777 try:
778 # if patch file write fails, abort early
778 # if patch file write fails, abort early
779 p = self.opener(patchfn, "w")
779 p = self.opener(patchfn, "w")
780 try:
780 try:
781 if date:
781 if date:
782 p.write("# HG changeset patch\n")
782 p.write("# HG changeset patch\n")
783 if user:
783 if user:
784 p.write("# User " + user + "\n")
784 p.write("# User " + user + "\n")
785 p.write("# Date %d %d\n\n" % date)
785 p.write("# Date %d %d\n\n" % date)
786 elif user:
786 elif user:
787 p.write("From: " + user + "\n\n")
787 p.write("From: " + user + "\n\n")
788
788
789 if hasattr(msg, '__call__'):
789 if hasattr(msg, '__call__'):
790 msg = msg()
790 msg = msg()
791 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
791 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
792 n = repo.commit(commitmsg, user, date, match=match, force=True)
792 n = repo.commit(commitmsg, user, date, match=match, force=True)
793 if n is None:
793 if n is None:
794 raise util.Abort(_("repo commit failed"))
794 raise util.Abort(_("repo commit failed"))
795 try:
795 try:
796 self.full_series[insert:insert] = [patchfn]
796 self.full_series[insert:insert] = [patchfn]
797 self.applied.append(statusentry(hex(n), patchfn))
797 self.applied.append(statusentry(hex(n), patchfn))
798 self.parse_series()
798 self.parse_series()
799 self.series_dirty = 1
799 self.series_dirty = 1
800 self.applied_dirty = 1
800 self.applied_dirty = 1
801 if msg:
801 if msg:
802 msg = msg + "\n\n"
802 msg = msg + "\n\n"
803 p.write(msg)
803 p.write(msg)
804 if commitfiles:
804 if commitfiles:
805 diffopts = self.diffopts()
805 diffopts = self.diffopts()
806 if opts.get('git'): diffopts.git = True
806 if opts.get('git'): diffopts.git = True
807 parent = self.qparents(repo, n)
807 parent = self.qparents(repo, n)
808 chunks = patch.diff(repo, node1=parent, node2=n,
808 chunks = patch.diff(repo, node1=parent, node2=n,
809 match=match, opts=diffopts)
809 match=match, opts=diffopts)
810 for chunk in chunks:
810 for chunk in chunks:
811 p.write(chunk)
811 p.write(chunk)
812 p.close()
812 p.close()
813 wlock.release()
813 wlock.release()
814 wlock = None
814 wlock = None
815 r = self.qrepo()
815 r = self.qrepo()
816 if r: r.add([patchfn])
816 if r: r.add([patchfn])
817 except:
817 except:
818 repo.rollback()
818 repo.rollback()
819 raise
819 raise
820 except Exception:
820 except Exception:
821 patchpath = self.join(patchfn)
821 patchpath = self.join(patchfn)
822 try:
822 try:
823 os.unlink(patchpath)
823 os.unlink(patchpath)
824 except:
824 except:
825 self.ui.warn(_('error unlinking %s\n') % patchpath)
825 self.ui.warn(_('error unlinking %s\n') % patchpath)
826 raise
826 raise
827 self.removeundo(repo)
827 self.removeundo(repo)
828 finally:
828 finally:
829 release(wlock)
829 release(wlock)
830
830
831 def strip(self, repo, rev, update=True, backup="all", force=None):
831 def strip(self, repo, rev, update=True, backup="all", force=None):
832 wlock = lock = None
832 wlock = lock = None
833 try:
833 try:
834 wlock = repo.wlock()
834 wlock = repo.wlock()
835 lock = repo.lock()
835 lock = repo.lock()
836
836
837 if update:
837 if update:
838 self.check_localchanges(repo, force=force, refresh=False)
838 self.check_localchanges(repo, force=force, refresh=False)
839 urev = self.qparents(repo, rev)
839 urev = self.qparents(repo, rev)
840 hg.clean(repo, urev)
840 hg.clean(repo, urev)
841 repo.dirstate.write()
841 repo.dirstate.write()
842
842
843 self.removeundo(repo)
843 self.removeundo(repo)
844 repair.strip(self.ui, repo, rev, backup)
844 repair.strip(self.ui, repo, rev, backup)
845 # strip may have unbundled a set of backed up revisions after
845 # strip may have unbundled a set of backed up revisions after
846 # the actual strip
846 # the actual strip
847 self.removeundo(repo)
847 self.removeundo(repo)
848 finally:
848 finally:
849 release(lock, wlock)
849 release(lock, wlock)
850
850
851 def isapplied(self, patch):
851 def isapplied(self, patch):
852 """returns (index, rev, patch)"""
852 """returns (index, rev, patch)"""
853 for i, a in enumerate(self.applied):
853 for i, a in enumerate(self.applied):
854 if a.name == patch:
854 if a.name == patch:
855 return (i, a.rev, a.name)
855 return (i, a.rev, a.name)
856 return None
856 return None
857
857
858 # if the exact patch name does not exist, we try a few
858 # if the exact patch name does not exist, we try a few
859 # variations. If strict is passed, we try only #1
859 # variations. If strict is passed, we try only #1
860 #
860 #
861 # 1) a number to indicate an offset in the series file
861 # 1) a number to indicate an offset in the series file
862 # 2) a unique substring of the patch name was given
862 # 2) a unique substring of the patch name was given
863 # 3) patchname[-+]num to indicate an offset in the series file
863 # 3) patchname[-+]num to indicate an offset in the series file
864 def lookup(self, patch, strict=False):
864 def lookup(self, patch, strict=False):
865 patch = patch and str(patch)
865 patch = patch and str(patch)
866
866
867 def partial_name(s):
867 def partial_name(s):
868 if s in self.series:
868 if s in self.series:
869 return s
869 return s
870 matches = [x for x in self.series if s in x]
870 matches = [x for x in self.series if s in x]
871 if len(matches) > 1:
871 if len(matches) > 1:
872 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
872 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
873 for m in matches:
873 for m in matches:
874 self.ui.warn(' %s\n' % m)
874 self.ui.warn(' %s\n' % m)
875 return None
875 return None
876 if matches:
876 if matches:
877 return matches[0]
877 return matches[0]
878 if len(self.series) > 0 and len(self.applied) > 0:
878 if len(self.series) > 0 and len(self.applied) > 0:
879 if s == 'qtip':
879 if s == 'qtip':
880 return self.series[self.series_end(True)-1]
880 return self.series[self.series_end(True)-1]
881 if s == 'qbase':
881 if s == 'qbase':
882 return self.series[0]
882 return self.series[0]
883 return None
883 return None
884
884
885 if patch is None:
885 if patch is None:
886 return None
886 return None
887 if patch in self.series:
887 if patch in self.series:
888 return patch
888 return patch
889
889
890 if not os.path.isfile(self.join(patch)):
890 if not os.path.isfile(self.join(patch)):
891 try:
891 try:
892 sno = int(patch)
892 sno = int(patch)
893 except(ValueError, OverflowError):
893 except(ValueError, OverflowError):
894 pass
894 pass
895 else:
895 else:
896 if -len(self.series) <= sno < len(self.series):
896 if -len(self.series) <= sno < len(self.series):
897 return self.series[sno]
897 return self.series[sno]
898
898
899 if not strict:
899 if not strict:
900 res = partial_name(patch)
900 res = partial_name(patch)
901 if res:
901 if res:
902 return res
902 return res
903 minus = patch.rfind('-')
903 minus = patch.rfind('-')
904 if minus >= 0:
904 if minus >= 0:
905 res = partial_name(patch[:minus])
905 res = partial_name(patch[:minus])
906 if res:
906 if res:
907 i = self.series.index(res)
907 i = self.series.index(res)
908 try:
908 try:
909 off = int(patch[minus+1:] or 1)
909 off = int(patch[minus+1:] or 1)
910 except(ValueError, OverflowError):
910 except(ValueError, OverflowError):
911 pass
911 pass
912 else:
912 else:
913 if i - off >= 0:
913 if i - off >= 0:
914 return self.series[i - off]
914 return self.series[i - off]
915 plus = patch.rfind('+')
915 plus = patch.rfind('+')
916 if plus >= 0:
916 if plus >= 0:
917 res = partial_name(patch[:plus])
917 res = partial_name(patch[:plus])
918 if res:
918 if res:
919 i = self.series.index(res)
919 i = self.series.index(res)
920 try:
920 try:
921 off = int(patch[plus+1:] or 1)
921 off = int(patch[plus+1:] or 1)
922 except(ValueError, OverflowError):
922 except(ValueError, OverflowError):
923 pass
923 pass
924 else:
924 else:
925 if i + off < len(self.series):
925 if i + off < len(self.series):
926 return self.series[i + off]
926 return self.series[i + off]
927 raise util.Abort(_("patch %s not in series") % patch)
927 raise util.Abort(_("patch %s not in series") % patch)
928
928
929 def push(self, repo, patch=None, force=False, list=False,
929 def push(self, repo, patch=None, force=False, list=False,
930 mergeq=None, all=False):
930 mergeq=None, all=False):
931 wlock = repo.wlock()
931 wlock = repo.wlock()
932 try:
932 try:
933 if repo.dirstate.parents()[0] not in repo.heads():
933 if repo.dirstate.parents()[0] not in repo.heads():
934 self.ui.status(_("(working directory not at a head)\n"))
934 self.ui.status(_("(working directory not at a head)\n"))
935
935
936 if not self.series:
936 if not self.series:
937 self.ui.warn(_('no patches in series\n'))
937 self.ui.warn(_('no patches in series\n'))
938 return 0
938 return 0
939
939
940 patch = self.lookup(patch)
940 patch = self.lookup(patch)
941 # Suppose our series file is: A B C and the current 'top'
941 # Suppose our series file is: A B C and the current 'top'
942 # patch is B. qpush C should be performed (moving forward)
942 # patch is B. qpush C should be performed (moving forward)
943 # qpush B is a NOP (no change) qpush A is an error (can't
943 # qpush B is a NOP (no change) qpush A is an error (can't
944 # go backwards with qpush)
944 # go backwards with qpush)
945 if patch:
945 if patch:
946 info = self.isapplied(patch)
946 info = self.isapplied(patch)
947 if info:
947 if info:
948 if info[0] < len(self.applied) - 1:
948 if info[0] < len(self.applied) - 1:
949 raise util.Abort(
949 raise util.Abort(
950 _("cannot push to a previous patch: %s") % patch)
950 _("cannot push to a previous patch: %s") % patch)
951 self.ui.warn(
951 self.ui.warn(
952 _('qpush: %s is already at the top\n') % patch)
952 _('qpush: %s is already at the top\n') % patch)
953 return
953 return
954 pushable, reason = self.pushable(patch)
954 pushable, reason = self.pushable(patch)
955 if not pushable:
955 if not pushable:
956 if reason:
956 if reason:
957 reason = _('guarded by %r') % reason
957 reason = _('guarded by %r') % reason
958 else:
958 else:
959 reason = _('no matching guards')
959 reason = _('no matching guards')
960 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
960 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
961 return 1
961 return 1
962 elif all:
962 elif all:
963 patch = self.series[-1]
963 patch = self.series[-1]
964 if self.isapplied(patch):
964 if self.isapplied(patch):
965 self.ui.warn(_('all patches are currently applied\n'))
965 self.ui.warn(_('all patches are currently applied\n'))
966 return 0
966 return 0
967
967
968 # Following the above example, starting at 'top' of B:
968 # Following the above example, starting at 'top' of B:
969 # qpush should be performed (pushes C), but a subsequent
969 # qpush should be performed (pushes C), but a subsequent
970 # qpush without an argument is an error (nothing to
970 # qpush without an argument is an error (nothing to
971 # apply). This allows a loop of "...while hg qpush..." to
971 # apply). This allows a loop of "...while hg qpush..." to
972 # work as it detects an error when done
972 # work as it detects an error when done
973 start = self.series_end()
973 start = self.series_end()
974 if start == len(self.series):
974 if start == len(self.series):
975 self.ui.warn(_('patch series already fully applied\n'))
975 self.ui.warn(_('patch series already fully applied\n'))
976 return 1
976 return 1
977 if not force:
977 if not force:
978 self.check_localchanges(repo)
978 self.check_localchanges(repo)
979
979
980 self.applied_dirty = 1
980 self.applied_dirty = 1
981 if start > 0:
981 if start > 0:
982 self.check_toppatch(repo)
982 self.check_toppatch(repo)
983 if not patch:
983 if not patch:
984 patch = self.series[start]
984 patch = self.series[start]
985 end = start + 1
985 end = start + 1
986 else:
986 else:
987 end = self.series.index(patch, start) + 1
987 end = self.series.index(patch, start) + 1
988
988
989 s = self.series[start:end]
989 s = self.series[start:end]
990 all_files = {}
990 all_files = {}
991 try:
991 try:
992 if mergeq:
992 if mergeq:
993 ret = self.mergepatch(repo, mergeq, s)
993 ret = self.mergepatch(repo, mergeq, s)
994 else:
994 else:
995 ret = self.apply(repo, s, list, all_files=all_files)
995 ret = self.apply(repo, s, list, all_files=all_files)
996 except:
996 except:
997 self.ui.warn(_('cleaning up working directory...'))
997 self.ui.warn(_('cleaning up working directory...'))
998 node = repo.dirstate.parents()[0]
998 node = repo.dirstate.parents()[0]
999 hg.revert(repo, node, None)
999 hg.revert(repo, node, None)
1000 unknown = repo.status(unknown=True)[4]
1000 unknown = repo.status(unknown=True)[4]
1001 # only remove unknown files that we know we touched or
1001 # only remove unknown files that we know we touched or
1002 # created while patching
1002 # created while patching
1003 for f in unknown:
1003 for f in unknown:
1004 if f in all_files:
1004 if f in all_files:
1005 util.unlink(repo.wjoin(f))
1005 util.unlink(repo.wjoin(f))
1006 self.ui.warn(_('done\n'))
1006 self.ui.warn(_('done\n'))
1007 raise
1007 raise
1008
1008
1009 if not self.applied:
1009 if not self.applied:
1010 return ret[0]
1010 return ret[0]
1011 top = self.applied[-1].name
1011 top = self.applied[-1].name
1012 if ret[0] and ret[0] > 1:
1012 if ret[0] and ret[0] > 1:
1013 msg = _("errors during apply, please fix and refresh %s\n")
1013 msg = _("errors during apply, please fix and refresh %s\n")
1014 self.ui.write(msg % top)
1014 self.ui.write(msg % top)
1015 else:
1015 else:
1016 self.ui.write(_("now at: %s\n") % top)
1016 self.ui.write(_("now at: %s\n") % top)
1017 return ret[0]
1017 return ret[0]
1018
1018
1019 finally:
1019 finally:
1020 wlock.release()
1020 wlock.release()
1021
1021
1022 def pop(self, repo, patch=None, force=False, update=True, all=False):
1022 def pop(self, repo, patch=None, force=False, update=True, all=False):
1023 def getfile(f, rev, flags):
1023 def getfile(f, rev, flags):
1024 t = repo.file(f).read(rev)
1024 t = repo.file(f).read(rev)
1025 repo.wwrite(f, t, flags)
1025 repo.wwrite(f, t, flags)
1026
1026
1027 wlock = repo.wlock()
1027 wlock = repo.wlock()
1028 try:
1028 try:
1029 if patch:
1029 if patch:
1030 # index, rev, patch
1030 # index, rev, patch
1031 info = self.isapplied(patch)
1031 info = self.isapplied(patch)
1032 if not info:
1032 if not info:
1033 patch = self.lookup(patch)
1033 patch = self.lookup(patch)
1034 info = self.isapplied(patch)
1034 info = self.isapplied(patch)
1035 if not info:
1035 if not info:
1036 raise util.Abort(_("patch %s is not applied") % patch)
1036 raise util.Abort(_("patch %s is not applied") % patch)
1037
1037
1038 if len(self.applied) == 0:
1038 if len(self.applied) == 0:
1039 # Allow qpop -a to work repeatedly,
1039 # Allow qpop -a to work repeatedly,
1040 # but not qpop without an argument
1040 # but not qpop without an argument
1041 self.ui.warn(_("no patches applied\n"))
1041 self.ui.warn(_("no patches applied\n"))
1042 return not all
1042 return not all
1043
1043
1044 if all:
1044 if all:
1045 start = 0
1045 start = 0
1046 elif patch:
1046 elif patch:
1047 start = info[0] + 1
1047 start = info[0] + 1
1048 else:
1048 else:
1049 start = len(self.applied) - 1
1049 start = len(self.applied) - 1
1050
1050
1051 if start >= len(self.applied):
1051 if start >= len(self.applied):
1052 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1052 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1053 return
1053 return
1054
1054
1055 if not update:
1055 if not update:
1056 parents = repo.dirstate.parents()
1056 parents = repo.dirstate.parents()
1057 rr = [ bin(x.rev) for x in self.applied ]
1057 rr = [ bin(x.rev) for x in self.applied ]
1058 for p in parents:
1058 for p in parents:
1059 if p in rr:
1059 if p in rr:
1060 self.ui.warn(_("qpop: forcing dirstate update\n"))
1060 self.ui.warn(_("qpop: forcing dirstate update\n"))
1061 update = True
1061 update = True
1062 else:
1062 else:
1063 parents = [p.hex() for p in repo[None].parents()]
1063 parents = [p.hex() for p in repo[None].parents()]
1064 needupdate = False
1064 needupdate = False
1065 for entry in self.applied[start:]:
1065 for entry in self.applied[start:]:
1066 if entry.rev in parents:
1066 if entry.rev in parents:
1067 needupdate = True
1067 needupdate = True
1068 break
1068 break
1069 update = needupdate
1069 update = needupdate
1070
1070
1071 if not force and update:
1071 if not force and update:
1072 self.check_localchanges(repo)
1072 self.check_localchanges(repo)
1073
1073
1074 self.applied_dirty = 1
1074 self.applied_dirty = 1
1075 end = len(self.applied)
1075 end = len(self.applied)
1076 rev = bin(self.applied[start].rev)
1076 rev = bin(self.applied[start].rev)
1077 if update:
1077 if update:
1078 top = self.check_toppatch(repo)
1078 top = self.check_toppatch(repo)
1079
1079
1080 try:
1080 try:
1081 heads = repo.changelog.heads(rev)
1081 heads = repo.changelog.heads(rev)
1082 except error.LookupError:
1082 except error.LookupError:
1083 node = short(rev)
1083 node = short(rev)
1084 raise util.Abort(_('trying to pop unknown node %s') % node)
1084 raise util.Abort(_('trying to pop unknown node %s') % node)
1085
1085
1086 if heads != [bin(self.applied[-1].rev)]:
1086 if heads != [bin(self.applied[-1].rev)]:
1087 raise util.Abort(_("popping would remove a revision not "
1087 raise util.Abort(_("popping would remove a revision not "
1088 "managed by this patch queue"))
1088 "managed by this patch queue"))
1089
1089
1090 # we know there are no local changes, so we can make a simplified
1090 # we know there are no local changes, so we can make a simplified
1091 # form of hg.update.
1091 # form of hg.update.
1092 if update:
1092 if update:
1093 qp = self.qparents(repo, rev)
1093 qp = self.qparents(repo, rev)
1094 changes = repo.changelog.read(qp)
1094 changes = repo.changelog.read(qp)
1095 mmap = repo.manifest.read(changes[0])
1095 mmap = repo.manifest.read(changes[0])
1096 m, a, r, d = repo.status(qp, top)[:4]
1096 m, a, r, d = repo.status(qp, top)[:4]
1097 if d:
1097 if d:
1098 raise util.Abort(_("deletions found between repo revs"))
1098 raise util.Abort(_("deletions found between repo revs"))
1099 for f in m:
1099 for f in m:
1100 getfile(f, mmap[f], mmap.flags(f))
1100 getfile(f, mmap[f], mmap.flags(f))
1101 for f in r:
1101 for f in r:
1102 getfile(f, mmap[f], mmap.flags(f))
1102 getfile(f, mmap[f], mmap.flags(f))
1103 for f in m + r:
1103 for f in m + r:
1104 repo.dirstate.normal(f)
1104 repo.dirstate.normal(f)
1105 for f in a:
1105 for f in a:
1106 try:
1106 try:
1107 os.unlink(repo.wjoin(f))
1107 os.unlink(repo.wjoin(f))
1108 except OSError, e:
1108 except OSError, e:
1109 if e.errno != errno.ENOENT:
1109 if e.errno != errno.ENOENT:
1110 raise
1110 raise
1111 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1111 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1112 except: pass
1112 except: pass
1113 repo.dirstate.forget(f)
1113 repo.dirstate.forget(f)
1114 repo.dirstate.setparents(qp, nullid)
1114 repo.dirstate.setparents(qp, nullid)
1115 for patch in reversed(self.applied[start:end]):
1115 for patch in reversed(self.applied[start:end]):
1116 self.ui.status(_("popping %s\n") % patch.name)
1116 self.ui.status(_("popping %s\n") % patch.name)
1117 del self.applied[start:end]
1117 del self.applied[start:end]
1118 self.strip(repo, rev, update=False, backup='strip')
1118 self.strip(repo, rev, update=False, backup='strip')
1119 if len(self.applied):
1119 if len(self.applied):
1120 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1120 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1121 else:
1121 else:
1122 self.ui.write(_("patch queue now empty\n"))
1122 self.ui.write(_("patch queue now empty\n"))
1123 finally:
1123 finally:
1124 wlock.release()
1124 wlock.release()
1125
1125
1126 def diff(self, repo, pats, opts):
1126 def diff(self, repo, pats, opts):
1127 top = self.check_toppatch(repo)
1127 top = self.check_toppatch(repo)
1128 if not top:
1128 if not top:
1129 self.ui.write(_("no patches applied\n"))
1129 self.ui.write(_("no patches applied\n"))
1130 return
1130 return
1131 qp = self.qparents(repo, top)
1131 qp = self.qparents(repo, top)
1132 if opts.get('inverse'):
1133 node1, node2 = None, qp
1134 else:
1135 node1, node2 = qp, None
1132 self._diffopts = patch.diffopts(self.ui, opts)
1136 self._diffopts = patch.diffopts(self.ui, opts)
1133 self.printdiff(repo, qp, files=pats, opts=opts)
1137 self.printdiff(repo, node1, node2, files=pats, opts=opts)
1134
1138
1135 def refresh(self, repo, pats=None, **opts):
1139 def refresh(self, repo, pats=None, **opts):
1136 if len(self.applied) == 0:
1140 if len(self.applied) == 0:
1137 self.ui.write(_("no patches applied\n"))
1141 self.ui.write(_("no patches applied\n"))
1138 return 1
1142 return 1
1139 msg = opts.get('msg', '').rstrip()
1143 msg = opts.get('msg', '').rstrip()
1140 newuser = opts.get('user')
1144 newuser = opts.get('user')
1141 newdate = opts.get('date')
1145 newdate = opts.get('date')
1142 if newdate:
1146 if newdate:
1143 newdate = '%d %d' % util.parsedate(newdate)
1147 newdate = '%d %d' % util.parsedate(newdate)
1144 wlock = repo.wlock()
1148 wlock = repo.wlock()
1145 try:
1149 try:
1146 self.check_toppatch(repo)
1150 self.check_toppatch(repo)
1147 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1151 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1148 top = bin(top)
1152 top = bin(top)
1149 if repo.changelog.heads(top) != [top]:
1153 if repo.changelog.heads(top) != [top]:
1150 raise util.Abort(_("cannot refresh a revision with children"))
1154 raise util.Abort(_("cannot refresh a revision with children"))
1151 cparents = repo.changelog.parents(top)
1155 cparents = repo.changelog.parents(top)
1152 patchparent = self.qparents(repo, top)
1156 patchparent = self.qparents(repo, top)
1153 ph = patchheader(self.join(patchfn))
1157 ph = patchheader(self.join(patchfn))
1154
1158
1155 patchf = self.opener(patchfn, 'r')
1159 patchf = self.opener(patchfn, 'r')
1156
1160
1157 # if the patch was a git patch, refresh it as a git patch
1161 # if the patch was a git patch, refresh it as a git patch
1158 for line in patchf:
1162 for line in patchf:
1159 if line.startswith('diff --git'):
1163 if line.startswith('diff --git'):
1160 self.diffopts().git = True
1164 self.diffopts().git = True
1161 break
1165 break
1162
1166
1163 if msg:
1167 if msg:
1164 ph.setmessage(msg)
1168 ph.setmessage(msg)
1165 if newuser:
1169 if newuser:
1166 ph.setuser(newuser)
1170 ph.setuser(newuser)
1167 if newdate:
1171 if newdate:
1168 ph.setdate(newdate)
1172 ph.setdate(newdate)
1169
1173
1170 # only commit new patch when write is complete
1174 # only commit new patch when write is complete
1171 patchf = self.opener(patchfn, 'w', atomictemp=True)
1175 patchf = self.opener(patchfn, 'w', atomictemp=True)
1172
1176
1173 patchf.seek(0)
1177 patchf.seek(0)
1174 patchf.truncate()
1178 patchf.truncate()
1175
1179
1176 comments = str(ph)
1180 comments = str(ph)
1177 if comments:
1181 if comments:
1178 patchf.write(comments)
1182 patchf.write(comments)
1179
1183
1180 if opts.get('git'):
1184 if opts.get('git'):
1181 self.diffopts().git = True
1185 self.diffopts().git = True
1182 tip = repo.changelog.tip()
1186 tip = repo.changelog.tip()
1183 if top == tip:
1187 if top == tip:
1184 # if the top of our patch queue is also the tip, there is an
1188 # if the top of our patch queue is also the tip, there is an
1185 # optimization here. We update the dirstate in place and strip
1189 # optimization here. We update the dirstate in place and strip
1186 # off the tip commit. Then just commit the current directory
1190 # off the tip commit. Then just commit the current directory
1187 # tree. We can also send repo.commit the list of files
1191 # tree. We can also send repo.commit the list of files
1188 # changed to speed up the diff
1192 # changed to speed up the diff
1189 #
1193 #
1190 # in short mode, we only diff the files included in the
1194 # in short mode, we only diff the files included in the
1191 # patch already plus specified files
1195 # patch already plus specified files
1192 #
1196 #
1193 # this should really read:
1197 # this should really read:
1194 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1198 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1195 # but we do it backwards to take advantage of manifest/chlog
1199 # but we do it backwards to take advantage of manifest/chlog
1196 # caching against the next repo.status call
1200 # caching against the next repo.status call
1197 #
1201 #
1198 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1202 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1199 changes = repo.changelog.read(tip)
1203 changes = repo.changelog.read(tip)
1200 man = repo.manifest.read(changes[0])
1204 man = repo.manifest.read(changes[0])
1201 aaa = aa[:]
1205 aaa = aa[:]
1202 matchfn = cmdutil.match(repo, pats, opts)
1206 matchfn = cmdutil.match(repo, pats, opts)
1203 if opts.get('short'):
1207 if opts.get('short'):
1204 # if amending a patch, we start with existing
1208 # if amending a patch, we start with existing
1205 # files plus specified files - unfiltered
1209 # files plus specified files - unfiltered
1206 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1210 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1207 # filter with inc/exl options
1211 # filter with inc/exl options
1208 matchfn = cmdutil.match(repo, opts=opts)
1212 matchfn = cmdutil.match(repo, opts=opts)
1209 else:
1213 else:
1210 match = cmdutil.matchall(repo)
1214 match = cmdutil.matchall(repo)
1211 m, a, r, d = repo.status(match=match)[:4]
1215 m, a, r, d = repo.status(match=match)[:4]
1212
1216
1213 # we might end up with files that were added between
1217 # we might end up with files that were added between
1214 # tip and the dirstate parent, but then changed in the
1218 # tip and the dirstate parent, but then changed in the
1215 # local dirstate. in this case, we want them to only
1219 # local dirstate. in this case, we want them to only
1216 # show up in the added section
1220 # show up in the added section
1217 for x in m:
1221 for x in m:
1218 if x not in aa:
1222 if x not in aa:
1219 mm.append(x)
1223 mm.append(x)
1220 # we might end up with files added by the local dirstate that
1224 # we might end up with files added by the local dirstate that
1221 # were deleted by the patch. In this case, they should only
1225 # were deleted by the patch. In this case, they should only
1222 # show up in the changed section.
1226 # show up in the changed section.
1223 for x in a:
1227 for x in a:
1224 if x in dd:
1228 if x in dd:
1225 del dd[dd.index(x)]
1229 del dd[dd.index(x)]
1226 mm.append(x)
1230 mm.append(x)
1227 else:
1231 else:
1228 aa.append(x)
1232 aa.append(x)
1229 # make sure any files deleted in the local dirstate
1233 # make sure any files deleted in the local dirstate
1230 # are not in the add or change column of the patch
1234 # are not in the add or change column of the patch
1231 forget = []
1235 forget = []
1232 for x in d + r:
1236 for x in d + r:
1233 if x in aa:
1237 if x in aa:
1234 del aa[aa.index(x)]
1238 del aa[aa.index(x)]
1235 forget.append(x)
1239 forget.append(x)
1236 continue
1240 continue
1237 elif x in mm:
1241 elif x in mm:
1238 del mm[mm.index(x)]
1242 del mm[mm.index(x)]
1239 dd.append(x)
1243 dd.append(x)
1240
1244
1241 m = list(set(mm))
1245 m = list(set(mm))
1242 r = list(set(dd))
1246 r = list(set(dd))
1243 a = list(set(aa))
1247 a = list(set(aa))
1244 c = [filter(matchfn, l) for l in (m, a, r)]
1248 c = [filter(matchfn, l) for l in (m, a, r)]
1245 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1249 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1246 chunks = patch.diff(repo, patchparent, match=match,
1250 chunks = patch.diff(repo, patchparent, match=match,
1247 changes=c, opts=self.diffopts())
1251 changes=c, opts=self.diffopts())
1248 for chunk in chunks:
1252 for chunk in chunks:
1249 patchf.write(chunk)
1253 patchf.write(chunk)
1250
1254
1251 try:
1255 try:
1252 if self.diffopts().git:
1256 if self.diffopts().git:
1253 copies = {}
1257 copies = {}
1254 for dst in a:
1258 for dst in a:
1255 src = repo.dirstate.copied(dst)
1259 src = repo.dirstate.copied(dst)
1256 # during qfold, the source file for copies may
1260 # during qfold, the source file for copies may
1257 # be removed. Treat this as a simple add.
1261 # be removed. Treat this as a simple add.
1258 if src is not None and src in repo.dirstate:
1262 if src is not None and src in repo.dirstate:
1259 copies.setdefault(src, []).append(dst)
1263 copies.setdefault(src, []).append(dst)
1260 repo.dirstate.add(dst)
1264 repo.dirstate.add(dst)
1261 # remember the copies between patchparent and tip
1265 # remember the copies between patchparent and tip
1262 for dst in aaa:
1266 for dst in aaa:
1263 f = repo.file(dst)
1267 f = repo.file(dst)
1264 src = f.renamed(man[dst])
1268 src = f.renamed(man[dst])
1265 if src:
1269 if src:
1266 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1270 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1267 if dst in a:
1271 if dst in a:
1268 copies[src[0]].append(dst)
1272 copies[src[0]].append(dst)
1269 # we can't copy a file created by the patch itself
1273 # we can't copy a file created by the patch itself
1270 if dst in copies:
1274 if dst in copies:
1271 del copies[dst]
1275 del copies[dst]
1272 for src, dsts in copies.iteritems():
1276 for src, dsts in copies.iteritems():
1273 for dst in dsts:
1277 for dst in dsts:
1274 repo.dirstate.copy(src, dst)
1278 repo.dirstate.copy(src, dst)
1275 else:
1279 else:
1276 for dst in a:
1280 for dst in a:
1277 repo.dirstate.add(dst)
1281 repo.dirstate.add(dst)
1278 # Drop useless copy information
1282 # Drop useless copy information
1279 for f in list(repo.dirstate.copies()):
1283 for f in list(repo.dirstate.copies()):
1280 repo.dirstate.copy(None, f)
1284 repo.dirstate.copy(None, f)
1281 for f in r:
1285 for f in r:
1282 repo.dirstate.remove(f)
1286 repo.dirstate.remove(f)
1283 # if the patch excludes a modified file, mark that
1287 # if the patch excludes a modified file, mark that
1284 # file with mtime=0 so status can see it.
1288 # file with mtime=0 so status can see it.
1285 mm = []
1289 mm = []
1286 for i in xrange(len(m)-1, -1, -1):
1290 for i in xrange(len(m)-1, -1, -1):
1287 if not matchfn(m[i]):
1291 if not matchfn(m[i]):
1288 mm.append(m[i])
1292 mm.append(m[i])
1289 del m[i]
1293 del m[i]
1290 for f in m:
1294 for f in m:
1291 repo.dirstate.normal(f)
1295 repo.dirstate.normal(f)
1292 for f in mm:
1296 for f in mm:
1293 repo.dirstate.normallookup(f)
1297 repo.dirstate.normallookup(f)
1294 for f in forget:
1298 for f in forget:
1295 repo.dirstate.forget(f)
1299 repo.dirstate.forget(f)
1296
1300
1297 if not msg:
1301 if not msg:
1298 if not ph.message:
1302 if not ph.message:
1299 message = "[mq]: %s\n" % patchfn
1303 message = "[mq]: %s\n" % patchfn
1300 else:
1304 else:
1301 message = "\n".join(ph.message)
1305 message = "\n".join(ph.message)
1302 else:
1306 else:
1303 message = msg
1307 message = msg
1304
1308
1305 user = ph.user or changes[1]
1309 user = ph.user or changes[1]
1306
1310
1307 # assumes strip can roll itself back if interrupted
1311 # assumes strip can roll itself back if interrupted
1308 repo.dirstate.setparents(*cparents)
1312 repo.dirstate.setparents(*cparents)
1309 self.applied.pop()
1313 self.applied.pop()
1310 self.applied_dirty = 1
1314 self.applied_dirty = 1
1311 self.strip(repo, top, update=False,
1315 self.strip(repo, top, update=False,
1312 backup='strip')
1316 backup='strip')
1313 except:
1317 except:
1314 repo.dirstate.invalidate()
1318 repo.dirstate.invalidate()
1315 raise
1319 raise
1316
1320
1317 try:
1321 try:
1318 # might be nice to attempt to roll back strip after this
1322 # might be nice to attempt to roll back strip after this
1319 patchf.rename()
1323 patchf.rename()
1320 n = repo.commit(message, user, ph.date, match=match,
1324 n = repo.commit(message, user, ph.date, match=match,
1321 force=True)
1325 force=True)
1322 self.applied.append(statusentry(hex(n), patchfn))
1326 self.applied.append(statusentry(hex(n), patchfn))
1323 except:
1327 except:
1324 ctx = repo[cparents[0]]
1328 ctx = repo[cparents[0]]
1325 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1329 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1326 self.save_dirty()
1330 self.save_dirty()
1327 self.ui.warn(_('refresh interrupted while patch was popped! '
1331 self.ui.warn(_('refresh interrupted while patch was popped! '
1328 '(revert --all, qpush to recover)\n'))
1332 '(revert --all, qpush to recover)\n'))
1329 raise
1333 raise
1330 else:
1334 else:
1331 self.printdiff(repo, patchparent, fp=patchf)
1335 self.printdiff(repo, patchparent, fp=patchf)
1332 patchf.rename()
1336 patchf.rename()
1333 added = repo.status()[1]
1337 added = repo.status()[1]
1334 for a in added:
1338 for a in added:
1335 f = repo.wjoin(a)
1339 f = repo.wjoin(a)
1336 try:
1340 try:
1337 os.unlink(f)
1341 os.unlink(f)
1338 except OSError, e:
1342 except OSError, e:
1339 if e.errno != errno.ENOENT:
1343 if e.errno != errno.ENOENT:
1340 raise
1344 raise
1341 try: os.removedirs(os.path.dirname(f))
1345 try: os.removedirs(os.path.dirname(f))
1342 except: pass
1346 except: pass
1343 # forget the file copies in the dirstate
1347 # forget the file copies in the dirstate
1344 # push should readd the files later on
1348 # push should readd the files later on
1345 repo.dirstate.forget(a)
1349 repo.dirstate.forget(a)
1346 self.pop(repo, force=True)
1350 self.pop(repo, force=True)
1347 self.push(repo, force=True)
1351 self.push(repo, force=True)
1348 finally:
1352 finally:
1349 wlock.release()
1353 wlock.release()
1350 self.removeundo(repo)
1354 self.removeundo(repo)
1351
1355
1352 def init(self, repo, create=False):
1356 def init(self, repo, create=False):
1353 if not create and os.path.isdir(self.path):
1357 if not create and os.path.isdir(self.path):
1354 raise util.Abort(_("patch queue directory already exists"))
1358 raise util.Abort(_("patch queue directory already exists"))
1355 try:
1359 try:
1356 os.mkdir(self.path)
1360 os.mkdir(self.path)
1357 except OSError, inst:
1361 except OSError, inst:
1358 if inst.errno != errno.EEXIST or not create:
1362 if inst.errno != errno.EEXIST or not create:
1359 raise
1363 raise
1360 if create:
1364 if create:
1361 return self.qrepo(create=True)
1365 return self.qrepo(create=True)
1362
1366
1363 def unapplied(self, repo, patch=None):
1367 def unapplied(self, repo, patch=None):
1364 if patch and patch not in self.series:
1368 if patch and patch not in self.series:
1365 raise util.Abort(_("patch %s is not in series file") % patch)
1369 raise util.Abort(_("patch %s is not in series file") % patch)
1366 if not patch:
1370 if not patch:
1367 start = self.series_end()
1371 start = self.series_end()
1368 else:
1372 else:
1369 start = self.series.index(patch) + 1
1373 start = self.series.index(patch) + 1
1370 unapplied = []
1374 unapplied = []
1371 for i in xrange(start, len(self.series)):
1375 for i in xrange(start, len(self.series)):
1372 pushable, reason = self.pushable(i)
1376 pushable, reason = self.pushable(i)
1373 if pushable:
1377 if pushable:
1374 unapplied.append((i, self.series[i]))
1378 unapplied.append((i, self.series[i]))
1375 self.explain_pushable(i)
1379 self.explain_pushable(i)
1376 return unapplied
1380 return unapplied
1377
1381
1378 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1382 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1379 summary=False):
1383 summary=False):
1380 def displayname(pfx, patchname):
1384 def displayname(pfx, patchname):
1381 if summary:
1385 if summary:
1382 ph = patchheader(self.join(patchname))
1386 ph = patchheader(self.join(patchname))
1383 msg = ph.message
1387 msg = ph.message
1384 msg = msg and ': ' + msg[0] or ': '
1388 msg = msg and ': ' + msg[0] or ': '
1385 else:
1389 else:
1386 msg = ''
1390 msg = ''
1387 msg = "%s%s%s" % (pfx, patchname, msg)
1391 msg = "%s%s%s" % (pfx, patchname, msg)
1388 if self.ui.interactive():
1392 if self.ui.interactive():
1389 msg = util.ellipsis(msg, util.termwidth())
1393 msg = util.ellipsis(msg, util.termwidth())
1390 self.ui.write(msg + '\n')
1394 self.ui.write(msg + '\n')
1391
1395
1392 applied = set([p.name for p in self.applied])
1396 applied = set([p.name for p in self.applied])
1393 if length is None:
1397 if length is None:
1394 length = len(self.series) - start
1398 length = len(self.series) - start
1395 if not missing:
1399 if not missing:
1396 if self.ui.verbose:
1400 if self.ui.verbose:
1397 idxwidth = len(str(start+length - 1))
1401 idxwidth = len(str(start+length - 1))
1398 for i in xrange(start, start+length):
1402 for i in xrange(start, start+length):
1399 patch = self.series[i]
1403 patch = self.series[i]
1400 if patch in applied:
1404 if patch in applied:
1401 stat = 'A'
1405 stat = 'A'
1402 elif self.pushable(i)[0]:
1406 elif self.pushable(i)[0]:
1403 stat = 'U'
1407 stat = 'U'
1404 else:
1408 else:
1405 stat = 'G'
1409 stat = 'G'
1406 pfx = ''
1410 pfx = ''
1407 if self.ui.verbose:
1411 if self.ui.verbose:
1408 pfx = '%*d %s ' % (idxwidth, i, stat)
1412 pfx = '%*d %s ' % (idxwidth, i, stat)
1409 elif status and status != stat:
1413 elif status and status != stat:
1410 continue
1414 continue
1411 displayname(pfx, patch)
1415 displayname(pfx, patch)
1412 else:
1416 else:
1413 msng_list = []
1417 msng_list = []
1414 for root, dirs, files in os.walk(self.path):
1418 for root, dirs, files in os.walk(self.path):
1415 d = root[len(self.path) + 1:]
1419 d = root[len(self.path) + 1:]
1416 for f in files:
1420 for f in files:
1417 fl = os.path.join(d, f)
1421 fl = os.path.join(d, f)
1418 if (fl not in self.series and
1422 if (fl not in self.series and
1419 fl not in (self.status_path, self.series_path,
1423 fl not in (self.status_path, self.series_path,
1420 self.guards_path)
1424 self.guards_path)
1421 and not fl.startswith('.')):
1425 and not fl.startswith('.')):
1422 msng_list.append(fl)
1426 msng_list.append(fl)
1423 for x in sorted(msng_list):
1427 for x in sorted(msng_list):
1424 pfx = self.ui.verbose and ('D ') or ''
1428 pfx = self.ui.verbose and ('D ') or ''
1425 displayname(pfx, x)
1429 displayname(pfx, x)
1426
1430
1427 def issaveline(self, l):
1431 def issaveline(self, l):
1428 if l.name == '.hg.patches.save.line':
1432 if l.name == '.hg.patches.save.line':
1429 return True
1433 return True
1430
1434
1431 def qrepo(self, create=False):
1435 def qrepo(self, create=False):
1432 if create or os.path.isdir(self.join(".hg")):
1436 if create or os.path.isdir(self.join(".hg")):
1433 return hg.repository(self.ui, path=self.path, create=create)
1437 return hg.repository(self.ui, path=self.path, create=create)
1434
1438
1435 def restore(self, repo, rev, delete=None, qupdate=None):
1439 def restore(self, repo, rev, delete=None, qupdate=None):
1436 c = repo.changelog.read(rev)
1440 c = repo.changelog.read(rev)
1437 desc = c[4].strip()
1441 desc = c[4].strip()
1438 lines = desc.splitlines()
1442 lines = desc.splitlines()
1439 i = 0
1443 i = 0
1440 datastart = None
1444 datastart = None
1441 series = []
1445 series = []
1442 applied = []
1446 applied = []
1443 qpp = None
1447 qpp = None
1444 for i, line in enumerate(lines):
1448 for i, line in enumerate(lines):
1445 if line == 'Patch Data:':
1449 if line == 'Patch Data:':
1446 datastart = i + 1
1450 datastart = i + 1
1447 elif line.startswith('Dirstate:'):
1451 elif line.startswith('Dirstate:'):
1448 l = line.rstrip()
1452 l = line.rstrip()
1449 l = l[10:].split(' ')
1453 l = l[10:].split(' ')
1450 qpp = [ bin(x) for x in l ]
1454 qpp = [ bin(x) for x in l ]
1451 elif datastart != None:
1455 elif datastart != None:
1452 l = line.rstrip()
1456 l = line.rstrip()
1453 se = statusentry(l)
1457 se = statusentry(l)
1454 file_ = se.name
1458 file_ = se.name
1455 if se.rev:
1459 if se.rev:
1456 applied.append(se)
1460 applied.append(se)
1457 else:
1461 else:
1458 series.append(file_)
1462 series.append(file_)
1459 if datastart is None:
1463 if datastart is None:
1460 self.ui.warn(_("No saved patch data found\n"))
1464 self.ui.warn(_("No saved patch data found\n"))
1461 return 1
1465 return 1
1462 self.ui.warn(_("restoring status: %s\n") % lines[0])
1466 self.ui.warn(_("restoring status: %s\n") % lines[0])
1463 self.full_series = series
1467 self.full_series = series
1464 self.applied = applied
1468 self.applied = applied
1465 self.parse_series()
1469 self.parse_series()
1466 self.series_dirty = 1
1470 self.series_dirty = 1
1467 self.applied_dirty = 1
1471 self.applied_dirty = 1
1468 heads = repo.changelog.heads()
1472 heads = repo.changelog.heads()
1469 if delete:
1473 if delete:
1470 if rev not in heads:
1474 if rev not in heads:
1471 self.ui.warn(_("save entry has children, leaving it alone\n"))
1475 self.ui.warn(_("save entry has children, leaving it alone\n"))
1472 else:
1476 else:
1473 self.ui.warn(_("removing save entry %s\n") % short(rev))
1477 self.ui.warn(_("removing save entry %s\n") % short(rev))
1474 pp = repo.dirstate.parents()
1478 pp = repo.dirstate.parents()
1475 if rev in pp:
1479 if rev in pp:
1476 update = True
1480 update = True
1477 else:
1481 else:
1478 update = False
1482 update = False
1479 self.strip(repo, rev, update=update, backup='strip')
1483 self.strip(repo, rev, update=update, backup='strip')
1480 if qpp:
1484 if qpp:
1481 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1485 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1482 (short(qpp[0]), short(qpp[1])))
1486 (short(qpp[0]), short(qpp[1])))
1483 if qupdate:
1487 if qupdate:
1484 self.ui.status(_("queue directory updating\n"))
1488 self.ui.status(_("queue directory updating\n"))
1485 r = self.qrepo()
1489 r = self.qrepo()
1486 if not r:
1490 if not r:
1487 self.ui.warn(_("Unable to load queue repository\n"))
1491 self.ui.warn(_("Unable to load queue repository\n"))
1488 return 1
1492 return 1
1489 hg.clean(r, qpp[0])
1493 hg.clean(r, qpp[0])
1490
1494
1491 def save(self, repo, msg=None):
1495 def save(self, repo, msg=None):
1492 if len(self.applied) == 0:
1496 if len(self.applied) == 0:
1493 self.ui.warn(_("save: no patches applied, exiting\n"))
1497 self.ui.warn(_("save: no patches applied, exiting\n"))
1494 return 1
1498 return 1
1495 if self.issaveline(self.applied[-1]):
1499 if self.issaveline(self.applied[-1]):
1496 self.ui.warn(_("status is already saved\n"))
1500 self.ui.warn(_("status is already saved\n"))
1497 return 1
1501 return 1
1498
1502
1499 ar = [ ':' + x for x in self.full_series ]
1503 ar = [ ':' + x for x in self.full_series ]
1500 if not msg:
1504 if not msg:
1501 msg = _("hg patches saved state")
1505 msg = _("hg patches saved state")
1502 else:
1506 else:
1503 msg = "hg patches: " + msg.rstrip('\r\n')
1507 msg = "hg patches: " + msg.rstrip('\r\n')
1504 r = self.qrepo()
1508 r = self.qrepo()
1505 if r:
1509 if r:
1506 pp = r.dirstate.parents()
1510 pp = r.dirstate.parents()
1507 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1511 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1508 msg += "\n\nPatch Data:\n"
1512 msg += "\n\nPatch Data:\n"
1509 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1513 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1510 "\n".join(ar) + '\n' or "")
1514 "\n".join(ar) + '\n' or "")
1511 n = repo.commit(text, force=True)
1515 n = repo.commit(text, force=True)
1512 if not n:
1516 if not n:
1513 self.ui.warn(_("repo commit failed\n"))
1517 self.ui.warn(_("repo commit failed\n"))
1514 return 1
1518 return 1
1515 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1519 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1516 self.applied_dirty = 1
1520 self.applied_dirty = 1
1517 self.removeundo(repo)
1521 self.removeundo(repo)
1518
1522
1519 def full_series_end(self):
1523 def full_series_end(self):
1520 if len(self.applied) > 0:
1524 if len(self.applied) > 0:
1521 p = self.applied[-1].name
1525 p = self.applied[-1].name
1522 end = self.find_series(p)
1526 end = self.find_series(p)
1523 if end is None:
1527 if end is None:
1524 return len(self.full_series)
1528 return len(self.full_series)
1525 return end + 1
1529 return end + 1
1526 return 0
1530 return 0
1527
1531
1528 def series_end(self, all_patches=False):
1532 def series_end(self, all_patches=False):
1529 """If all_patches is False, return the index of the next pushable patch
1533 """If all_patches is False, return the index of the next pushable patch
1530 in the series, or the series length. If all_patches is True, return the
1534 in the series, or the series length. If all_patches is True, return the
1531 index of the first patch past the last applied one.
1535 index of the first patch past the last applied one.
1532 """
1536 """
1533 end = 0
1537 end = 0
1534 def next(start):
1538 def next(start):
1535 if all_patches:
1539 if all_patches:
1536 return start
1540 return start
1537 i = start
1541 i = start
1538 while i < len(self.series):
1542 while i < len(self.series):
1539 p, reason = self.pushable(i)
1543 p, reason = self.pushable(i)
1540 if p:
1544 if p:
1541 break
1545 break
1542 self.explain_pushable(i)
1546 self.explain_pushable(i)
1543 i += 1
1547 i += 1
1544 return i
1548 return i
1545 if len(self.applied) > 0:
1549 if len(self.applied) > 0:
1546 p = self.applied[-1].name
1550 p = self.applied[-1].name
1547 try:
1551 try:
1548 end = self.series.index(p)
1552 end = self.series.index(p)
1549 except ValueError:
1553 except ValueError:
1550 return 0
1554 return 0
1551 return next(end + 1)
1555 return next(end + 1)
1552 return next(end)
1556 return next(end)
1553
1557
1554 def appliedname(self, index):
1558 def appliedname(self, index):
1555 pname = self.applied[index].name
1559 pname = self.applied[index].name
1556 if not self.ui.verbose:
1560 if not self.ui.verbose:
1557 p = pname
1561 p = pname
1558 else:
1562 else:
1559 p = str(self.series.index(pname)) + " " + pname
1563 p = str(self.series.index(pname)) + " " + pname
1560 return p
1564 return p
1561
1565
1562 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1566 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1563 force=None, git=False):
1567 force=None, git=False):
1564 def checkseries(patchname):
1568 def checkseries(patchname):
1565 if patchname in self.series:
1569 if patchname in self.series:
1566 raise util.Abort(_('patch %s is already in the series file')
1570 raise util.Abort(_('patch %s is already in the series file')
1567 % patchname)
1571 % patchname)
1568 def checkfile(patchname):
1572 def checkfile(patchname):
1569 if not force and os.path.exists(self.join(patchname)):
1573 if not force and os.path.exists(self.join(patchname)):
1570 raise util.Abort(_('patch "%s" already exists')
1574 raise util.Abort(_('patch "%s" already exists')
1571 % patchname)
1575 % patchname)
1572
1576
1573 if rev:
1577 if rev:
1574 if files:
1578 if files:
1575 raise util.Abort(_('option "-r" not valid when importing '
1579 raise util.Abort(_('option "-r" not valid when importing '
1576 'files'))
1580 'files'))
1577 rev = cmdutil.revrange(repo, rev)
1581 rev = cmdutil.revrange(repo, rev)
1578 rev.sort(reverse=True)
1582 rev.sort(reverse=True)
1579 if (len(files) > 1 or len(rev) > 1) and patchname:
1583 if (len(files) > 1 or len(rev) > 1) and patchname:
1580 raise util.Abort(_('option "-n" not valid when importing multiple '
1584 raise util.Abort(_('option "-n" not valid when importing multiple '
1581 'patches'))
1585 'patches'))
1582 i = 0
1586 i = 0
1583 added = []
1587 added = []
1584 if rev:
1588 if rev:
1585 # If mq patches are applied, we can only import revisions
1589 # If mq patches are applied, we can only import revisions
1586 # that form a linear path to qbase.
1590 # that form a linear path to qbase.
1587 # Otherwise, they should form a linear path to a head.
1591 # Otherwise, they should form a linear path to a head.
1588 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1592 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1589 if len(heads) > 1:
1593 if len(heads) > 1:
1590 raise util.Abort(_('revision %d is the root of more than one '
1594 raise util.Abort(_('revision %d is the root of more than one '
1591 'branch') % rev[-1])
1595 'branch') % rev[-1])
1592 if self.applied:
1596 if self.applied:
1593 base = hex(repo.changelog.node(rev[0]))
1597 base = hex(repo.changelog.node(rev[0]))
1594 if base in [n.rev for n in self.applied]:
1598 if base in [n.rev for n in self.applied]:
1595 raise util.Abort(_('revision %d is already managed')
1599 raise util.Abort(_('revision %d is already managed')
1596 % rev[0])
1600 % rev[0])
1597 if heads != [bin(self.applied[-1].rev)]:
1601 if heads != [bin(self.applied[-1].rev)]:
1598 raise util.Abort(_('revision %d is not the parent of '
1602 raise util.Abort(_('revision %d is not the parent of '
1599 'the queue') % rev[0])
1603 'the queue') % rev[0])
1600 base = repo.changelog.rev(bin(self.applied[0].rev))
1604 base = repo.changelog.rev(bin(self.applied[0].rev))
1601 lastparent = repo.changelog.parentrevs(base)[0]
1605 lastparent = repo.changelog.parentrevs(base)[0]
1602 else:
1606 else:
1603 if heads != [repo.changelog.node(rev[0])]:
1607 if heads != [repo.changelog.node(rev[0])]:
1604 raise util.Abort(_('revision %d has unmanaged children')
1608 raise util.Abort(_('revision %d has unmanaged children')
1605 % rev[0])
1609 % rev[0])
1606 lastparent = None
1610 lastparent = None
1607
1611
1608 if git:
1612 if git:
1609 self.diffopts().git = True
1613 self.diffopts().git = True
1610
1614
1611 for r in rev:
1615 for r in rev:
1612 p1, p2 = repo.changelog.parentrevs(r)
1616 p1, p2 = repo.changelog.parentrevs(r)
1613 n = repo.changelog.node(r)
1617 n = repo.changelog.node(r)
1614 if p2 != nullrev:
1618 if p2 != nullrev:
1615 raise util.Abort(_('cannot import merge revision %d') % r)
1619 raise util.Abort(_('cannot import merge revision %d') % r)
1616 if lastparent and lastparent != r:
1620 if lastparent and lastparent != r:
1617 raise util.Abort(_('revision %d is not the parent of %d')
1621 raise util.Abort(_('revision %d is not the parent of %d')
1618 % (r, lastparent))
1622 % (r, lastparent))
1619 lastparent = p1
1623 lastparent = p1
1620
1624
1621 if not patchname:
1625 if not patchname:
1622 patchname = normname('%d.diff' % r)
1626 patchname = normname('%d.diff' % r)
1623 self.check_reserved_name(patchname)
1627 self.check_reserved_name(patchname)
1624 checkseries(patchname)
1628 checkseries(patchname)
1625 checkfile(patchname)
1629 checkfile(patchname)
1626 self.full_series.insert(0, patchname)
1630 self.full_series.insert(0, patchname)
1627
1631
1628 patchf = self.opener(patchname, "w")
1632 patchf = self.opener(patchname, "w")
1629 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1633 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1630 patchf.close()
1634 patchf.close()
1631
1635
1632 se = statusentry(hex(n), patchname)
1636 se = statusentry(hex(n), patchname)
1633 self.applied.insert(0, se)
1637 self.applied.insert(0, se)
1634
1638
1635 added.append(patchname)
1639 added.append(patchname)
1636 patchname = None
1640 patchname = None
1637 self.parse_series()
1641 self.parse_series()
1638 self.applied_dirty = 1
1642 self.applied_dirty = 1
1639
1643
1640 for filename in files:
1644 for filename in files:
1641 if existing:
1645 if existing:
1642 if filename == '-':
1646 if filename == '-':
1643 raise util.Abort(_('-e is incompatible with import from -'))
1647 raise util.Abort(_('-e is incompatible with import from -'))
1644 if not patchname:
1648 if not patchname:
1645 patchname = normname(filename)
1649 patchname = normname(filename)
1646 self.check_reserved_name(patchname)
1650 self.check_reserved_name(patchname)
1647 if not os.path.isfile(self.join(patchname)):
1651 if not os.path.isfile(self.join(patchname)):
1648 raise util.Abort(_("patch %s does not exist") % patchname)
1652 raise util.Abort(_("patch %s does not exist") % patchname)
1649 else:
1653 else:
1650 try:
1654 try:
1651 if filename == '-':
1655 if filename == '-':
1652 if not patchname:
1656 if not patchname:
1653 raise util.Abort(_('need --name to import a patch from -'))
1657 raise util.Abort(_('need --name to import a patch from -'))
1654 text = sys.stdin.read()
1658 text = sys.stdin.read()
1655 else:
1659 else:
1656 text = url.open(self.ui, filename).read()
1660 text = url.open(self.ui, filename).read()
1657 except (OSError, IOError):
1661 except (OSError, IOError):
1658 raise util.Abort(_("unable to read %s") % filename)
1662 raise util.Abort(_("unable to read %s") % filename)
1659 if not patchname:
1663 if not patchname:
1660 patchname = normname(os.path.basename(filename))
1664 patchname = normname(os.path.basename(filename))
1661 self.check_reserved_name(patchname)
1665 self.check_reserved_name(patchname)
1662 checkfile(patchname)
1666 checkfile(patchname)
1663 patchf = self.opener(patchname, "w")
1667 patchf = self.opener(patchname, "w")
1664 patchf.write(text)
1668 patchf.write(text)
1665 if not force:
1669 if not force:
1666 checkseries(patchname)
1670 checkseries(patchname)
1667 if patchname not in self.series:
1671 if patchname not in self.series:
1668 index = self.full_series_end() + i
1672 index = self.full_series_end() + i
1669 self.full_series[index:index] = [patchname]
1673 self.full_series[index:index] = [patchname]
1670 self.parse_series()
1674 self.parse_series()
1671 self.ui.warn(_("adding %s to series file\n") % patchname)
1675 self.ui.warn(_("adding %s to series file\n") % patchname)
1672 i += 1
1676 i += 1
1673 added.append(patchname)
1677 added.append(patchname)
1674 patchname = None
1678 patchname = None
1675 self.series_dirty = 1
1679 self.series_dirty = 1
1676 qrepo = self.qrepo()
1680 qrepo = self.qrepo()
1677 if qrepo:
1681 if qrepo:
1678 qrepo.add(added)
1682 qrepo.add(added)
1679
1683
1680 def delete(ui, repo, *patches, **opts):
1684 def delete(ui, repo, *patches, **opts):
1681 """remove patches from queue
1685 """remove patches from queue
1682
1686
1683 The patches must not be applied, and at least one patch is required. With
1687 The patches must not be applied, and at least one patch is required. With
1684 -k/--keep, the patch files are preserved in the patch directory.
1688 -k/--keep, the patch files are preserved in the patch directory.
1685
1689
1686 To stop managing a patch and move it into permanent history,
1690 To stop managing a patch and move it into permanent history,
1687 use the qfinish command."""
1691 use the qfinish command."""
1688 q = repo.mq
1692 q = repo.mq
1689 q.delete(repo, patches, opts)
1693 q.delete(repo, patches, opts)
1690 q.save_dirty()
1694 q.save_dirty()
1691 return 0
1695 return 0
1692
1696
1693 def applied(ui, repo, patch=None, **opts):
1697 def applied(ui, repo, patch=None, **opts):
1694 """print the patches already applied"""
1698 """print the patches already applied"""
1695
1699
1696 q = repo.mq
1700 q = repo.mq
1697 l = len(q.applied)
1701 l = len(q.applied)
1698
1702
1699 if patch:
1703 if patch:
1700 if patch not in q.series:
1704 if patch not in q.series:
1701 raise util.Abort(_("patch %s is not in series file") % patch)
1705 raise util.Abort(_("patch %s is not in series file") % patch)
1702 end = q.series.index(patch) + 1
1706 end = q.series.index(patch) + 1
1703 else:
1707 else:
1704 end = q.series_end(True)
1708 end = q.series_end(True)
1705
1709
1706 if opts.get('last') and not end:
1710 if opts.get('last') and not end:
1707 ui.write(_("no patches applied\n"))
1711 ui.write(_("no patches applied\n"))
1708 return 1
1712 return 1
1709 elif opts.get('last') and end == 1:
1713 elif opts.get('last') and end == 1:
1710 ui.write(_("only one patch applied\n"))
1714 ui.write(_("only one patch applied\n"))
1711 return 1
1715 return 1
1712 elif opts.get('last'):
1716 elif opts.get('last'):
1713 start = end - 2
1717 start = end - 2
1714 end = 1
1718 end = 1
1715 else:
1719 else:
1716 start = 0
1720 start = 0
1717
1721
1718 return q.qseries(repo, length=end, start=start, status='A',
1722 return q.qseries(repo, length=end, start=start, status='A',
1719 summary=opts.get('summary'))
1723 summary=opts.get('summary'))
1720
1724
1721 def unapplied(ui, repo, patch=None, **opts):
1725 def unapplied(ui, repo, patch=None, **opts):
1722 """print the patches not yet applied"""
1726 """print the patches not yet applied"""
1723
1727
1724 q = repo.mq
1728 q = repo.mq
1725 if patch:
1729 if patch:
1726 if patch not in q.series:
1730 if patch not in q.series:
1727 raise util.Abort(_("patch %s is not in series file") % patch)
1731 raise util.Abort(_("patch %s is not in series file") % patch)
1728 start = q.series.index(patch) + 1
1732 start = q.series.index(patch) + 1
1729 else:
1733 else:
1730 start = q.series_end(True)
1734 start = q.series_end(True)
1731
1735
1732 if start == len(q.series) and opts.get('first'):
1736 if start == len(q.series) and opts.get('first'):
1733 ui.write(_("all patches applied\n"))
1737 ui.write(_("all patches applied\n"))
1734 return 1
1738 return 1
1735
1739
1736 length = opts.get('first') and 1 or None
1740 length = opts.get('first') and 1 or None
1737 return q.qseries(repo, start=start, length=length, status='U',
1741 return q.qseries(repo, start=start, length=length, status='U',
1738 summary=opts.get('summary'))
1742 summary=opts.get('summary'))
1739
1743
1740 def qimport(ui, repo, *filename, **opts):
1744 def qimport(ui, repo, *filename, **opts):
1741 """import a patch
1745 """import a patch
1742
1746
1743 The patch is inserted into the series after the last applied
1747 The patch is inserted into the series after the last applied
1744 patch. If no patches have been applied, qimport prepends the patch
1748 patch. If no patches have been applied, qimport prepends the patch
1745 to the series.
1749 to the series.
1746
1750
1747 The patch will have the same name as its source file unless you
1751 The patch will have the same name as its source file unless you
1748 give it a new one with -n/--name.
1752 give it a new one with -n/--name.
1749
1753
1750 You can register an existing patch inside the patch directory with
1754 You can register an existing patch inside the patch directory with
1751 the -e/--existing flag.
1755 the -e/--existing flag.
1752
1756
1753 With -f/--force, an existing patch of the same name will be
1757 With -f/--force, an existing patch of the same name will be
1754 overwritten.
1758 overwritten.
1755
1759
1756 An existing changeset may be placed under mq control with -r/--rev
1760 An existing changeset may be placed under mq control with -r/--rev
1757 (e.g. qimport --rev tip -n patch will place tip under mq control).
1761 (e.g. qimport --rev tip -n patch will place tip under mq control).
1758 With -g/--git, patches imported with --rev will use the git diff
1762 With -g/--git, patches imported with --rev will use the git diff
1759 format. See the diffs help topic for information on why this is
1763 format. See the diffs help topic for information on why this is
1760 important for preserving rename/copy information and permission
1764 important for preserving rename/copy information and permission
1761 changes.
1765 changes.
1762
1766
1763 To import a patch from standard input, pass - as the patch file.
1767 To import a patch from standard input, pass - as the patch file.
1764 When importing from standard input, a patch name must be specified
1768 When importing from standard input, a patch name must be specified
1765 using the --name flag.
1769 using the --name flag.
1766 """
1770 """
1767 q = repo.mq
1771 q = repo.mq
1768 q.qimport(repo, filename, patchname=opts['name'],
1772 q.qimport(repo, filename, patchname=opts['name'],
1769 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1773 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1770 git=opts['git'])
1774 git=opts['git'])
1771 q.save_dirty()
1775 q.save_dirty()
1772
1776
1773 if opts.get('push') and not opts.get('rev'):
1777 if opts.get('push') and not opts.get('rev'):
1774 return q.push(repo, None)
1778 return q.push(repo, None)
1775 return 0
1779 return 0
1776
1780
1777 def init(ui, repo, **opts):
1781 def init(ui, repo, **opts):
1778 """init a new queue repository
1782 """init a new queue repository
1779
1783
1780 The queue repository is unversioned by default. If
1784 The queue repository is unversioned by default. If
1781 -c/--create-repo is specified, qinit will create a separate nested
1785 -c/--create-repo is specified, qinit will create a separate nested
1782 repository for patches (qinit -c may also be run later to convert
1786 repository for patches (qinit -c may also be run later to convert
1783 an unversioned patch repository into a versioned one). You can use
1787 an unversioned patch repository into a versioned one). You can use
1784 qcommit to commit changes to this queue repository."""
1788 qcommit to commit changes to this queue repository."""
1785 q = repo.mq
1789 q = repo.mq
1786 r = q.init(repo, create=opts['create_repo'])
1790 r = q.init(repo, create=opts['create_repo'])
1787 q.save_dirty()
1791 q.save_dirty()
1788 if r:
1792 if r:
1789 if not os.path.exists(r.wjoin('.hgignore')):
1793 if not os.path.exists(r.wjoin('.hgignore')):
1790 fp = r.wopener('.hgignore', 'w')
1794 fp = r.wopener('.hgignore', 'w')
1791 fp.write('^\\.hg\n')
1795 fp.write('^\\.hg\n')
1792 fp.write('^\\.mq\n')
1796 fp.write('^\\.mq\n')
1793 fp.write('syntax: glob\n')
1797 fp.write('syntax: glob\n')
1794 fp.write('status\n')
1798 fp.write('status\n')
1795 fp.write('guards\n')
1799 fp.write('guards\n')
1796 fp.close()
1800 fp.close()
1797 if not os.path.exists(r.wjoin('series')):
1801 if not os.path.exists(r.wjoin('series')):
1798 r.wopener('series', 'w').close()
1802 r.wopener('series', 'w').close()
1799 r.add(['.hgignore', 'series'])
1803 r.add(['.hgignore', 'series'])
1800 commands.add(ui, r)
1804 commands.add(ui, r)
1801 return 0
1805 return 0
1802
1806
1803 def clone(ui, source, dest=None, **opts):
1807 def clone(ui, source, dest=None, **opts):
1804 '''clone main and patch repository at same time
1808 '''clone main and patch repository at same time
1805
1809
1806 If source is local, destination will have no patches applied. If
1810 If source is local, destination will have no patches applied. If
1807 source is remote, this command can not check if patches are
1811 source is remote, this command can not check if patches are
1808 applied in source, so cannot guarantee that patches are not
1812 applied in source, so cannot guarantee that patches are not
1809 applied in destination. If you clone remote repository, be sure
1813 applied in destination. If you clone remote repository, be sure
1810 before that it has no patches applied.
1814 before that it has no patches applied.
1811
1815
1812 Source patch repository is looked for in <src>/.hg/patches by
1816 Source patch repository is looked for in <src>/.hg/patches by
1813 default. Use -p <url> to change.
1817 default. Use -p <url> to change.
1814
1818
1815 The patch directory must be a nested Mercurial repository, as
1819 The patch directory must be a nested Mercurial repository, as
1816 would be created by qinit -c.
1820 would be created by qinit -c.
1817 '''
1821 '''
1818 def patchdir(repo):
1822 def patchdir(repo):
1819 url = repo.url()
1823 url = repo.url()
1820 if url.endswith('/'):
1824 if url.endswith('/'):
1821 url = url[:-1]
1825 url = url[:-1]
1822 return url + '/.hg/patches'
1826 return url + '/.hg/patches'
1823 if dest is None:
1827 if dest is None:
1824 dest = hg.defaultdest(source)
1828 dest = hg.defaultdest(source)
1825 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1829 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1826 if opts['patches']:
1830 if opts['patches']:
1827 patchespath = ui.expandpath(opts['patches'])
1831 patchespath = ui.expandpath(opts['patches'])
1828 else:
1832 else:
1829 patchespath = patchdir(sr)
1833 patchespath = patchdir(sr)
1830 try:
1834 try:
1831 hg.repository(ui, patchespath)
1835 hg.repository(ui, patchespath)
1832 except error.RepoError:
1836 except error.RepoError:
1833 raise util.Abort(_('versioned patch repository not found'
1837 raise util.Abort(_('versioned patch repository not found'
1834 ' (see qinit -c)'))
1838 ' (see qinit -c)'))
1835 qbase, destrev = None, None
1839 qbase, destrev = None, None
1836 if sr.local():
1840 if sr.local():
1837 if sr.mq.applied:
1841 if sr.mq.applied:
1838 qbase = bin(sr.mq.applied[0].rev)
1842 qbase = bin(sr.mq.applied[0].rev)
1839 if not hg.islocal(dest):
1843 if not hg.islocal(dest):
1840 heads = set(sr.heads())
1844 heads = set(sr.heads())
1841 destrev = list(heads.difference(sr.heads(qbase)))
1845 destrev = list(heads.difference(sr.heads(qbase)))
1842 destrev.append(sr.changelog.parents(qbase)[0])
1846 destrev.append(sr.changelog.parents(qbase)[0])
1843 elif sr.capable('lookup'):
1847 elif sr.capable('lookup'):
1844 try:
1848 try:
1845 qbase = sr.lookup('qbase')
1849 qbase = sr.lookup('qbase')
1846 except error.RepoError:
1850 except error.RepoError:
1847 pass
1851 pass
1848 ui.note(_('cloning main repository\n'))
1852 ui.note(_('cloning main repository\n'))
1849 sr, dr = hg.clone(ui, sr.url(), dest,
1853 sr, dr = hg.clone(ui, sr.url(), dest,
1850 pull=opts['pull'],
1854 pull=opts['pull'],
1851 rev=destrev,
1855 rev=destrev,
1852 update=False,
1856 update=False,
1853 stream=opts['uncompressed'])
1857 stream=opts['uncompressed'])
1854 ui.note(_('cloning patch repository\n'))
1858 ui.note(_('cloning patch repository\n'))
1855 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1859 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1856 pull=opts['pull'], update=not opts['noupdate'],
1860 pull=opts['pull'], update=not opts['noupdate'],
1857 stream=opts['uncompressed'])
1861 stream=opts['uncompressed'])
1858 if dr.local():
1862 if dr.local():
1859 if qbase:
1863 if qbase:
1860 ui.note(_('stripping applied patches from destination '
1864 ui.note(_('stripping applied patches from destination '
1861 'repository\n'))
1865 'repository\n'))
1862 dr.mq.strip(dr, qbase, update=False, backup=None)
1866 dr.mq.strip(dr, qbase, update=False, backup=None)
1863 if not opts['noupdate']:
1867 if not opts['noupdate']:
1864 ui.note(_('updating destination repository\n'))
1868 ui.note(_('updating destination repository\n'))
1865 hg.update(dr, dr.changelog.tip())
1869 hg.update(dr, dr.changelog.tip())
1866
1870
1867 def commit(ui, repo, *pats, **opts):
1871 def commit(ui, repo, *pats, **opts):
1868 """commit changes in the queue repository"""
1872 """commit changes in the queue repository"""
1869 q = repo.mq
1873 q = repo.mq
1870 r = q.qrepo()
1874 r = q.qrepo()
1871 if not r: raise util.Abort('no queue repository')
1875 if not r: raise util.Abort('no queue repository')
1872 commands.commit(r.ui, r, *pats, **opts)
1876 commands.commit(r.ui, r, *pats, **opts)
1873
1877
1874 def series(ui, repo, **opts):
1878 def series(ui, repo, **opts):
1875 """print the entire series file"""
1879 """print the entire series file"""
1876 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1880 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1877 return 0
1881 return 0
1878
1882
1879 def top(ui, repo, **opts):
1883 def top(ui, repo, **opts):
1880 """print the name of the current patch"""
1884 """print the name of the current patch"""
1881 q = repo.mq
1885 q = repo.mq
1882 t = q.applied and q.series_end(True) or 0
1886 t = q.applied and q.series_end(True) or 0
1883 if t:
1887 if t:
1884 return q.qseries(repo, start=t-1, length=1, status='A',
1888 return q.qseries(repo, start=t-1, length=1, status='A',
1885 summary=opts.get('summary'))
1889 summary=opts.get('summary'))
1886 else:
1890 else:
1887 ui.write(_("no patches applied\n"))
1891 ui.write(_("no patches applied\n"))
1888 return 1
1892 return 1
1889
1893
1890 def next(ui, repo, **opts):
1894 def next(ui, repo, **opts):
1891 """print the name of the next patch"""
1895 """print the name of the next patch"""
1892 q = repo.mq
1896 q = repo.mq
1893 end = q.series_end()
1897 end = q.series_end()
1894 if end == len(q.series):
1898 if end == len(q.series):
1895 ui.write(_("all patches applied\n"))
1899 ui.write(_("all patches applied\n"))
1896 return 1
1900 return 1
1897 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1901 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1898
1902
1899 def prev(ui, repo, **opts):
1903 def prev(ui, repo, **opts):
1900 """print the name of the previous patch"""
1904 """print the name of the previous patch"""
1901 q = repo.mq
1905 q = repo.mq
1902 l = len(q.applied)
1906 l = len(q.applied)
1903 if l == 1:
1907 if l == 1:
1904 ui.write(_("only one patch applied\n"))
1908 ui.write(_("only one patch applied\n"))
1905 return 1
1909 return 1
1906 if not l:
1910 if not l:
1907 ui.write(_("no patches applied\n"))
1911 ui.write(_("no patches applied\n"))
1908 return 1
1912 return 1
1909 return q.qseries(repo, start=l-2, length=1, status='A',
1913 return q.qseries(repo, start=l-2, length=1, status='A',
1910 summary=opts.get('summary'))
1914 summary=opts.get('summary'))
1911
1915
1912 def setupheaderopts(ui, opts):
1916 def setupheaderopts(ui, opts):
1913 def do(opt, val):
1917 def do(opt, val):
1914 if not opts[opt] and opts['current' + opt]:
1918 if not opts[opt] and opts['current' + opt]:
1915 opts[opt] = val
1919 opts[opt] = val
1916 do('user', ui.username())
1920 do('user', ui.username())
1917 do('date', "%d %d" % util.makedate())
1921 do('date', "%d %d" % util.makedate())
1918
1922
1919 def new(ui, repo, patch, *args, **opts):
1923 def new(ui, repo, patch, *args, **opts):
1920 """create a new patch
1924 """create a new patch
1921
1925
1922 qnew creates a new patch on top of the currently-applied patch (if
1926 qnew creates a new patch on top of the currently-applied patch (if
1923 any). It will refuse to run if there are any outstanding changes
1927 any). It will refuse to run if there are any outstanding changes
1924 unless -f/--force is specified, in which case the patch will be
1928 unless -f/--force is specified, in which case the patch will be
1925 initialized with them. You may also use -I/--include,
1929 initialized with them. You may also use -I/--include,
1926 -X/--exclude, and/or a list of files after the patch name to add
1930 -X/--exclude, and/or a list of files after the patch name to add
1927 only changes to matching files to the new patch, leaving the rest
1931 only changes to matching files to the new patch, leaving the rest
1928 as uncommitted modifications.
1932 as uncommitted modifications.
1929
1933
1930 -u/--user and -d/--date can be used to set the (given) user and
1934 -u/--user and -d/--date can be used to set the (given) user and
1931 date, respectively. -U/--currentuser and -D/--currentdate set user
1935 date, respectively. -U/--currentuser and -D/--currentdate set user
1932 to current user and date to current date.
1936 to current user and date to current date.
1933
1937
1934 -e/--edit, -m/--message or -l/--logfile set the patch header as
1938 -e/--edit, -m/--message or -l/--logfile set the patch header as
1935 well as the commit message. If none is specified, the header is
1939 well as the commit message. If none is specified, the header is
1936 empty and the commit message is '[mq]: PATCH'.
1940 empty and the commit message is '[mq]: PATCH'.
1937
1941
1938 Use the -g/--git option to keep the patch in the git extended diff
1942 Use the -g/--git option to keep the patch in the git extended diff
1939 format. Read the diffs help topic for more information on why this
1943 format. Read the diffs help topic for more information on why this
1940 is important for preserving permission changes and copy/rename
1944 is important for preserving permission changes and copy/rename
1941 information.
1945 information.
1942 """
1946 """
1943 msg = cmdutil.logmessage(opts)
1947 msg = cmdutil.logmessage(opts)
1944 def getmsg(): return ui.edit(msg, ui.username())
1948 def getmsg(): return ui.edit(msg, ui.username())
1945 q = repo.mq
1949 q = repo.mq
1946 opts['msg'] = msg
1950 opts['msg'] = msg
1947 if opts.get('edit'):
1951 if opts.get('edit'):
1948 opts['msg'] = getmsg
1952 opts['msg'] = getmsg
1949 else:
1953 else:
1950 opts['msg'] = msg
1954 opts['msg'] = msg
1951 setupheaderopts(ui, opts)
1955 setupheaderopts(ui, opts)
1952 q.new(repo, patch, *args, **opts)
1956 q.new(repo, patch, *args, **opts)
1953 q.save_dirty()
1957 q.save_dirty()
1954 return 0
1958 return 0
1955
1959
1956 def refresh(ui, repo, *pats, **opts):
1960 def refresh(ui, repo, *pats, **opts):
1957 """update the current patch
1961 """update the current patch
1958
1962
1959 If any file patterns are provided, the refreshed patch will
1963 If any file patterns are provided, the refreshed patch will
1960 contain only the modifications that match those patterns; the
1964 contain only the modifications that match those patterns; the
1961 remaining modifications will remain in the working directory.
1965 remaining modifications will remain in the working directory.
1962
1966
1963 If -s/--short is specified, files currently included in the patch
1967 If -s/--short is specified, files currently included in the patch
1964 will be refreshed just like matched files and remain in the patch.
1968 will be refreshed just like matched files and remain in the patch.
1965
1969
1966 hg add/remove/copy/rename work as usual, though you might want to
1970 hg add/remove/copy/rename work as usual, though you might want to
1967 use git-style patches (-g/--git or [diff] git=1) to track copies
1971 use git-style patches (-g/--git or [diff] git=1) to track copies
1968 and renames. See the diffs help topic for more information on the
1972 and renames. See the diffs help topic for more information on the
1969 git diff format.
1973 git diff format.
1970 """
1974 """
1971 q = repo.mq
1975 q = repo.mq
1972 message = cmdutil.logmessage(opts)
1976 message = cmdutil.logmessage(opts)
1973 if opts['edit']:
1977 if opts['edit']:
1974 if not q.applied:
1978 if not q.applied:
1975 ui.write(_("no patches applied\n"))
1979 ui.write(_("no patches applied\n"))
1976 return 1
1980 return 1
1977 if message:
1981 if message:
1978 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1982 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1979 patch = q.applied[-1].name
1983 patch = q.applied[-1].name
1980 ph = patchheader(q.join(patch))
1984 ph = patchheader(q.join(patch))
1981 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1985 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1982 setupheaderopts(ui, opts)
1986 setupheaderopts(ui, opts)
1983 ret = q.refresh(repo, pats, msg=message, **opts)
1987 ret = q.refresh(repo, pats, msg=message, **opts)
1984 q.save_dirty()
1988 q.save_dirty()
1985 return ret
1989 return ret
1986
1990
1987 def diff(ui, repo, *pats, **opts):
1991 def diff(ui, repo, *pats, **opts):
1988 """diff of the current patch and subsequent modifications
1992 """diff of the current patch and subsequent modifications
1989
1993
1990 Shows a diff which includes the current patch as well as any
1994 Shows a diff which includes the current patch as well as any
1991 changes which have been made in the working directory since the
1995 changes which have been made in the working directory since the
1992 last refresh (thus showing what the current patch would become
1996 last refresh (thus showing what the current patch would become
1993 after a qrefresh).
1997 after a qrefresh).
1994
1998
1995 Use 'hg diff' if you only want to see the changes made since the
1999 Use 'hg diff' if you only want to see the changes made since the
1996 last qrefresh, or 'hg export qtip' if you want to see changes made
2000 last qrefresh, or 'hg export qtip' if you want to see changes made
1997 by the current patch without including changes made since the
2001 by the current patch without including changes made since the
1998 qrefresh.
2002 qrefresh.
1999 """
2003 """
2000 repo.mq.diff(repo, pats, opts)
2004 repo.mq.diff(repo, pats, opts)
2001 return 0
2005 return 0
2002
2006
2003 def fold(ui, repo, *files, **opts):
2007 def fold(ui, repo, *files, **opts):
2004 """fold the named patches into the current patch
2008 """fold the named patches into the current patch
2005
2009
2006 Patches must not yet be applied. Each patch will be successively
2010 Patches must not yet be applied. Each patch will be successively
2007 applied to the current patch in the order given. If all the
2011 applied to the current patch in the order given. If all the
2008 patches apply successfully, the current patch will be refreshed
2012 patches apply successfully, the current patch will be refreshed
2009 with the new cumulative patch, and the folded patches will be
2013 with the new cumulative patch, and the folded patches will be
2010 deleted. With -k/--keep, the folded patch files will not be
2014 deleted. With -k/--keep, the folded patch files will not be
2011 removed afterwards.
2015 removed afterwards.
2012
2016
2013 The header for each folded patch will be concatenated with the
2017 The header for each folded patch will be concatenated with the
2014 current patch header, separated by a line of '* * *'."""
2018 current patch header, separated by a line of '* * *'."""
2015
2019
2016 q = repo.mq
2020 q = repo.mq
2017
2021
2018 if not files:
2022 if not files:
2019 raise util.Abort(_('qfold requires at least one patch name'))
2023 raise util.Abort(_('qfold requires at least one patch name'))
2020 if not q.check_toppatch(repo):
2024 if not q.check_toppatch(repo):
2021 raise util.Abort(_('No patches applied'))
2025 raise util.Abort(_('No patches applied'))
2022 q.check_localchanges(repo)
2026 q.check_localchanges(repo)
2023
2027
2024 message = cmdutil.logmessage(opts)
2028 message = cmdutil.logmessage(opts)
2025 if opts['edit']:
2029 if opts['edit']:
2026 if message:
2030 if message:
2027 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2031 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2028
2032
2029 parent = q.lookup('qtip')
2033 parent = q.lookup('qtip')
2030 patches = []
2034 patches = []
2031 messages = []
2035 messages = []
2032 for f in files:
2036 for f in files:
2033 p = q.lookup(f)
2037 p = q.lookup(f)
2034 if p in patches or p == parent:
2038 if p in patches or p == parent:
2035 ui.warn(_('Skipping already folded patch %s') % p)
2039 ui.warn(_('Skipping already folded patch %s') % p)
2036 if q.isapplied(p):
2040 if q.isapplied(p):
2037 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2041 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2038 patches.append(p)
2042 patches.append(p)
2039
2043
2040 for p in patches:
2044 for p in patches:
2041 if not message:
2045 if not message:
2042 ph = patchheader(q.join(p))
2046 ph = patchheader(q.join(p))
2043 if ph.message:
2047 if ph.message:
2044 messages.append(ph.message)
2048 messages.append(ph.message)
2045 pf = q.join(p)
2049 pf = q.join(p)
2046 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2050 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2047 if not patchsuccess:
2051 if not patchsuccess:
2048 raise util.Abort(_('Error folding patch %s') % p)
2052 raise util.Abort(_('Error folding patch %s') % p)
2049 patch.updatedir(ui, repo, files)
2053 patch.updatedir(ui, repo, files)
2050
2054
2051 if not message:
2055 if not message:
2052 ph = patchheader(q.join(parent))
2056 ph = patchheader(q.join(parent))
2053 message, user = ph.message, ph.user
2057 message, user = ph.message, ph.user
2054 for msg in messages:
2058 for msg in messages:
2055 message.append('* * *')
2059 message.append('* * *')
2056 message.extend(msg)
2060 message.extend(msg)
2057 message = '\n'.join(message)
2061 message = '\n'.join(message)
2058
2062
2059 if opts['edit']:
2063 if opts['edit']:
2060 message = ui.edit(message, user or ui.username())
2064 message = ui.edit(message, user or ui.username())
2061
2065
2062 q.refresh(repo, msg=message)
2066 q.refresh(repo, msg=message)
2063 q.delete(repo, patches, opts)
2067 q.delete(repo, patches, opts)
2064 q.save_dirty()
2068 q.save_dirty()
2065
2069
2066 def goto(ui, repo, patch, **opts):
2070 def goto(ui, repo, patch, **opts):
2067 '''push or pop patches until named patch is at top of stack'''
2071 '''push or pop patches until named patch is at top of stack'''
2068 q = repo.mq
2072 q = repo.mq
2069 patch = q.lookup(patch)
2073 patch = q.lookup(patch)
2070 if q.isapplied(patch):
2074 if q.isapplied(patch):
2071 ret = q.pop(repo, patch, force=opts['force'])
2075 ret = q.pop(repo, patch, force=opts['force'])
2072 else:
2076 else:
2073 ret = q.push(repo, patch, force=opts['force'])
2077 ret = q.push(repo, patch, force=opts['force'])
2074 q.save_dirty()
2078 q.save_dirty()
2075 return ret
2079 return ret
2076
2080
2077 def guard(ui, repo, *args, **opts):
2081 def guard(ui, repo, *args, **opts):
2078 '''set or print guards for a patch
2082 '''set or print guards for a patch
2079
2083
2080 Guards control whether a patch can be pushed. A patch with no
2084 Guards control whether a patch can be pushed. A patch with no
2081 guards is always pushed. A patch with a positive guard ("+foo") is
2085 guards is always pushed. A patch with a positive guard ("+foo") is
2082 pushed only if the qselect command has activated it. A patch with
2086 pushed only if the qselect command has activated it. A patch with
2083 a negative guard ("-foo") is never pushed if the qselect command
2087 a negative guard ("-foo") is never pushed if the qselect command
2084 has activated it.
2088 has activated it.
2085
2089
2086 With no arguments, print the currently active guards.
2090 With no arguments, print the currently active guards.
2087 With arguments, set guards for the named patch.
2091 With arguments, set guards for the named patch.
2088 NOTE: Specifying negative guards now requires '--'.
2092 NOTE: Specifying negative guards now requires '--'.
2089
2093
2090 To set guards on another patch:
2094 To set guards on another patch:
2091 hg qguard -- other.patch +2.6.17 -stable
2095 hg qguard -- other.patch +2.6.17 -stable
2092 '''
2096 '''
2093 def status(idx):
2097 def status(idx):
2094 guards = q.series_guards[idx] or ['unguarded']
2098 guards = q.series_guards[idx] or ['unguarded']
2095 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2099 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2096 q = repo.mq
2100 q = repo.mq
2097 patch = None
2101 patch = None
2098 args = list(args)
2102 args = list(args)
2099 if opts['list']:
2103 if opts['list']:
2100 if args or opts['none']:
2104 if args or opts['none']:
2101 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2105 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2102 for i in xrange(len(q.series)):
2106 for i in xrange(len(q.series)):
2103 status(i)
2107 status(i)
2104 return
2108 return
2105 if not args or args[0][0:1] in '-+':
2109 if not args or args[0][0:1] in '-+':
2106 if not q.applied:
2110 if not q.applied:
2107 raise util.Abort(_('no patches applied'))
2111 raise util.Abort(_('no patches applied'))
2108 patch = q.applied[-1].name
2112 patch = q.applied[-1].name
2109 if patch is None and args[0][0:1] not in '-+':
2113 if patch is None and args[0][0:1] not in '-+':
2110 patch = args.pop(0)
2114 patch = args.pop(0)
2111 if patch is None:
2115 if patch is None:
2112 raise util.Abort(_('no patch to work with'))
2116 raise util.Abort(_('no patch to work with'))
2113 if args or opts['none']:
2117 if args or opts['none']:
2114 idx = q.find_series(patch)
2118 idx = q.find_series(patch)
2115 if idx is None:
2119 if idx is None:
2116 raise util.Abort(_('no patch named %s') % patch)
2120 raise util.Abort(_('no patch named %s') % patch)
2117 q.set_guards(idx, args)
2121 q.set_guards(idx, args)
2118 q.save_dirty()
2122 q.save_dirty()
2119 else:
2123 else:
2120 status(q.series.index(q.lookup(patch)))
2124 status(q.series.index(q.lookup(patch)))
2121
2125
2122 def header(ui, repo, patch=None):
2126 def header(ui, repo, patch=None):
2123 """print the header of the topmost or specified patch"""
2127 """print the header of the topmost or specified patch"""
2124 q = repo.mq
2128 q = repo.mq
2125
2129
2126 if patch:
2130 if patch:
2127 patch = q.lookup(patch)
2131 patch = q.lookup(patch)
2128 else:
2132 else:
2129 if not q.applied:
2133 if not q.applied:
2130 ui.write('no patches applied\n')
2134 ui.write('no patches applied\n')
2131 return 1
2135 return 1
2132 patch = q.lookup('qtip')
2136 patch = q.lookup('qtip')
2133 ph = patchheader(repo.mq.join(patch))
2137 ph = patchheader(repo.mq.join(patch))
2134
2138
2135 ui.write('\n'.join(ph.message) + '\n')
2139 ui.write('\n'.join(ph.message) + '\n')
2136
2140
2137 def lastsavename(path):
2141 def lastsavename(path):
2138 (directory, base) = os.path.split(path)
2142 (directory, base) = os.path.split(path)
2139 names = os.listdir(directory)
2143 names = os.listdir(directory)
2140 namere = re.compile("%s.([0-9]+)" % base)
2144 namere = re.compile("%s.([0-9]+)" % base)
2141 maxindex = None
2145 maxindex = None
2142 maxname = None
2146 maxname = None
2143 for f in names:
2147 for f in names:
2144 m = namere.match(f)
2148 m = namere.match(f)
2145 if m:
2149 if m:
2146 index = int(m.group(1))
2150 index = int(m.group(1))
2147 if maxindex is None or index > maxindex:
2151 if maxindex is None or index > maxindex:
2148 maxindex = index
2152 maxindex = index
2149 maxname = f
2153 maxname = f
2150 if maxname:
2154 if maxname:
2151 return (os.path.join(directory, maxname), maxindex)
2155 return (os.path.join(directory, maxname), maxindex)
2152 return (None, None)
2156 return (None, None)
2153
2157
2154 def savename(path):
2158 def savename(path):
2155 (last, index) = lastsavename(path)
2159 (last, index) = lastsavename(path)
2156 if last is None:
2160 if last is None:
2157 index = 0
2161 index = 0
2158 newpath = path + ".%d" % (index + 1)
2162 newpath = path + ".%d" % (index + 1)
2159 return newpath
2163 return newpath
2160
2164
2161 def push(ui, repo, patch=None, **opts):
2165 def push(ui, repo, patch=None, **opts):
2162 """push the next patch onto the stack
2166 """push the next patch onto the stack
2163
2167
2164 When -f/--force is applied, all local changes in patched files
2168 When -f/--force is applied, all local changes in patched files
2165 will be lost.
2169 will be lost.
2166 """
2170 """
2167 q = repo.mq
2171 q = repo.mq
2168 mergeq = None
2172 mergeq = None
2169
2173
2170 if opts['merge']:
2174 if opts['merge']:
2171 if opts['name']:
2175 if opts['name']:
2172 newpath = repo.join(opts['name'])
2176 newpath = repo.join(opts['name'])
2173 else:
2177 else:
2174 newpath, i = lastsavename(q.path)
2178 newpath, i = lastsavename(q.path)
2175 if not newpath:
2179 if not newpath:
2176 ui.warn(_("no saved queues found, please use -n\n"))
2180 ui.warn(_("no saved queues found, please use -n\n"))
2177 return 1
2181 return 1
2178 mergeq = queue(ui, repo.join(""), newpath)
2182 mergeq = queue(ui, repo.join(""), newpath)
2179 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2183 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2180 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2184 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2181 mergeq=mergeq, all=opts.get('all'))
2185 mergeq=mergeq, all=opts.get('all'))
2182 return ret
2186 return ret
2183
2187
2184 def pop(ui, repo, patch=None, **opts):
2188 def pop(ui, repo, patch=None, **opts):
2185 """pop the current patch off the stack
2189 """pop the current patch off the stack
2186
2190
2187 By default, pops off the top of the patch stack. If given a patch
2191 By default, pops off the top of the patch stack. If given a patch
2188 name, keeps popping off patches until the named patch is at the
2192 name, keeps popping off patches until the named patch is at the
2189 top of the stack.
2193 top of the stack.
2190 """
2194 """
2191 localupdate = True
2195 localupdate = True
2192 if opts['name']:
2196 if opts['name']:
2193 q = queue(ui, repo.join(""), repo.join(opts['name']))
2197 q = queue(ui, repo.join(""), repo.join(opts['name']))
2194 ui.warn(_('using patch queue: %s\n') % q.path)
2198 ui.warn(_('using patch queue: %s\n') % q.path)
2195 localupdate = False
2199 localupdate = False
2196 else:
2200 else:
2197 q = repo.mq
2201 q = repo.mq
2198 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2202 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2199 all=opts['all'])
2203 all=opts['all'])
2200 q.save_dirty()
2204 q.save_dirty()
2201 return ret
2205 return ret
2202
2206
2203 def rename(ui, repo, patch, name=None, **opts):
2207 def rename(ui, repo, patch, name=None, **opts):
2204 """rename a patch
2208 """rename a patch
2205
2209
2206 With one argument, renames the current patch to PATCH1.
2210 With one argument, renames the current patch to PATCH1.
2207 With two arguments, renames PATCH1 to PATCH2."""
2211 With two arguments, renames PATCH1 to PATCH2."""
2208
2212
2209 q = repo.mq
2213 q = repo.mq
2210
2214
2211 if not name:
2215 if not name:
2212 name = patch
2216 name = patch
2213 patch = None
2217 patch = None
2214
2218
2215 if patch:
2219 if patch:
2216 patch = q.lookup(patch)
2220 patch = q.lookup(patch)
2217 else:
2221 else:
2218 if not q.applied:
2222 if not q.applied:
2219 ui.write(_('no patches applied\n'))
2223 ui.write(_('no patches applied\n'))
2220 return
2224 return
2221 patch = q.lookup('qtip')
2225 patch = q.lookup('qtip')
2222 absdest = q.join(name)
2226 absdest = q.join(name)
2223 if os.path.isdir(absdest):
2227 if os.path.isdir(absdest):
2224 name = normname(os.path.join(name, os.path.basename(patch)))
2228 name = normname(os.path.join(name, os.path.basename(patch)))
2225 absdest = q.join(name)
2229 absdest = q.join(name)
2226 if os.path.exists(absdest):
2230 if os.path.exists(absdest):
2227 raise util.Abort(_('%s already exists') % absdest)
2231 raise util.Abort(_('%s already exists') % absdest)
2228
2232
2229 if name in q.series:
2233 if name in q.series:
2230 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2234 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2231
2235
2232 if ui.verbose:
2236 if ui.verbose:
2233 ui.write('renaming %s to %s\n' % (patch, name))
2237 ui.write('renaming %s to %s\n' % (patch, name))
2234 i = q.find_series(patch)
2238 i = q.find_series(patch)
2235 guards = q.guard_re.findall(q.full_series[i])
2239 guards = q.guard_re.findall(q.full_series[i])
2236 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2240 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2237 q.parse_series()
2241 q.parse_series()
2238 q.series_dirty = 1
2242 q.series_dirty = 1
2239
2243
2240 info = q.isapplied(patch)
2244 info = q.isapplied(patch)
2241 if info:
2245 if info:
2242 q.applied[info[0]] = statusentry(info[1], name)
2246 q.applied[info[0]] = statusentry(info[1], name)
2243 q.applied_dirty = 1
2247 q.applied_dirty = 1
2244
2248
2245 util.rename(q.join(patch), absdest)
2249 util.rename(q.join(patch), absdest)
2246 r = q.qrepo()
2250 r = q.qrepo()
2247 if r:
2251 if r:
2248 wlock = r.wlock()
2252 wlock = r.wlock()
2249 try:
2253 try:
2250 if r.dirstate[patch] == 'a':
2254 if r.dirstate[patch] == 'a':
2251 r.dirstate.forget(patch)
2255 r.dirstate.forget(patch)
2252 r.dirstate.add(name)
2256 r.dirstate.add(name)
2253 else:
2257 else:
2254 if r.dirstate[name] == 'r':
2258 if r.dirstate[name] == 'r':
2255 r.undelete([name])
2259 r.undelete([name])
2256 r.copy(patch, name)
2260 r.copy(patch, name)
2257 r.remove([patch], False)
2261 r.remove([patch], False)
2258 finally:
2262 finally:
2259 wlock.release()
2263 wlock.release()
2260
2264
2261 q.save_dirty()
2265 q.save_dirty()
2262
2266
2263 def restore(ui, repo, rev, **opts):
2267 def restore(ui, repo, rev, **opts):
2264 """restore the queue state saved by a revision"""
2268 """restore the queue state saved by a revision"""
2265 rev = repo.lookup(rev)
2269 rev = repo.lookup(rev)
2266 q = repo.mq
2270 q = repo.mq
2267 q.restore(repo, rev, delete=opts['delete'],
2271 q.restore(repo, rev, delete=opts['delete'],
2268 qupdate=opts['update'])
2272 qupdate=opts['update'])
2269 q.save_dirty()
2273 q.save_dirty()
2270 return 0
2274 return 0
2271
2275
2272 def save(ui, repo, **opts):
2276 def save(ui, repo, **opts):
2273 """save current queue state"""
2277 """save current queue state"""
2274 q = repo.mq
2278 q = repo.mq
2275 message = cmdutil.logmessage(opts)
2279 message = cmdutil.logmessage(opts)
2276 ret = q.save(repo, msg=message)
2280 ret = q.save(repo, msg=message)
2277 if ret:
2281 if ret:
2278 return ret
2282 return ret
2279 q.save_dirty()
2283 q.save_dirty()
2280 if opts['copy']:
2284 if opts['copy']:
2281 path = q.path
2285 path = q.path
2282 if opts['name']:
2286 if opts['name']:
2283 newpath = os.path.join(q.basepath, opts['name'])
2287 newpath = os.path.join(q.basepath, opts['name'])
2284 if os.path.exists(newpath):
2288 if os.path.exists(newpath):
2285 if not os.path.isdir(newpath):
2289 if not os.path.isdir(newpath):
2286 raise util.Abort(_('destination %s exists and is not '
2290 raise util.Abort(_('destination %s exists and is not '
2287 'a directory') % newpath)
2291 'a directory') % newpath)
2288 if not opts['force']:
2292 if not opts['force']:
2289 raise util.Abort(_('destination %s exists, '
2293 raise util.Abort(_('destination %s exists, '
2290 'use -f to force') % newpath)
2294 'use -f to force') % newpath)
2291 else:
2295 else:
2292 newpath = savename(path)
2296 newpath = savename(path)
2293 ui.warn(_("copy %s to %s\n") % (path, newpath))
2297 ui.warn(_("copy %s to %s\n") % (path, newpath))
2294 util.copyfiles(path, newpath)
2298 util.copyfiles(path, newpath)
2295 if opts['empty']:
2299 if opts['empty']:
2296 try:
2300 try:
2297 os.unlink(q.join(q.status_path))
2301 os.unlink(q.join(q.status_path))
2298 except:
2302 except:
2299 pass
2303 pass
2300 return 0
2304 return 0
2301
2305
2302 def strip(ui, repo, rev, **opts):
2306 def strip(ui, repo, rev, **opts):
2303 """strip a revision and all its descendants from the repository
2307 """strip a revision and all its descendants from the repository
2304
2308
2305 If one of the working directory's parent revisions is stripped, the
2309 If one of the working directory's parent revisions is stripped, the
2306 working directory will be updated to the parent of the stripped
2310 working directory will be updated to the parent of the stripped
2307 revision.
2311 revision.
2308 """
2312 """
2309 backup = 'all'
2313 backup = 'all'
2310 if opts['backup']:
2314 if opts['backup']:
2311 backup = 'strip'
2315 backup = 'strip'
2312 elif opts['nobackup']:
2316 elif opts['nobackup']:
2313 backup = 'none'
2317 backup = 'none'
2314
2318
2315 rev = repo.lookup(rev)
2319 rev = repo.lookup(rev)
2316 p = repo.dirstate.parents()
2320 p = repo.dirstate.parents()
2317 cl = repo.changelog
2321 cl = repo.changelog
2318 update = True
2322 update = True
2319 if p[0] == nullid:
2323 if p[0] == nullid:
2320 update = False
2324 update = False
2321 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2325 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2322 update = False
2326 update = False
2323 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2327 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2324 update = False
2328 update = False
2325
2329
2326 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2330 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2327 return 0
2331 return 0
2328
2332
2329 def select(ui, repo, *args, **opts):
2333 def select(ui, repo, *args, **opts):
2330 '''set or print guarded patches to push
2334 '''set or print guarded patches to push
2331
2335
2332 Use the qguard command to set or print guards on patch, then use
2336 Use the qguard command to set or print guards on patch, then use
2333 qselect to tell mq which guards to use. A patch will be pushed if
2337 qselect to tell mq which guards to use. A patch will be pushed if
2334 it has no guards or any positive guards match the currently
2338 it has no guards or any positive guards match the currently
2335 selected guard, but will not be pushed if any negative guards
2339 selected guard, but will not be pushed if any negative guards
2336 match the current guard. For example:
2340 match the current guard. For example:
2337
2341
2338 qguard foo.patch -stable (negative guard)
2342 qguard foo.patch -stable (negative guard)
2339 qguard bar.patch +stable (positive guard)
2343 qguard bar.patch +stable (positive guard)
2340 qselect stable
2344 qselect stable
2341
2345
2342 This activates the "stable" guard. mq will skip foo.patch (because
2346 This activates the "stable" guard. mq will skip foo.patch (because
2343 it has a negative match) but push bar.patch (because it has a
2347 it has a negative match) but push bar.patch (because it has a
2344 positive match).
2348 positive match).
2345
2349
2346 With no arguments, prints the currently active guards.
2350 With no arguments, prints the currently active guards.
2347 With one argument, sets the active guard.
2351 With one argument, sets the active guard.
2348
2352
2349 Use -n/--none to deactivate guards (no other arguments needed).
2353 Use -n/--none to deactivate guards (no other arguments needed).
2350 When no guards are active, patches with positive guards are
2354 When no guards are active, patches with positive guards are
2351 skipped and patches with negative guards are pushed.
2355 skipped and patches with negative guards are pushed.
2352
2356
2353 qselect can change the guards on applied patches. It does not pop
2357 qselect can change the guards on applied patches. It does not pop
2354 guarded patches by default. Use --pop to pop back to the last
2358 guarded patches by default. Use --pop to pop back to the last
2355 applied patch that is not guarded. Use --reapply (which implies
2359 applied patch that is not guarded. Use --reapply (which implies
2356 --pop) to push back to the current patch afterwards, but skip
2360 --pop) to push back to the current patch afterwards, but skip
2357 guarded patches.
2361 guarded patches.
2358
2362
2359 Use -s/--series to print a list of all guards in the series file
2363 Use -s/--series to print a list of all guards in the series file
2360 (no other arguments needed). Use -v for more information.'''
2364 (no other arguments needed). Use -v for more information.'''
2361
2365
2362 q = repo.mq
2366 q = repo.mq
2363 guards = q.active()
2367 guards = q.active()
2364 if args or opts['none']:
2368 if args or opts['none']:
2365 old_unapplied = q.unapplied(repo)
2369 old_unapplied = q.unapplied(repo)
2366 old_guarded = [i for i in xrange(len(q.applied)) if
2370 old_guarded = [i for i in xrange(len(q.applied)) if
2367 not q.pushable(i)[0]]
2371 not q.pushable(i)[0]]
2368 q.set_active(args)
2372 q.set_active(args)
2369 q.save_dirty()
2373 q.save_dirty()
2370 if not args:
2374 if not args:
2371 ui.status(_('guards deactivated\n'))
2375 ui.status(_('guards deactivated\n'))
2372 if not opts['pop'] and not opts['reapply']:
2376 if not opts['pop'] and not opts['reapply']:
2373 unapplied = q.unapplied(repo)
2377 unapplied = q.unapplied(repo)
2374 guarded = [i for i in xrange(len(q.applied))
2378 guarded = [i for i in xrange(len(q.applied))
2375 if not q.pushable(i)[0]]
2379 if not q.pushable(i)[0]]
2376 if len(unapplied) != len(old_unapplied):
2380 if len(unapplied) != len(old_unapplied):
2377 ui.status(_('number of unguarded, unapplied patches has '
2381 ui.status(_('number of unguarded, unapplied patches has '
2378 'changed from %d to %d\n') %
2382 'changed from %d to %d\n') %
2379 (len(old_unapplied), len(unapplied)))
2383 (len(old_unapplied), len(unapplied)))
2380 if len(guarded) != len(old_guarded):
2384 if len(guarded) != len(old_guarded):
2381 ui.status(_('number of guarded, applied patches has changed '
2385 ui.status(_('number of guarded, applied patches has changed '
2382 'from %d to %d\n') %
2386 'from %d to %d\n') %
2383 (len(old_guarded), len(guarded)))
2387 (len(old_guarded), len(guarded)))
2384 elif opts['series']:
2388 elif opts['series']:
2385 guards = {}
2389 guards = {}
2386 noguards = 0
2390 noguards = 0
2387 for gs in q.series_guards:
2391 for gs in q.series_guards:
2388 if not gs:
2392 if not gs:
2389 noguards += 1
2393 noguards += 1
2390 for g in gs:
2394 for g in gs:
2391 guards.setdefault(g, 0)
2395 guards.setdefault(g, 0)
2392 guards[g] += 1
2396 guards[g] += 1
2393 if ui.verbose:
2397 if ui.verbose:
2394 guards['NONE'] = noguards
2398 guards['NONE'] = noguards
2395 guards = guards.items()
2399 guards = guards.items()
2396 guards.sort(key=lambda x: x[0][1:])
2400 guards.sort(key=lambda x: x[0][1:])
2397 if guards:
2401 if guards:
2398 ui.note(_('guards in series file:\n'))
2402 ui.note(_('guards in series file:\n'))
2399 for guard, count in guards:
2403 for guard, count in guards:
2400 ui.note('%2d ' % count)
2404 ui.note('%2d ' % count)
2401 ui.write(guard, '\n')
2405 ui.write(guard, '\n')
2402 else:
2406 else:
2403 ui.note(_('no guards in series file\n'))
2407 ui.note(_('no guards in series file\n'))
2404 else:
2408 else:
2405 if guards:
2409 if guards:
2406 ui.note(_('active guards:\n'))
2410 ui.note(_('active guards:\n'))
2407 for g in guards:
2411 for g in guards:
2408 ui.write(g, '\n')
2412 ui.write(g, '\n')
2409 else:
2413 else:
2410 ui.write(_('no active guards\n'))
2414 ui.write(_('no active guards\n'))
2411 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2415 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2412 popped = False
2416 popped = False
2413 if opts['pop'] or opts['reapply']:
2417 if opts['pop'] or opts['reapply']:
2414 for i in xrange(len(q.applied)):
2418 for i in xrange(len(q.applied)):
2415 pushable, reason = q.pushable(i)
2419 pushable, reason = q.pushable(i)
2416 if not pushable:
2420 if not pushable:
2417 ui.status(_('popping guarded patches\n'))
2421 ui.status(_('popping guarded patches\n'))
2418 popped = True
2422 popped = True
2419 if i == 0:
2423 if i == 0:
2420 q.pop(repo, all=True)
2424 q.pop(repo, all=True)
2421 else:
2425 else:
2422 q.pop(repo, i-1)
2426 q.pop(repo, i-1)
2423 break
2427 break
2424 if popped:
2428 if popped:
2425 try:
2429 try:
2426 if reapply:
2430 if reapply:
2427 ui.status(_('reapplying unguarded patches\n'))
2431 ui.status(_('reapplying unguarded patches\n'))
2428 q.push(repo, reapply)
2432 q.push(repo, reapply)
2429 finally:
2433 finally:
2430 q.save_dirty()
2434 q.save_dirty()
2431
2435
2432 def finish(ui, repo, *revrange, **opts):
2436 def finish(ui, repo, *revrange, **opts):
2433 """move applied patches into repository history
2437 """move applied patches into repository history
2434
2438
2435 Finishes the specified revisions (corresponding to applied
2439 Finishes the specified revisions (corresponding to applied
2436 patches) by moving them out of mq control into regular repository
2440 patches) by moving them out of mq control into regular repository
2437 history.
2441 history.
2438
2442
2439 Accepts a revision range or the -a/--applied option. If --applied
2443 Accepts a revision range or the -a/--applied option. If --applied
2440 is specified, all applied mq revisions are removed from mq
2444 is specified, all applied mq revisions are removed from mq
2441 control. Otherwise, the given revisions must be at the base of the
2445 control. Otherwise, the given revisions must be at the base of the
2442 stack of applied patches.
2446 stack of applied patches.
2443
2447
2444 This can be especially useful if your changes have been applied to
2448 This can be especially useful if your changes have been applied to
2445 an upstream repository, or if you are about to push your changes
2449 an upstream repository, or if you are about to push your changes
2446 to upstream.
2450 to upstream.
2447 """
2451 """
2448 if not opts['applied'] and not revrange:
2452 if not opts['applied'] and not revrange:
2449 raise util.Abort(_('no revisions specified'))
2453 raise util.Abort(_('no revisions specified'))
2450 elif opts['applied']:
2454 elif opts['applied']:
2451 revrange = ('qbase:qtip',) + revrange
2455 revrange = ('qbase:qtip',) + revrange
2452
2456
2453 q = repo.mq
2457 q = repo.mq
2454 if not q.applied:
2458 if not q.applied:
2455 ui.status(_('no patches applied\n'))
2459 ui.status(_('no patches applied\n'))
2456 return 0
2460 return 0
2457
2461
2458 revs = cmdutil.revrange(repo, revrange)
2462 revs = cmdutil.revrange(repo, revrange)
2459 q.finish(repo, revs)
2463 q.finish(repo, revs)
2460 q.save_dirty()
2464 q.save_dirty()
2461 return 0
2465 return 0
2462
2466
2463 def reposetup(ui, repo):
2467 def reposetup(ui, repo):
2464 class mqrepo(repo.__class__):
2468 class mqrepo(repo.__class__):
2465 @util.propertycache
2469 @util.propertycache
2466 def mq(self):
2470 def mq(self):
2467 return queue(self.ui, self.join(""))
2471 return queue(self.ui, self.join(""))
2468
2472
2469 def abort_if_wdir_patched(self, errmsg, force=False):
2473 def abort_if_wdir_patched(self, errmsg, force=False):
2470 if self.mq.applied and not force:
2474 if self.mq.applied and not force:
2471 parent = hex(self.dirstate.parents()[0])
2475 parent = hex(self.dirstate.parents()[0])
2472 if parent in [s.rev for s in self.mq.applied]:
2476 if parent in [s.rev for s in self.mq.applied]:
2473 raise util.Abort(errmsg)
2477 raise util.Abort(errmsg)
2474
2478
2475 def commit(self, text="", user=None, date=None, match=None,
2479 def commit(self, text="", user=None, date=None, match=None,
2476 force=False, editor=False, extra={}):
2480 force=False, editor=False, extra={}):
2477 self.abort_if_wdir_patched(
2481 self.abort_if_wdir_patched(
2478 _('cannot commit over an applied mq patch'),
2482 _('cannot commit over an applied mq patch'),
2479 force)
2483 force)
2480
2484
2481 return super(mqrepo, self).commit(text, user, date, match, force,
2485 return super(mqrepo, self).commit(text, user, date, match, force,
2482 editor, extra)
2486 editor, extra)
2483
2487
2484 def push(self, remote, force=False, revs=None):
2488 def push(self, remote, force=False, revs=None):
2485 if self.mq.applied and not force and not revs:
2489 if self.mq.applied and not force and not revs:
2486 raise util.Abort(_('source has mq patches applied'))
2490 raise util.Abort(_('source has mq patches applied'))
2487 return super(mqrepo, self).push(remote, force, revs)
2491 return super(mqrepo, self).push(remote, force, revs)
2488
2492
2489 def _findtags(self):
2493 def _findtags(self):
2490 '''augment tags from base class with patch tags'''
2494 '''augment tags from base class with patch tags'''
2491 result = super(mqrepo, self)._findtags()
2495 result = super(mqrepo, self)._findtags()
2492
2496
2493 q = self.mq
2497 q = self.mq
2494 if not q.applied:
2498 if not q.applied:
2495 return result
2499 return result
2496
2500
2497 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2501 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2498
2502
2499 if mqtags[-1][0] not in self.changelog.nodemap:
2503 if mqtags[-1][0] not in self.changelog.nodemap:
2500 self.ui.warn(_('mq status file refers to unknown node %s\n')
2504 self.ui.warn(_('mq status file refers to unknown node %s\n')
2501 % short(mqtags[-1][0]))
2505 % short(mqtags[-1][0]))
2502 return result
2506 return result
2503
2507
2504 mqtags.append((mqtags[-1][0], 'qtip'))
2508 mqtags.append((mqtags[-1][0], 'qtip'))
2505 mqtags.append((mqtags[0][0], 'qbase'))
2509 mqtags.append((mqtags[0][0], 'qbase'))
2506 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2510 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2507 tags = result[0]
2511 tags = result[0]
2508 for patch in mqtags:
2512 for patch in mqtags:
2509 if patch[1] in tags:
2513 if patch[1] in tags:
2510 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2514 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2511 % patch[1])
2515 % patch[1])
2512 else:
2516 else:
2513 tags[patch[1]] = patch[0]
2517 tags[patch[1]] = patch[0]
2514
2518
2515 return result
2519 return result
2516
2520
2517 def _branchtags(self, partial, lrev):
2521 def _branchtags(self, partial, lrev):
2518 q = self.mq
2522 q = self.mq
2519 if not q.applied:
2523 if not q.applied:
2520 return super(mqrepo, self)._branchtags(partial, lrev)
2524 return super(mqrepo, self)._branchtags(partial, lrev)
2521
2525
2522 cl = self.changelog
2526 cl = self.changelog
2523 qbasenode = bin(q.applied[0].rev)
2527 qbasenode = bin(q.applied[0].rev)
2524 if qbasenode not in cl.nodemap:
2528 if qbasenode not in cl.nodemap:
2525 self.ui.warn(_('mq status file refers to unknown node %s\n')
2529 self.ui.warn(_('mq status file refers to unknown node %s\n')
2526 % short(qbasenode))
2530 % short(qbasenode))
2527 return super(mqrepo, self)._branchtags(partial, lrev)
2531 return super(mqrepo, self)._branchtags(partial, lrev)
2528
2532
2529 qbase = cl.rev(qbasenode)
2533 qbase = cl.rev(qbasenode)
2530 start = lrev + 1
2534 start = lrev + 1
2531 if start < qbase:
2535 if start < qbase:
2532 # update the cache (excluding the patches) and save it
2536 # update the cache (excluding the patches) and save it
2533 self._updatebranchcache(partial, lrev+1, qbase)
2537 self._updatebranchcache(partial, lrev+1, qbase)
2534 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2538 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2535 start = qbase
2539 start = qbase
2536 # if start = qbase, the cache is as updated as it should be.
2540 # if start = qbase, the cache is as updated as it should be.
2537 # if start > qbase, the cache includes (part of) the patches.
2541 # if start > qbase, the cache includes (part of) the patches.
2538 # we might as well use it, but we won't save it.
2542 # we might as well use it, but we won't save it.
2539
2543
2540 # update the cache up to the tip
2544 # update the cache up to the tip
2541 self._updatebranchcache(partial, start, len(cl))
2545 self._updatebranchcache(partial, start, len(cl))
2542
2546
2543 return partial
2547 return partial
2544
2548
2545 if repo.local():
2549 if repo.local():
2546 repo.__class__ = mqrepo
2550 repo.__class__ = mqrepo
2547
2551
2548 def mqimport(orig, ui, repo, *args, **kwargs):
2552 def mqimport(orig, ui, repo, *args, **kwargs):
2549 if hasattr(repo, 'abort_if_wdir_patched'):
2553 if hasattr(repo, 'abort_if_wdir_patched'):
2550 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2554 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2551 kwargs.get('force'))
2555 kwargs.get('force'))
2552 return orig(ui, repo, *args, **kwargs)
2556 return orig(ui, repo, *args, **kwargs)
2553
2557
2554 def uisetup(ui):
2558 def uisetup(ui):
2555 extensions.wrapcommand(commands.table, 'import', mqimport)
2559 extensions.wrapcommand(commands.table, 'import', mqimport)
2556
2560
2557 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2561 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2558
2562
2559 cmdtable = {
2563 cmdtable = {
2560 "qapplied":
2564 "qapplied":
2561 (applied,
2565 (applied,
2562 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2566 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2563 _('hg qapplied [-1] [-s] [PATCH]')),
2567 _('hg qapplied [-1] [-s] [PATCH]')),
2564 "qclone":
2568 "qclone":
2565 (clone,
2569 (clone,
2566 [('', 'pull', None, _('use pull protocol to copy metadata')),
2570 [('', 'pull', None, _('use pull protocol to copy metadata')),
2567 ('U', 'noupdate', None, _('do not update the new working directories')),
2571 ('U', 'noupdate', None, _('do not update the new working directories')),
2568 ('', 'uncompressed', None,
2572 ('', 'uncompressed', None,
2569 _('use uncompressed transfer (fast over LAN)')),
2573 _('use uncompressed transfer (fast over LAN)')),
2570 ('p', 'patches', '', _('location of source patch repository')),
2574 ('p', 'patches', '', _('location of source patch repository')),
2571 ] + commands.remoteopts,
2575 ] + commands.remoteopts,
2572 _('hg qclone [OPTION]... SOURCE [DEST]')),
2576 _('hg qclone [OPTION]... SOURCE [DEST]')),
2573 "qcommit|qci":
2577 "qcommit|qci":
2574 (commit,
2578 (commit,
2575 commands.table["^commit|ci"][1],
2579 commands.table["^commit|ci"][1],
2576 _('hg qcommit [OPTION]... [FILE]...')),
2580 _('hg qcommit [OPTION]... [FILE]...')),
2577 "^qdiff":
2581 "^qdiff":
2578 (diff,
2582 (diff,
2579 commands.diffopts + commands.diffopts2 + commands.walkopts,
2583 commands.diffopts + commands.diffopts2 + commands.walkopts,
2580 _('hg qdiff [OPTION]... [FILE]...')),
2584 _('hg qdiff [OPTION]... [FILE]...')),
2581 "qdelete|qremove|qrm":
2585 "qdelete|qremove|qrm":
2582 (delete,
2586 (delete,
2583 [('k', 'keep', None, _('keep patch file')),
2587 [('k', 'keep', None, _('keep patch file')),
2584 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2588 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2585 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2589 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2586 'qfold':
2590 'qfold':
2587 (fold,
2591 (fold,
2588 [('e', 'edit', None, _('edit patch header')),
2592 [('e', 'edit', None, _('edit patch header')),
2589 ('k', 'keep', None, _('keep folded patch files')),
2593 ('k', 'keep', None, _('keep folded patch files')),
2590 ] + commands.commitopts,
2594 ] + commands.commitopts,
2591 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2595 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2592 'qgoto':
2596 'qgoto':
2593 (goto,
2597 (goto,
2594 [('f', 'force', None, _('overwrite any local changes'))],
2598 [('f', 'force', None, _('overwrite any local changes'))],
2595 _('hg qgoto [OPTION]... PATCH')),
2599 _('hg qgoto [OPTION]... PATCH')),
2596 'qguard':
2600 'qguard':
2597 (guard,
2601 (guard,
2598 [('l', 'list', None, _('list all patches and guards')),
2602 [('l', 'list', None, _('list all patches and guards')),
2599 ('n', 'none', None, _('drop all guards'))],
2603 ('n', 'none', None, _('drop all guards'))],
2600 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2604 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2601 'qheader': (header, [], _('hg qheader [PATCH]')),
2605 'qheader': (header, [], _('hg qheader [PATCH]')),
2602 "^qimport":
2606 "^qimport":
2603 (qimport,
2607 (qimport,
2604 [('e', 'existing', None, _('import file in patch directory')),
2608 [('e', 'existing', None, _('import file in patch directory')),
2605 ('n', 'name', '', _('name of patch file')),
2609 ('n', 'name', '', _('name of patch file')),
2606 ('f', 'force', None, _('overwrite existing files')),
2610 ('f', 'force', None, _('overwrite existing files')),
2607 ('r', 'rev', [], _('place existing revisions under mq control')),
2611 ('r', 'rev', [], _('place existing revisions under mq control')),
2608 ('g', 'git', None, _('use git extended diff format')),
2612 ('g', 'git', None, _('use git extended diff format')),
2609 ('P', 'push', None, _('qpush after importing'))],
2613 ('P', 'push', None, _('qpush after importing'))],
2610 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2614 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2611 "^qinit":
2615 "^qinit":
2612 (init,
2616 (init,
2613 [('c', 'create-repo', None, _('create queue repository'))],
2617 [('c', 'create-repo', None, _('create queue repository'))],
2614 _('hg qinit [-c]')),
2618 _('hg qinit [-c]')),
2615 "qnew":
2619 "qnew":
2616 (new,
2620 (new,
2617 [('e', 'edit', None, _('edit commit message')),
2621 [('e', 'edit', None, _('edit commit message')),
2618 ('f', 'force', None, _('import uncommitted changes into patch')),
2622 ('f', 'force', None, _('import uncommitted changes into patch')),
2619 ('g', 'git', None, _('use git extended diff format')),
2623 ('g', 'git', None, _('use git extended diff format')),
2620 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2624 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2621 ('u', 'user', '', _('add "From: <given user>" to patch')),
2625 ('u', 'user', '', _('add "From: <given user>" to patch')),
2622 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2626 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2623 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2627 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2624 ] + commands.walkopts + commands.commitopts,
2628 ] + commands.walkopts + commands.commitopts,
2625 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2629 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2626 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2630 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2627 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2631 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2628 "^qpop":
2632 "^qpop":
2629 (pop,
2633 (pop,
2630 [('a', 'all', None, _('pop all patches')),
2634 [('a', 'all', None, _('pop all patches')),
2631 ('n', 'name', '', _('queue name to pop')),
2635 ('n', 'name', '', _('queue name to pop')),
2632 ('f', 'force', None, _('forget any local changes to patched files'))],
2636 ('f', 'force', None, _('forget any local changes to patched files'))],
2633 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2637 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2634 "^qpush":
2638 "^qpush":
2635 (push,
2639 (push,
2636 [('f', 'force', None, _('apply if the patch has rejects')),
2640 [('f', 'force', None, _('apply if the patch has rejects')),
2637 ('l', 'list', None, _('list patch name in commit text')),
2641 ('l', 'list', None, _('list patch name in commit text')),
2638 ('a', 'all', None, _('apply all patches')),
2642 ('a', 'all', None, _('apply all patches')),
2639 ('m', 'merge', None, _('merge from another queue')),
2643 ('m', 'merge', None, _('merge from another queue')),
2640 ('n', 'name', '', _('merge queue name'))],
2644 ('n', 'name', '', _('merge queue name'))],
2641 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2645 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2642 "^qrefresh":
2646 "^qrefresh":
2643 (refresh,
2647 (refresh,
2644 [('e', 'edit', None, _('edit commit message')),
2648 [('e', 'edit', None, _('edit commit message')),
2645 ('g', 'git', None, _('use git extended diff format')),
2649 ('g', 'git', None, _('use git extended diff format')),
2646 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2650 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2647 ('U', 'currentuser', None, _('add/update author field in patch with current user')),
2651 ('U', 'currentuser', None, _('add/update author field in patch with current user')),
2648 ('u', 'user', '', _('add/update author field in patch with given user')),
2652 ('u', 'user', '', _('add/update author field in patch with given user')),
2649 ('D', 'currentdate', None, _('add/update date field in patch with current date')),
2653 ('D', 'currentdate', None, _('add/update date field in patch with current date')),
2650 ('d', 'date', '', _('add/update date field in patch with given date'))
2654 ('d', 'date', '', _('add/update date field in patch with given date'))
2651 ] + commands.walkopts + commands.commitopts,
2655 ] + commands.walkopts + commands.commitopts,
2652 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2656 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2653 'qrename|qmv':
2657 'qrename|qmv':
2654 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2658 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2655 "qrestore":
2659 "qrestore":
2656 (restore,
2660 (restore,
2657 [('d', 'delete', None, _('delete save entry')),
2661 [('d', 'delete', None, _('delete save entry')),
2658 ('u', 'update', None, _('update queue working directory'))],
2662 ('u', 'update', None, _('update queue working directory'))],
2659 _('hg qrestore [-d] [-u] REV')),
2663 _('hg qrestore [-d] [-u] REV')),
2660 "qsave":
2664 "qsave":
2661 (save,
2665 (save,
2662 [('c', 'copy', None, _('copy patch directory')),
2666 [('c', 'copy', None, _('copy patch directory')),
2663 ('n', 'name', '', _('copy directory name')),
2667 ('n', 'name', '', _('copy directory name')),
2664 ('e', 'empty', None, _('clear queue status file')),
2668 ('e', 'empty', None, _('clear queue status file')),
2665 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2669 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2666 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2670 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2667 "qselect":
2671 "qselect":
2668 (select,
2672 (select,
2669 [('n', 'none', None, _('disable all guards')),
2673 [('n', 'none', None, _('disable all guards')),
2670 ('s', 'series', None, _('list all guards in series file')),
2674 ('s', 'series', None, _('list all guards in series file')),
2671 ('', 'pop', None, _('pop to before first guarded applied patch')),
2675 ('', 'pop', None, _('pop to before first guarded applied patch')),
2672 ('', 'reapply', None, _('pop, then reapply patches'))],
2676 ('', 'reapply', None, _('pop, then reapply patches'))],
2673 _('hg qselect [OPTION]... [GUARD]...')),
2677 _('hg qselect [OPTION]... [GUARD]...')),
2674 "qseries":
2678 "qseries":
2675 (series,
2679 (series,
2676 [('m', 'missing', None, _('print patches not in series')),
2680 [('m', 'missing', None, _('print patches not in series')),
2677 ] + seriesopts,
2681 ] + seriesopts,
2678 _('hg qseries [-ms]')),
2682 _('hg qseries [-ms]')),
2679 "^strip":
2683 "^strip":
2680 (strip,
2684 (strip,
2681 [('f', 'force', None, _('force removal with local changes')),
2685 [('f', 'force', None, _('force removal with local changes')),
2682 ('b', 'backup', None, _('bundle unrelated changesets')),
2686 ('b', 'backup', None, _('bundle unrelated changesets')),
2683 ('n', 'nobackup', None, _('no backups'))],
2687 ('n', 'nobackup', None, _('no backups'))],
2684 _('hg strip [-f] [-b] [-n] REV')),
2688 _('hg strip [-f] [-b] [-n] REV')),
2685 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2689 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2686 "qunapplied":
2690 "qunapplied":
2687 (unapplied,
2691 (unapplied,
2688 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2692 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2689 _('hg qunapplied [-1] [-s] [PATCH]')),
2693 _('hg qunapplied [-1] [-s] [PATCH]')),
2690 "qfinish":
2694 "qfinish":
2691 (finish,
2695 (finish,
2692 [('a', 'applied', None, _('finish all applied changesets'))],
2696 [('a', 'applied', None, _('finish all applied changesets'))],
2693 _('hg qfinish [-a] [REV]...')),
2697 _('hg qfinish [-a] [REV]...')),
2694 }
2698 }
@@ -1,3698 +1,3703 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, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, error
12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 import patch, help, mdiff, url, encoding
13 import patch, help, mdiff, url, encoding
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17 import minirst
17 import minirst
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see hg forget.
28 undo an add before that, see hg forget.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31 """
31 """
32
32
33 bad = []
33 bad = []
34 names = []
34 names = []
35 m = cmdutil.match(repo, pats, opts)
35 m = cmdutil.match(repo, pats, opts)
36 oldbad = m.bad
36 oldbad = m.bad
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38
38
39 for f in repo.walk(m):
39 for f in repo.walk(m):
40 exact = m.exact(f)
40 exact = m.exact(f)
41 if exact or f not in repo.dirstate:
41 if exact or f not in repo.dirstate:
42 names.append(f)
42 names.append(f)
43 if ui.verbose or not exact:
43 if ui.verbose or not exact:
44 ui.status(_('adding %s\n') % m.rel(f))
44 ui.status(_('adding %s\n') % m.rel(f))
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 bad += [f for f in repo.add(names) if f in m.files()]
46 bad += [f for f in repo.add(names) if f in m.files()]
47 return bad and 1 or 0
47 return bad and 1 or 0
48
48
49 def addremove(ui, repo, *pats, **opts):
49 def addremove(ui, repo, *pats, **opts):
50 """add all new files, delete all missing files
50 """add all new files, delete all missing files
51
51
52 Add all new files and remove all missing files from the
52 Add all new files and remove all missing files from the
53 repository.
53 repository.
54
54
55 New files are ignored if they match any of the patterns in
55 New files are ignored if they match any of the patterns in
56 .hgignore. As with add, these changes take effect at the next
56 .hgignore. As with add, these changes take effect at the next
57 commit.
57 commit.
58
58
59 Use the -s/--similarity option to detect renamed files. With a
59 Use the -s/--similarity option to detect renamed files. With a
60 parameter greater than 0, this compares every removed file with
60 parameter greater than 0, this compares every removed file with
61 every added file and records those similar enough as renames. This
61 every added file and records those similar enough as renames. This
62 option takes a percentage between 0 (disabled) and 100 (files must
62 option takes a percentage between 0 (disabled) and 100 (files must
63 be identical) as its parameter. Detecting renamed files this way
63 be identical) as its parameter. Detecting renamed files this way
64 can be expensive.
64 can be expensive.
65 """
65 """
66 try:
66 try:
67 sim = float(opts.get('similarity') or 0)
67 sim = float(opts.get('similarity') or 0)
68 except ValueError:
68 except ValueError:
69 raise util.Abort(_('similarity must be a number'))
69 raise util.Abort(_('similarity must be a number'))
70 if sim < 0 or sim > 100:
70 if sim < 0 or sim > 100:
71 raise util.Abort(_('similarity must be between 0 and 100'))
71 raise util.Abort(_('similarity must be between 0 and 100'))
72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
73
73
74 def annotate(ui, repo, *pats, **opts):
74 def annotate(ui, repo, *pats, **opts):
75 """show changeset information by line for each file
75 """show changeset information by line for each file
76
76
77 List changes in files, showing the revision id responsible for
77 List changes in files, showing the revision id responsible for
78 each line
78 each line
79
79
80 This command is useful for discovering when a change was made and
80 This command is useful for discovering when a change was made and
81 by whom.
81 by whom.
82
82
83 Without the -a/--text option, annotate will avoid processing files
83 Without the -a/--text option, annotate will avoid processing files
84 it detects as binary. With -a, annotate will annotate the file
84 it detects as binary. With -a, annotate will annotate the file
85 anyway, although the results will probably be neither useful
85 anyway, although the results will probably be neither useful
86 nor desirable.
86 nor desirable.
87 """
87 """
88 datefunc = ui.quiet and util.shortdate or util.datestr
88 datefunc = ui.quiet and util.shortdate or util.datestr
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90
90
91 if not pats:
91 if not pats:
92 raise util.Abort(_('at least one filename or pattern is required'))
92 raise util.Abort(_('at least one filename or pattern is required'))
93
93
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 ('number', lambda x: str(x[0].rev())),
95 ('number', lambda x: str(x[0].rev())),
96 ('changeset', lambda x: short(x[0].node())),
96 ('changeset', lambda x: short(x[0].node())),
97 ('date', getdate),
97 ('date', getdate),
98 ('follow', lambda x: x[0].path()),
98 ('follow', lambda x: x[0].path()),
99 ]
99 ]
100
100
101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
102 and not opts.get('follow')):
102 and not opts.get('follow')):
103 opts['number'] = 1
103 opts['number'] = 1
104
104
105 linenumber = opts.get('line_number') is not None
105 linenumber = opts.get('line_number') is not None
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
108
108
109 funcmap = [func for op, func in opmap if opts.get(op)]
109 funcmap = [func for op, func in opmap if opts.get(op)]
110 if linenumber:
110 if linenumber:
111 lastfunc = funcmap[-1]
111 lastfunc = funcmap[-1]
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113
113
114 ctx = repo[opts.get('rev')]
114 ctx = repo[opts.get('rev')]
115
115
116 m = cmdutil.match(repo, pats, opts)
116 m = cmdutil.match(repo, pats, opts)
117 for abs in ctx.walk(m):
117 for abs in ctx.walk(m):
118 fctx = ctx[abs]
118 fctx = ctx[abs]
119 if not opts.get('text') and util.binary(fctx.data()):
119 if not opts.get('text') and util.binary(fctx.data()):
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 continue
121 continue
122
122
123 lines = fctx.annotate(follow=opts.get('follow'),
123 lines = fctx.annotate(follow=opts.get('follow'),
124 linenumber=linenumber)
124 linenumber=linenumber)
125 pieces = []
125 pieces = []
126
126
127 for f in funcmap:
127 for f in funcmap:
128 l = [f(n) for n, dummy in lines]
128 l = [f(n) for n, dummy in lines]
129 if l:
129 if l:
130 ml = max(map(len, l))
130 ml = max(map(len, l))
131 pieces.append(["%*s" % (ml, x) for x in l])
131 pieces.append(["%*s" % (ml, x) for x in l])
132
132
133 if pieces:
133 if pieces:
134 for p, l in zip(zip(*pieces), lines):
134 for p, l in zip(zip(*pieces), lines):
135 ui.write("%s: %s" % (" ".join(p), l[1]))
135 ui.write("%s: %s" % (" ".join(p), l[1]))
136
136
137 def archive(ui, repo, dest, **opts):
137 def archive(ui, repo, dest, **opts):
138 '''create an unversioned archive of a repository revision
138 '''create an unversioned archive of a repository revision
139
139
140 By default, the revision used is the parent of the working
140 By default, the revision used is the parent of the working
141 directory; use -r/--rev to specify a different revision.
141 directory; use -r/--rev to specify a different revision.
142
142
143 To specify the type of archive to create, use -t/--type. Valid
143 To specify the type of archive to create, use -t/--type. Valid
144 types are::
144 types are::
145
145
146 "files" (default): a directory full of files
146 "files" (default): a directory full of files
147 "tar": tar archive, uncompressed
147 "tar": tar archive, uncompressed
148 "tbz2": tar archive, compressed using bzip2
148 "tbz2": tar archive, compressed using bzip2
149 "tgz": tar archive, compressed using gzip
149 "tgz": tar archive, compressed using gzip
150 "uzip": zip archive, uncompressed
150 "uzip": zip archive, uncompressed
151 "zip": zip archive, compressed using deflate
151 "zip": zip archive, compressed using deflate
152
152
153 The exact name of the destination archive or directory is given
153 The exact name of the destination archive or directory is given
154 using a format string; see 'hg help export' for details.
154 using a format string; see 'hg help export' for details.
155
155
156 Each member added to an archive file has a directory prefix
156 Each member added to an archive file has a directory prefix
157 prepended. Use -p/--prefix to specify a format string for the
157 prepended. Use -p/--prefix to specify a format string for the
158 prefix. The default is the basename of the archive, with suffixes
158 prefix. The default is the basename of the archive, with suffixes
159 removed.
159 removed.
160 '''
160 '''
161
161
162 ctx = repo[opts.get('rev')]
162 ctx = repo[opts.get('rev')]
163 if not ctx:
163 if not ctx:
164 raise util.Abort(_('no working directory: please specify a revision'))
164 raise util.Abort(_('no working directory: please specify a revision'))
165 node = ctx.node()
165 node = ctx.node()
166 dest = cmdutil.make_filename(repo, dest, node)
166 dest = cmdutil.make_filename(repo, dest, node)
167 if os.path.realpath(dest) == repo.root:
167 if os.path.realpath(dest) == repo.root:
168 raise util.Abort(_('repository root cannot be destination'))
168 raise util.Abort(_('repository root cannot be destination'))
169 matchfn = cmdutil.match(repo, [], opts)
169 matchfn = cmdutil.match(repo, [], opts)
170 kind = opts.get('type') or 'files'
170 kind = opts.get('type') or 'files'
171 prefix = opts.get('prefix')
171 prefix = opts.get('prefix')
172 if dest == '-':
172 if dest == '-':
173 if kind == 'files':
173 if kind == 'files':
174 raise util.Abort(_('cannot archive plain files to stdout'))
174 raise util.Abort(_('cannot archive plain files to stdout'))
175 dest = sys.stdout
175 dest = sys.stdout
176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
177 prefix = cmdutil.make_filename(repo, prefix, node)
177 prefix = cmdutil.make_filename(repo, prefix, node)
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 matchfn, prefix)
179 matchfn, prefix)
180
180
181 def backout(ui, repo, node=None, rev=None, **opts):
181 def backout(ui, repo, node=None, rev=None, **opts):
182 '''reverse effect of earlier changeset
182 '''reverse effect of earlier changeset
183
183
184 Commit the backed out changes as a new changeset. The new
184 Commit the backed out changes as a new changeset. The new
185 changeset is a child of the backed out changeset.
185 changeset is a child of the backed out changeset.
186
186
187 If you backout a changeset other than the tip, a new head is
187 If you backout a changeset other than the tip, a new head is
188 created. This head will be the new tip and you should merge this
188 created. This head will be the new tip and you should merge this
189 backout changeset with another head.
189 backout changeset with another head.
190
190
191 The --merge option remembers the parent of the working directory
191 The --merge option remembers the parent of the working directory
192 before starting the backout, then merges the new head with that
192 before starting the backout, then merges the new head with that
193 changeset afterwards. This saves you from doing the merge by hand.
193 changeset afterwards. This saves you from doing the merge by hand.
194 The result of this merge is not committed, as with a normal merge.
194 The result of this merge is not committed, as with a normal merge.
195
195
196 See 'hg help dates' for a list of formats valid for -d/--date.
196 See 'hg help dates' for a list of formats valid for -d/--date.
197 '''
197 '''
198 if rev and node:
198 if rev and node:
199 raise util.Abort(_("please specify just one revision"))
199 raise util.Abort(_("please specify just one revision"))
200
200
201 if not rev:
201 if not rev:
202 rev = node
202 rev = node
203
203
204 if not rev:
204 if not rev:
205 raise util.Abort(_("please specify a revision to backout"))
205 raise util.Abort(_("please specify a revision to backout"))
206
206
207 date = opts.get('date')
207 date = opts.get('date')
208 if date:
208 if date:
209 opts['date'] = util.parsedate(date)
209 opts['date'] = util.parsedate(date)
210
210
211 cmdutil.bail_if_changed(repo)
211 cmdutil.bail_if_changed(repo)
212 node = repo.lookup(rev)
212 node = repo.lookup(rev)
213
213
214 op1, op2 = repo.dirstate.parents()
214 op1, op2 = repo.dirstate.parents()
215 a = repo.changelog.ancestor(op1, node)
215 a = repo.changelog.ancestor(op1, node)
216 if a != node:
216 if a != node:
217 raise util.Abort(_('cannot backout change on a different branch'))
217 raise util.Abort(_('cannot backout change on a different branch'))
218
218
219 p1, p2 = repo.changelog.parents(node)
219 p1, p2 = repo.changelog.parents(node)
220 if p1 == nullid:
220 if p1 == nullid:
221 raise util.Abort(_('cannot backout a change with no parents'))
221 raise util.Abort(_('cannot backout a change with no parents'))
222 if p2 != nullid:
222 if p2 != nullid:
223 if not opts.get('parent'):
223 if not opts.get('parent'):
224 raise util.Abort(_('cannot backout a merge changeset without '
224 raise util.Abort(_('cannot backout a merge changeset without '
225 '--parent'))
225 '--parent'))
226 p = repo.lookup(opts['parent'])
226 p = repo.lookup(opts['parent'])
227 if p not in (p1, p2):
227 if p not in (p1, p2):
228 raise util.Abort(_('%s is not a parent of %s') %
228 raise util.Abort(_('%s is not a parent of %s') %
229 (short(p), short(node)))
229 (short(p), short(node)))
230 parent = p
230 parent = p
231 else:
231 else:
232 if opts.get('parent'):
232 if opts.get('parent'):
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 parent = p1
234 parent = p1
235
235
236 # the backout should appear on the same branch
236 # the backout should appear on the same branch
237 branch = repo.dirstate.branch()
237 branch = repo.dirstate.branch()
238 hg.clean(repo, node, show_stats=False)
238 hg.clean(repo, node, show_stats=False)
239 repo.dirstate.setbranch(branch)
239 repo.dirstate.setbranch(branch)
240 revert_opts = opts.copy()
240 revert_opts = opts.copy()
241 revert_opts['date'] = None
241 revert_opts['date'] = None
242 revert_opts['all'] = True
242 revert_opts['all'] = True
243 revert_opts['rev'] = hex(parent)
243 revert_opts['rev'] = hex(parent)
244 revert_opts['no_backup'] = None
244 revert_opts['no_backup'] = None
245 revert(ui, repo, **revert_opts)
245 revert(ui, repo, **revert_opts)
246 commit_opts = opts.copy()
246 commit_opts = opts.copy()
247 commit_opts['addremove'] = False
247 commit_opts['addremove'] = False
248 if not commit_opts['message'] and not commit_opts['logfile']:
248 if not commit_opts['message'] and not commit_opts['logfile']:
249 # we don't translate commit messages
249 # we don't translate commit messages
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 good or
277 you have performed tests, mark the working directory as good or
278 bad, and bisect will either update to another candidate changeset
278 bad, 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 to mark revisions as good or bad:
285 Its exit status will be used to mark revisions as good or bad:
286 status 0 means good, 125 means to skip the revision, 127
286 status 0 means good, 125 means to skip the revision, 127
287 (command not found) will abort the bisection, and any other
287 (command not found) will abort the bisection, and any other
288 non-zero exit status means the revision is bad.
288 non-zero exit status means the revision is 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 changesets = 1
342 changesets = 1
343 try:
343 try:
344 while changesets:
344 while changesets:
345 # update state
345 # update state
346 status = util.system(command)
346 status = util.system(command)
347 if status == 125:
347 if status == 125:
348 transition = "skip"
348 transition = "skip"
349 elif status == 0:
349 elif status == 0:
350 transition = "good"
350 transition = "good"
351 # status < 0 means process was killed
351 # status < 0 means process was killed
352 elif status == 127:
352 elif status == 127:
353 raise util.Abort(_("failed to execute %s") % command)
353 raise util.Abort(_("failed to execute %s") % command)
354 elif status < 0:
354 elif status < 0:
355 raise util.Abort(_("%s killed") % command)
355 raise util.Abort(_("%s killed") % command)
356 else:
356 else:
357 transition = "bad"
357 transition = "bad"
358 ctx = repo[rev or '.']
358 ctx = repo[rev or '.']
359 state[transition].append(ctx.node())
359 state[transition].append(ctx.node())
360 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
360 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
361 check_state(state, interactive=False)
361 check_state(state, interactive=False)
362 # bisect
362 # bisect
363 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
363 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
364 # update to next check
364 # update to next check
365 cmdutil.bail_if_changed(repo)
365 cmdutil.bail_if_changed(repo)
366 hg.clean(repo, nodes[0], show_stats=False)
366 hg.clean(repo, nodes[0], show_stats=False)
367 finally:
367 finally:
368 hbisect.save_state(repo, state)
368 hbisect.save_state(repo, state)
369 return print_result(nodes, good)
369 return print_result(nodes, good)
370
370
371 # update state
371 # update state
372 node = repo.lookup(rev or '.')
372 node = repo.lookup(rev or '.')
373 if good or bad or skip:
373 if good or bad or skip:
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 hbisect.save_state(repo, state)
380 hbisect.save_state(repo, state)
381
381
382 if not check_state(state):
382 if not check_state(state):
383 return
383 return
384
384
385 # actually bisect
385 # actually bisect
386 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
386 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
387 if changesets == 0:
387 if changesets == 0:
388 print_result(nodes, good)
388 print_result(nodes, good)
389 else:
389 else:
390 assert len(nodes) == 1 # only a single node can be tested next
390 assert len(nodes) == 1 # only a single node can be tested next
391 node = nodes[0]
391 node = nodes[0]
392 # compute the approximate number of remaining tests
392 # compute the approximate number of remaining tests
393 tests, size = 0, 2
393 tests, size = 0, 2
394 while size <= changesets:
394 while size <= changesets:
395 tests, size = tests + 1, size * 2
395 tests, size = tests + 1, size * 2
396 rev = repo.changelog.rev(node)
396 rev = repo.changelog.rev(node)
397 ui.write(_("Testing changeset %d:%s "
397 ui.write(_("Testing changeset %d:%s "
398 "(%d changesets remaining, ~%d tests)\n")
398 "(%d changesets remaining, ~%d tests)\n")
399 % (rev, short(node), changesets, tests))
399 % (rev, short(node), changesets, tests))
400 if not noupdate:
400 if not noupdate:
401 cmdutil.bail_if_changed(repo)
401 cmdutil.bail_if_changed(repo)
402 return hg.clean(repo, node)
402 return hg.clean(repo, node)
403
403
404 def branch(ui, repo, label=None, **opts):
404 def branch(ui, repo, label=None, **opts):
405 """set or show the current branch name
405 """set or show the current branch name
406
406
407 With no argument, show the current branch name. With one argument,
407 With no argument, show the current branch name. With one argument,
408 set the working directory branch name (the branch will not exist
408 set the working directory branch name (the branch will not exist
409 in the repository until the next commit). Standard practice
409 in the repository until the next commit). Standard practice
410 recommends that primary development take place on the 'default'
410 recommends that primary development take place on the 'default'
411 branch.
411 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 already exists, even if it's inactive.
414 branch name that already exists, even if it's inactive.
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. Use
420 Use the command 'hg update' to switch to an existing branch. Use
421 'hg commit --close-branch' to mark this branch as closed.
421 'hg commit --close-branch' to mark this branch as closed.
422 """
422 """
423
423
424 if opts.get('clean'):
424 if opts.get('clean'):
425 label = repo[None].parents()[0].branch()
425 label = repo[None].parents()[0].branch()
426 repo.dirstate.setbranch(label)
426 repo.dirstate.setbranch(label)
427 ui.status(_('reset working directory to branch %s\n') % label)
427 ui.status(_('reset working directory to branch %s\n') % label)
428 elif label:
428 elif label:
429 if not opts.get('force') and label in repo.branchtags():
429 if not opts.get('force') and label in repo.branchtags():
430 if label not in [p.branch() for p in repo.parents()]:
430 if label not in [p.branch() for p in repo.parents()]:
431 raise util.Abort(_('a branch of the same name already exists'
431 raise util.Abort(_('a branch of the same name already exists'
432 ' (use --force to override)'))
432 ' (use --force to override)'))
433 repo.dirstate.setbranch(encoding.fromlocal(label))
433 repo.dirstate.setbranch(encoding.fromlocal(label))
434 ui.status(_('marked working directory as branch %s\n') % label)
434 ui.status(_('marked working directory as branch %s\n') % label)
435 else:
435 else:
436 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
436 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
437
437
438 def branches(ui, repo, active=False, closed=False):
438 def branches(ui, repo, active=False, closed=False):
439 """list repository named branches
439 """list repository named branches
440
440
441 List the repository's named branches, indicating which ones are
441 List the repository's named branches, indicating which ones are
442 inactive. If -c/--closed is specified, also list branches which have
442 inactive. If -c/--closed is specified, also list branches which have
443 been marked closed (see hg commit --close-branch).
443 been marked closed (see hg commit --close-branch).
444
444
445 If -a/--active is specified, only show active branches. A branch
445 If -a/--active is specified, only show active branches. A branch
446 is considered active if it contains repository heads.
446 is considered active if it contains repository heads.
447
447
448 Use the command 'hg update' to switch to an existing branch.
448 Use the command 'hg update' to switch to an existing branch.
449 """
449 """
450
450
451 hexfunc = ui.debugflag and hex or short
451 hexfunc = ui.debugflag and hex or short
452 activebranches = [repo[n].branch() for n in repo.heads()]
452 activebranches = [repo[n].branch() for n in repo.heads()]
453 def testactive(tag, node):
453 def testactive(tag, node):
454 realhead = tag in activebranches
454 realhead = tag in activebranches
455 open = node in repo.branchheads(tag, closed=False)
455 open = node in repo.branchheads(tag, closed=False)
456 return realhead and open
456 return realhead and open
457 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
457 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
458 for tag, node in repo.branchtags().items()],
458 for tag, node in repo.branchtags().items()],
459 reverse=True)
459 reverse=True)
460
460
461 for isactive, node, tag in branches:
461 for isactive, node, tag in branches:
462 if (not active) or isactive:
462 if (not active) or isactive:
463 encodedtag = encoding.tolocal(tag)
463 encodedtag = encoding.tolocal(tag)
464 if ui.quiet:
464 if ui.quiet:
465 ui.write("%s\n" % encodedtag)
465 ui.write("%s\n" % encodedtag)
466 else:
466 else:
467 hn = repo.lookup(node)
467 hn = repo.lookup(node)
468 if isactive:
468 if isactive:
469 notice = ''
469 notice = ''
470 elif hn not in repo.branchheads(tag, closed=False):
470 elif hn not in repo.branchheads(tag, closed=False):
471 if not closed:
471 if not closed:
472 continue
472 continue
473 notice = ' (closed)'
473 notice = ' (closed)'
474 else:
474 else:
475 notice = ' (inactive)'
475 notice = ' (inactive)'
476 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
476 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
477 data = encodedtag, rev, hexfunc(hn), notice
477 data = encodedtag, rev, hexfunc(hn), notice
478 ui.write("%s %s:%s%s\n" % data)
478 ui.write("%s %s:%s%s\n" % data)
479
479
480 def bundle(ui, repo, fname, dest=None, **opts):
480 def bundle(ui, repo, fname, dest=None, **opts):
481 """create a changegroup file
481 """create a changegroup file
482
482
483 Generate a compressed changegroup file collecting changesets not
483 Generate a compressed changegroup file collecting changesets not
484 known to be in another repository.
484 known to be in another repository.
485
485
486 If no destination repository is specified the destination is
486 If no destination repository is specified the destination is
487 assumed to have all the nodes specified by one or more --base
487 assumed to have all the nodes specified by one or more --base
488 parameters. To create a bundle containing all changesets, use
488 parameters. To create a bundle containing all changesets, use
489 -a/--all (or --base null).
489 -a/--all (or --base null).
490
490
491 You can change compression method with the -t/--type option.
491 You can change compression method with the -t/--type option.
492 The available compression methods are: none, bzip2, and
492 The available compression methods are: none, bzip2, and
493 gzip (by default, bundles are compressed using bzip2).
493 gzip (by default, bundles are compressed using bzip2).
494
494
495 The bundle file can then be transferred using conventional means
495 The bundle file can then be transferred using conventional means
496 and applied to another repository with the unbundle or pull
496 and applied to another repository with the unbundle or pull
497 command. This is useful when direct push and pull are not
497 command. This is useful when direct push and pull are not
498 available or when exporting an entire repository is undesirable.
498 available or when exporting an entire repository is undesirable.
499
499
500 Applying bundles preserves all changeset contents including
500 Applying bundles preserves all changeset contents including
501 permissions, copy/rename information, and revision history.
501 permissions, copy/rename information, and revision history.
502 """
502 """
503 revs = opts.get('rev') or None
503 revs = opts.get('rev') or None
504 if revs:
504 if revs:
505 revs = [repo.lookup(rev) for rev in revs]
505 revs = [repo.lookup(rev) for rev in revs]
506 if opts.get('all'):
506 if opts.get('all'):
507 base = ['null']
507 base = ['null']
508 else:
508 else:
509 base = opts.get('base')
509 base = opts.get('base')
510 if base:
510 if base:
511 if dest:
511 if dest:
512 raise util.Abort(_("--base is incompatible with specifying "
512 raise util.Abort(_("--base is incompatible with specifying "
513 "a destination"))
513 "a destination"))
514 base = [repo.lookup(rev) for rev in base]
514 base = [repo.lookup(rev) for rev in base]
515 # create the right base
515 # create the right base
516 # XXX: nodesbetween / changegroup* should be "fixed" instead
516 # XXX: nodesbetween / changegroup* should be "fixed" instead
517 o = []
517 o = []
518 has = set((nullid,))
518 has = set((nullid,))
519 for n in base:
519 for n in base:
520 has.update(repo.changelog.reachable(n))
520 has.update(repo.changelog.reachable(n))
521 if revs:
521 if revs:
522 visit = list(revs)
522 visit = list(revs)
523 else:
523 else:
524 visit = repo.changelog.heads()
524 visit = repo.changelog.heads()
525 seen = {}
525 seen = {}
526 while visit:
526 while visit:
527 n = visit.pop(0)
527 n = visit.pop(0)
528 parents = [p for p in repo.changelog.parents(n) if p not in has]
528 parents = [p for p in repo.changelog.parents(n) if p not in has]
529 if len(parents) == 0:
529 if len(parents) == 0:
530 o.insert(0, n)
530 o.insert(0, n)
531 else:
531 else:
532 for p in parents:
532 for p in parents:
533 if p not in seen:
533 if p not in seen:
534 seen[p] = 1
534 seen[p] = 1
535 visit.append(p)
535 visit.append(p)
536 else:
536 else:
537 dest, revs, checkout = hg.parseurl(
537 dest, revs, checkout = hg.parseurl(
538 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
538 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
539 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
539 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
540 o = repo.findoutgoing(other, force=opts.get('force'))
540 o = repo.findoutgoing(other, force=opts.get('force'))
541
541
542 if revs:
542 if revs:
543 cg = repo.changegroupsubset(o, revs, 'bundle')
543 cg = repo.changegroupsubset(o, revs, 'bundle')
544 else:
544 else:
545 cg = repo.changegroup(o, 'bundle')
545 cg = repo.changegroup(o, 'bundle')
546
546
547 bundletype = opts.get('type', 'bzip2').lower()
547 bundletype = opts.get('type', 'bzip2').lower()
548 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
548 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
549 bundletype = btypes.get(bundletype)
549 bundletype = btypes.get(bundletype)
550 if bundletype not in changegroup.bundletypes:
550 if bundletype not in changegroup.bundletypes:
551 raise util.Abort(_('unknown bundle type specified with --type'))
551 raise util.Abort(_('unknown bundle type specified with --type'))
552
552
553 changegroup.writebundle(cg, fname, bundletype)
553 changegroup.writebundle(cg, fname, bundletype)
554
554
555 def cat(ui, repo, file1, *pats, **opts):
555 def cat(ui, repo, file1, *pats, **opts):
556 """output the current or given revision of files
556 """output the current or given revision of files
557
557
558 Print the specified files as they were at the given revision. If
558 Print the specified files as they were at the given revision. If
559 no revision is given, the parent of the working directory is used,
559 no revision is given, the parent of the working directory is used,
560 or tip if no revision is checked out.
560 or tip if no revision is checked out.
561
561
562 Output may be to a file, in which case the name of the file is
562 Output may be to a file, in which case the name of the file is
563 given using a format string. The formatting rules are the same as
563 given using a format string. The formatting rules are the same as
564 for the export command, with the following additions::
564 for the export command, with the following additions::
565
565
566 %s basename of file being printed
566 %s basename of file being printed
567 %d dirname of file being printed, or '.' if in repository root
567 %d dirname of file being printed, or '.' if in repository root
568 %p root-relative path name of file being printed
568 %p root-relative path name of file being printed
569 """
569 """
570 ctx = repo[opts.get('rev')]
570 ctx = repo[opts.get('rev')]
571 err = 1
571 err = 1
572 m = cmdutil.match(repo, (file1,) + pats, opts)
572 m = cmdutil.match(repo, (file1,) + pats, opts)
573 for abs in ctx.walk(m):
573 for abs in ctx.walk(m):
574 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
574 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
575 data = ctx[abs].data()
575 data = ctx[abs].data()
576 if opts.get('decode'):
576 if opts.get('decode'):
577 data = repo.wwritedata(abs, data)
577 data = repo.wwritedata(abs, data)
578 fp.write(data)
578 fp.write(data)
579 err = 0
579 err = 0
580 return err
580 return err
581
581
582 def clone(ui, source, dest=None, **opts):
582 def clone(ui, source, dest=None, **opts):
583 """make a copy of an existing repository
583 """make a copy of an existing repository
584
584
585 Create a copy of an existing repository in a new directory.
585 Create a copy of an existing repository in a new directory.
586
586
587 If no destination directory name is specified, it defaults to the
587 If no destination directory name is specified, it defaults to the
588 basename of the source.
588 basename of the source.
589
589
590 The location of the source is added to the new repository's
590 The location of the source is added to the new repository's
591 .hg/hgrc file, as the default to be used for future pulls.
591 .hg/hgrc file, as the default to be used for future pulls.
592
592
593 See 'hg help urls' for valid source format details.
593 See 'hg help urls' for valid source format details.
594
594
595 It is possible to specify an ssh:// URL as the destination, but no
595 It is possible to specify an ssh:// URL as the destination, but no
596 .hg/hgrc and working directory will be created on the remote side.
596 .hg/hgrc and working directory will be created on the remote side.
597 Please see 'hg help urls' for important details about ssh:// URLs.
597 Please see 'hg help urls' for important details about ssh:// URLs.
598
598
599 If the -U/--noupdate option is specified, the new clone will contain
599 If the -U/--noupdate option is specified, the new clone will contain
600 only a repository (.hg) and no working copy (the working copy parent
600 only a repository (.hg) and no working copy (the working copy parent
601 will be the null changeset). Otherwise, clone will initially check
601 will be the null changeset). Otherwise, clone will initially check
602 out (in order of precedence): ::
602 out (in order of precedence): ::
603
603
604 a) the changeset, tag or branch specified with -u/--updaterev
604 a) the changeset, tag or branch specified with -u/--updaterev
605 b) the changeset, tag or branch given with the first -r/--rev
605 b) the changeset, tag or branch given with the first -r/--rev
606 c) the head of the default branch
606 c) the head of the default branch
607
607
608 Use 'hg clone -u . src dst' to checkout the source repository's
608 Use 'hg clone -u . src dst' to checkout the source repository's
609 parent changeset (applicable for local source repositories only).
609 parent changeset (applicable for local source repositories only).
610
610
611 A set of changesets (tags, or branch names) to pull may be specified
611 A set of changesets (tags, or branch names) to pull may be specified
612 by listing each changeset (tag, or branch name) with -r/--rev.
612 by listing each changeset (tag, or branch name) with -r/--rev.
613 If -r/--rev is used, the cloned repository will contain only a subset
613 If -r/--rev is used, the cloned repository will contain only a subset
614 of the changesets of the source repository. Only the set of changesets
614 of the changesets of the source repository. Only the set of changesets
615 defined by all -r/--rev options (including their direct and indirect
615 defined by all -r/--rev options (including their direct and indirect
616 parent changesets) will be pulled into the destination repository.
616 parent changesets) will be pulled into the destination repository.
617 No subsequent changesets (including subsequent tags) will be present
617 No subsequent changesets (including subsequent tags) will be present
618 in the destination.
618 in the destination.
619
619
620 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
620 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
621 local source repositories.
621 local source repositories.
622
622
623 For efficiency, hardlinks are used for cloning whenever the source
623 For efficiency, hardlinks are used for cloning whenever the source
624 and destination are on the same filesystem (note this applies only
624 and destination are on the same filesystem (note this applies only
625 to the repository data, not to the checked out files). Some
625 to the repository data, not to the checked out files). Some
626 filesystems, such as AFS, implement hardlinking incorrectly, but
626 filesystems, such as AFS, implement hardlinking incorrectly, but
627 do not report errors. In these cases, use the --pull option to
627 do not report errors. In these cases, use the --pull option to
628 avoid hardlinking.
628 avoid hardlinking.
629
629
630 In some cases, you can clone repositories and checked out files
630 In some cases, you can clone repositories and checked out files
631 using full hardlinks with ::
631 using full hardlinks with ::
632
632
633 $ cp -al REPO REPOCLONE
633 $ cp -al REPO REPOCLONE
634
634
635 This is the fastest way to clone, but it is not always safe. The
635 This is the fastest way to clone, but it is not always safe. The
636 operation is not atomic (making sure REPO is not modified during
636 operation is not atomic (making sure REPO is not modified during
637 the operation is up to you) and you have to make sure your editor
637 the operation is up to you) and you have to make sure your editor
638 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
638 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
639 this is not compatible with certain extensions that place their
639 this is not compatible with certain extensions that place their
640 metadata under the .hg directory, such as mq.
640 metadata under the .hg directory, such as mq.
641 """
641 """
642 if opts.get('noupdate') and opts.get('updaterev'):
642 if opts.get('noupdate') and opts.get('updaterev'):
643 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
643 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
644
644
645 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
645 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
646 pull=opts.get('pull'),
646 pull=opts.get('pull'),
647 stream=opts.get('uncompressed'),
647 stream=opts.get('uncompressed'),
648 rev=opts.get('rev'),
648 rev=opts.get('rev'),
649 update=opts.get('updaterev') or not opts.get('noupdate'))
649 update=opts.get('updaterev') or not opts.get('noupdate'))
650
650
651 def commit(ui, repo, *pats, **opts):
651 def commit(ui, repo, *pats, **opts):
652 """commit the specified files or all outstanding changes
652 """commit the specified files or all outstanding changes
653
653
654 Commit changes to the given files into the repository. Unlike a
654 Commit changes to the given files into the repository. Unlike a
655 centralized RCS, this operation is a local operation. See hg push
655 centralized RCS, this operation is a local operation. See hg push
656 for a way to actively distribute your changes.
656 for a way to actively distribute your changes.
657
657
658 If a list of files is omitted, all changes reported by "hg status"
658 If a list of files is omitted, all changes reported by "hg status"
659 will be committed.
659 will be committed.
660
660
661 If you are committing the result of a merge, do not provide any
661 If you are committing the result of a merge, do not provide any
662 filenames or -I/-X filters.
662 filenames or -I/-X filters.
663
663
664 If no commit message is specified, the configured editor is
664 If no commit message is specified, the configured editor is
665 started to prompt you for a message.
665 started to prompt you for a message.
666
666
667 See 'hg help dates' for a list of formats valid for -d/--date.
667 See 'hg help dates' for a list of formats valid for -d/--date.
668 """
668 """
669 extra = {}
669 extra = {}
670 if opts.get('close_branch'):
670 if opts.get('close_branch'):
671 extra['close'] = 1
671 extra['close'] = 1
672 e = cmdutil.commiteditor
672 e = cmdutil.commiteditor
673 if opts.get('force_editor'):
673 if opts.get('force_editor'):
674 e = cmdutil.commitforceeditor
674 e = cmdutil.commitforceeditor
675
675
676 def commitfunc(ui, repo, message, match, opts):
676 def commitfunc(ui, repo, message, match, opts):
677 return repo.commit(message, opts.get('user'), opts.get('date'), match,
677 return repo.commit(message, opts.get('user'), opts.get('date'), match,
678 editor=e, extra=extra)
678 editor=e, extra=extra)
679
679
680 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
680 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
681 if not node:
681 if not node:
682 ui.status(_("nothing changed\n"))
682 ui.status(_("nothing changed\n"))
683 return
683 return
684 cl = repo.changelog
684 cl = repo.changelog
685 rev = cl.rev(node)
685 rev = cl.rev(node)
686 parents = cl.parentrevs(rev)
686 parents = cl.parentrevs(rev)
687 if rev - 1 in parents:
687 if rev - 1 in parents:
688 # one of the parents was the old tip
688 # one of the parents was the old tip
689 pass
689 pass
690 elif (parents == (nullrev, nullrev) or
690 elif (parents == (nullrev, nullrev) or
691 len(cl.heads(cl.node(parents[0]))) > 1 and
691 len(cl.heads(cl.node(parents[0]))) > 1 and
692 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
692 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
693 ui.status(_('created new head\n'))
693 ui.status(_('created new head\n'))
694
694
695 if ui.debugflag:
695 if ui.debugflag:
696 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
696 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
697 elif ui.verbose:
697 elif ui.verbose:
698 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
698 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
699
699
700 def copy(ui, repo, *pats, **opts):
700 def copy(ui, repo, *pats, **opts):
701 """mark files as copied for the next commit
701 """mark files as copied for the next commit
702
702
703 Mark dest as having copies of source files. If dest is a
703 Mark dest as having copies of source files. If dest is a
704 directory, copies are put in that directory. If dest is a file,
704 directory, copies are put in that directory. If dest is a file,
705 the source must be a single file.
705 the source must be a single file.
706
706
707 By default, this command copies the contents of files as they
707 By default, this command copies the contents of files as they
708 exist in the working directory. If invoked with -A/--after, the
708 exist in the working directory. If invoked with -A/--after, the
709 operation is recorded, but no copying is performed.
709 operation is recorded, but no copying is performed.
710
710
711 This command takes effect with the next commit. To undo a copy
711 This command takes effect with the next commit. To undo a copy
712 before that, see hg revert.
712 before that, see hg revert.
713 """
713 """
714 wlock = repo.wlock(False)
714 wlock = repo.wlock(False)
715 try:
715 try:
716 return cmdutil.copy(ui, repo, pats, opts)
716 return cmdutil.copy(ui, repo, pats, opts)
717 finally:
717 finally:
718 wlock.release()
718 wlock.release()
719
719
720 def debugancestor(ui, repo, *args):
720 def debugancestor(ui, repo, *args):
721 """find the ancestor revision of two revisions in a given index"""
721 """find the ancestor revision of two revisions in a given index"""
722 if len(args) == 3:
722 if len(args) == 3:
723 index, rev1, rev2 = args
723 index, rev1, rev2 = args
724 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
724 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
725 lookup = r.lookup
725 lookup = r.lookup
726 elif len(args) == 2:
726 elif len(args) == 2:
727 if not repo:
727 if not repo:
728 raise util.Abort(_("There is no Mercurial repository here "
728 raise util.Abort(_("There is no Mercurial repository here "
729 "(.hg not found)"))
729 "(.hg not found)"))
730 rev1, rev2 = args
730 rev1, rev2 = args
731 r = repo.changelog
731 r = repo.changelog
732 lookup = repo.lookup
732 lookup = repo.lookup
733 else:
733 else:
734 raise util.Abort(_('either two or three arguments required'))
734 raise util.Abort(_('either two or three arguments required'))
735 a = r.ancestor(lookup(rev1), lookup(rev2))
735 a = r.ancestor(lookup(rev1), lookup(rev2))
736 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
736 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
737
737
738 def debugcommands(ui, cmd='', *args):
738 def debugcommands(ui, cmd='', *args):
739 for cmd, vals in sorted(table.iteritems()):
739 for cmd, vals in sorted(table.iteritems()):
740 cmd = cmd.split('|')[0].strip('^')
740 cmd = cmd.split('|')[0].strip('^')
741 opts = ', '.join([i[1] for i in vals[1]])
741 opts = ', '.join([i[1] for i in vals[1]])
742 ui.write('%s: %s\n' % (cmd, opts))
742 ui.write('%s: %s\n' % (cmd, opts))
743
743
744 def debugcomplete(ui, cmd='', **opts):
744 def debugcomplete(ui, cmd='', **opts):
745 """returns the completion list associated with the given command"""
745 """returns the completion list associated with the given command"""
746
746
747 if opts.get('options'):
747 if opts.get('options'):
748 options = []
748 options = []
749 otables = [globalopts]
749 otables = [globalopts]
750 if cmd:
750 if cmd:
751 aliases, entry = cmdutil.findcmd(cmd, table, False)
751 aliases, entry = cmdutil.findcmd(cmd, table, False)
752 otables.append(entry[1])
752 otables.append(entry[1])
753 for t in otables:
753 for t in otables:
754 for o in t:
754 for o in t:
755 if o[0]:
755 if o[0]:
756 options.append('-%s' % o[0])
756 options.append('-%s' % o[0])
757 options.append('--%s' % o[1])
757 options.append('--%s' % o[1])
758 ui.write("%s\n" % "\n".join(options))
758 ui.write("%s\n" % "\n".join(options))
759 return
759 return
760
760
761 cmdlist = cmdutil.findpossible(cmd, table)
761 cmdlist = cmdutil.findpossible(cmd, table)
762 if ui.verbose:
762 if ui.verbose:
763 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
763 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
764 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
764 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
765
765
766 def debugfsinfo(ui, path = "."):
766 def debugfsinfo(ui, path = "."):
767 open('.debugfsinfo', 'w').write('')
767 open('.debugfsinfo', 'w').write('')
768 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
768 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
769 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
769 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
770 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
770 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
771 and 'yes' or 'no'))
771 and 'yes' or 'no'))
772 os.unlink('.debugfsinfo')
772 os.unlink('.debugfsinfo')
773
773
774 def debugrebuildstate(ui, repo, rev="tip"):
774 def debugrebuildstate(ui, repo, rev="tip"):
775 """rebuild the dirstate as it would look like for the given revision"""
775 """rebuild the dirstate as it would look like for the given revision"""
776 ctx = repo[rev]
776 ctx = repo[rev]
777 wlock = repo.wlock()
777 wlock = repo.wlock()
778 try:
778 try:
779 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
779 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
780 finally:
780 finally:
781 wlock.release()
781 wlock.release()
782
782
783 def debugcheckstate(ui, repo):
783 def debugcheckstate(ui, repo):
784 """validate the correctness of the current dirstate"""
784 """validate the correctness of the current dirstate"""
785 parent1, parent2 = repo.dirstate.parents()
785 parent1, parent2 = repo.dirstate.parents()
786 m1 = repo[parent1].manifest()
786 m1 = repo[parent1].manifest()
787 m2 = repo[parent2].manifest()
787 m2 = repo[parent2].manifest()
788 errors = 0
788 errors = 0
789 for f in repo.dirstate:
789 for f in repo.dirstate:
790 state = repo.dirstate[f]
790 state = repo.dirstate[f]
791 if state in "nr" and f not in m1:
791 if state in "nr" and f not in m1:
792 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
792 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
793 errors += 1
793 errors += 1
794 if state in "a" and f in m1:
794 if state in "a" and f in m1:
795 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
795 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
796 errors += 1
796 errors += 1
797 if state in "m" and f not in m1 and f not in m2:
797 if state in "m" and f not in m1 and f not in m2:
798 ui.warn(_("%s in state %s, but not in either manifest\n") %
798 ui.warn(_("%s in state %s, but not in either manifest\n") %
799 (f, state))
799 (f, state))
800 errors += 1
800 errors += 1
801 for f in m1:
801 for f in m1:
802 state = repo.dirstate[f]
802 state = repo.dirstate[f]
803 if state not in "nrm":
803 if state not in "nrm":
804 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
804 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
805 errors += 1
805 errors += 1
806 if errors:
806 if errors:
807 error = _(".hg/dirstate inconsistent with current parent's manifest")
807 error = _(".hg/dirstate inconsistent with current parent's manifest")
808 raise util.Abort(error)
808 raise util.Abort(error)
809
809
810 def showconfig(ui, repo, *values, **opts):
810 def showconfig(ui, repo, *values, **opts):
811 """show combined config settings from all hgrc files
811 """show combined config settings from all hgrc files
812
812
813 With no arguments, print names and values of all config items.
813 With no arguments, print names and values of all config items.
814
814
815 With one argument of the form section.name, print just the value
815 With one argument of the form section.name, print just the value
816 of that config item.
816 of that config item.
817
817
818 With multiple arguments, print names and values of all config
818 With multiple arguments, print names and values of all config
819 items with matching section names.
819 items with matching section names.
820
820
821 With --debug, the source (filename and line number) is printed
821 With --debug, the source (filename and line number) is printed
822 for each config item.
822 for each config item.
823 """
823 """
824
824
825 untrusted = bool(opts.get('untrusted'))
825 untrusted = bool(opts.get('untrusted'))
826 if values:
826 if values:
827 if len([v for v in values if '.' in v]) > 1:
827 if len([v for v in values if '.' in v]) > 1:
828 raise util.Abort(_('only one config item permitted'))
828 raise util.Abort(_('only one config item permitted'))
829 for section, name, value in ui.walkconfig(untrusted=untrusted):
829 for section, name, value in ui.walkconfig(untrusted=untrusted):
830 sectname = section + '.' + name
830 sectname = section + '.' + name
831 if values:
831 if values:
832 for v in values:
832 for v in values:
833 if v == section:
833 if v == section:
834 ui.debug('%s: ' %
834 ui.debug('%s: ' %
835 ui.configsource(section, name, untrusted))
835 ui.configsource(section, name, untrusted))
836 ui.write('%s=%s\n' % (sectname, value))
836 ui.write('%s=%s\n' % (sectname, value))
837 elif v == sectname:
837 elif v == sectname:
838 ui.debug('%s: ' %
838 ui.debug('%s: ' %
839 ui.configsource(section, name, untrusted))
839 ui.configsource(section, name, untrusted))
840 ui.write(value, '\n')
840 ui.write(value, '\n')
841 else:
841 else:
842 ui.debug('%s: ' %
842 ui.debug('%s: ' %
843 ui.configsource(section, name, untrusted))
843 ui.configsource(section, name, untrusted))
844 ui.write('%s=%s\n' % (sectname, value))
844 ui.write('%s=%s\n' % (sectname, value))
845
845
846 def debugsetparents(ui, repo, rev1, rev2=None):
846 def debugsetparents(ui, repo, rev1, rev2=None):
847 """manually set the parents of the current working directory
847 """manually set the parents of the current working directory
848
848
849 This is useful for writing repository conversion tools, but should
849 This is useful for writing repository conversion tools, but should
850 be used with care.
850 be used with care.
851 """
851 """
852
852
853 if not rev2:
853 if not rev2:
854 rev2 = hex(nullid)
854 rev2 = hex(nullid)
855
855
856 wlock = repo.wlock()
856 wlock = repo.wlock()
857 try:
857 try:
858 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
858 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
859 finally:
859 finally:
860 wlock.release()
860 wlock.release()
861
861
862 def debugstate(ui, repo, nodates=None):
862 def debugstate(ui, repo, nodates=None):
863 """show the contents of the current dirstate"""
863 """show the contents of the current dirstate"""
864 timestr = ""
864 timestr = ""
865 showdate = not nodates
865 showdate = not nodates
866 for file_, ent in sorted(repo.dirstate._map.iteritems()):
866 for file_, ent in sorted(repo.dirstate._map.iteritems()):
867 if showdate:
867 if showdate:
868 if ent[3] == -1:
868 if ent[3] == -1:
869 # Pad or slice to locale representation
869 # Pad or slice to locale representation
870 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
870 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
871 timestr = 'unset'
871 timestr = 'unset'
872 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
872 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
873 else:
873 else:
874 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
874 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
875 if ent[1] & 020000:
875 if ent[1] & 020000:
876 mode = 'lnk'
876 mode = 'lnk'
877 else:
877 else:
878 mode = '%3o' % (ent[1] & 0777)
878 mode = '%3o' % (ent[1] & 0777)
879 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
879 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
880 for f in repo.dirstate.copies():
880 for f in repo.dirstate.copies():
881 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
881 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
882
882
883 def debugsub(ui, repo, rev=None):
883 def debugsub(ui, repo, rev=None):
884 if rev == '':
884 if rev == '':
885 rev = None
885 rev = None
886 for k,v in sorted(repo[rev].substate.items()):
886 for k,v in sorted(repo[rev].substate.items()):
887 ui.write('path %s\n' % k)
887 ui.write('path %s\n' % k)
888 ui.write(' source %s\n' % v[0])
888 ui.write(' source %s\n' % v[0])
889 ui.write(' revision %s\n' % v[1])
889 ui.write(' revision %s\n' % v[1])
890
890
891 def debugdata(ui, file_, rev):
891 def debugdata(ui, file_, rev):
892 """dump the contents of a data file revision"""
892 """dump the contents of a data file revision"""
893 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
893 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
894 try:
894 try:
895 ui.write(r.revision(r.lookup(rev)))
895 ui.write(r.revision(r.lookup(rev)))
896 except KeyError:
896 except KeyError:
897 raise util.Abort(_('invalid revision identifier %s') % rev)
897 raise util.Abort(_('invalid revision identifier %s') % rev)
898
898
899 def debugdate(ui, date, range=None, **opts):
899 def debugdate(ui, date, range=None, **opts):
900 """parse and display a date"""
900 """parse and display a date"""
901 if opts["extended"]:
901 if opts["extended"]:
902 d = util.parsedate(date, util.extendeddateformats)
902 d = util.parsedate(date, util.extendeddateformats)
903 else:
903 else:
904 d = util.parsedate(date)
904 d = util.parsedate(date)
905 ui.write("internal: %s %s\n" % d)
905 ui.write("internal: %s %s\n" % d)
906 ui.write("standard: %s\n" % util.datestr(d))
906 ui.write("standard: %s\n" % util.datestr(d))
907 if range:
907 if range:
908 m = util.matchdate(range)
908 m = util.matchdate(range)
909 ui.write("match: %s\n" % m(d[0]))
909 ui.write("match: %s\n" % m(d[0]))
910
910
911 def debugindex(ui, file_):
911 def debugindex(ui, file_):
912 """dump the contents of an index file"""
912 """dump the contents of an index file"""
913 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
913 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
914 ui.write(" rev offset length base linkrev"
914 ui.write(" rev offset length base linkrev"
915 " nodeid p1 p2\n")
915 " nodeid p1 p2\n")
916 for i in r:
916 for i in r:
917 node = r.node(i)
917 node = r.node(i)
918 try:
918 try:
919 pp = r.parents(node)
919 pp = r.parents(node)
920 except:
920 except:
921 pp = [nullid, nullid]
921 pp = [nullid, nullid]
922 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
922 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
923 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
923 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
924 short(node), short(pp[0]), short(pp[1])))
924 short(node), short(pp[0]), short(pp[1])))
925
925
926 def debugindexdot(ui, file_):
926 def debugindexdot(ui, file_):
927 """dump an index DAG as a graphviz dot file"""
927 """dump an index DAG as a graphviz dot file"""
928 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
928 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
929 ui.write("digraph G {\n")
929 ui.write("digraph G {\n")
930 for i in r:
930 for i in r:
931 node = r.node(i)
931 node = r.node(i)
932 pp = r.parents(node)
932 pp = r.parents(node)
933 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
933 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
934 if pp[1] != nullid:
934 if pp[1] != nullid:
935 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
935 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
936 ui.write("}\n")
936 ui.write("}\n")
937
937
938 def debuginstall(ui):
938 def debuginstall(ui):
939 '''test Mercurial installation'''
939 '''test Mercurial installation'''
940
940
941 def writetemp(contents):
941 def writetemp(contents):
942 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
942 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
943 f = os.fdopen(fd, "wb")
943 f = os.fdopen(fd, "wb")
944 f.write(contents)
944 f.write(contents)
945 f.close()
945 f.close()
946 return name
946 return name
947
947
948 problems = 0
948 problems = 0
949
949
950 # encoding
950 # encoding
951 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
951 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
952 try:
952 try:
953 encoding.fromlocal("test")
953 encoding.fromlocal("test")
954 except util.Abort, inst:
954 except util.Abort, inst:
955 ui.write(" %s\n" % inst)
955 ui.write(" %s\n" % inst)
956 ui.write(_(" (check that your locale is properly set)\n"))
956 ui.write(_(" (check that your locale is properly set)\n"))
957 problems += 1
957 problems += 1
958
958
959 # compiled modules
959 # compiled modules
960 ui.status(_("Checking extensions...\n"))
960 ui.status(_("Checking extensions...\n"))
961 try:
961 try:
962 import bdiff, mpatch, base85
962 import bdiff, mpatch, base85
963 except Exception, inst:
963 except Exception, inst:
964 ui.write(" %s\n" % inst)
964 ui.write(" %s\n" % inst)
965 ui.write(_(" One or more extensions could not be found"))
965 ui.write(_(" One or more extensions could not be found"))
966 ui.write(_(" (check that you compiled the extensions)\n"))
966 ui.write(_(" (check that you compiled the extensions)\n"))
967 problems += 1
967 problems += 1
968
968
969 # templates
969 # templates
970 ui.status(_("Checking templates...\n"))
970 ui.status(_("Checking templates...\n"))
971 try:
971 try:
972 import templater
972 import templater
973 templater.templater(templater.templatepath("map-cmdline.default"))
973 templater.templater(templater.templatepath("map-cmdline.default"))
974 except Exception, inst:
974 except Exception, inst:
975 ui.write(" %s\n" % inst)
975 ui.write(" %s\n" % inst)
976 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
976 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
977 problems += 1
977 problems += 1
978
978
979 # patch
979 # patch
980 ui.status(_("Checking patch...\n"))
980 ui.status(_("Checking patch...\n"))
981 patchproblems = 0
981 patchproblems = 0
982 a = "1\n2\n3\n4\n"
982 a = "1\n2\n3\n4\n"
983 b = "1\n2\n3\ninsert\n4\n"
983 b = "1\n2\n3\ninsert\n4\n"
984 fa = writetemp(a)
984 fa = writetemp(a)
985 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
985 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
986 os.path.basename(fa))
986 os.path.basename(fa))
987 fd = writetemp(d)
987 fd = writetemp(d)
988
988
989 files = {}
989 files = {}
990 try:
990 try:
991 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
991 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
992 except util.Abort, e:
992 except util.Abort, e:
993 ui.write(_(" patch call failed:\n"))
993 ui.write(_(" patch call failed:\n"))
994 ui.write(" " + str(e) + "\n")
994 ui.write(" " + str(e) + "\n")
995 patchproblems += 1
995 patchproblems += 1
996 else:
996 else:
997 if list(files) != [os.path.basename(fa)]:
997 if list(files) != [os.path.basename(fa)]:
998 ui.write(_(" unexpected patch output!\n"))
998 ui.write(_(" unexpected patch output!\n"))
999 patchproblems += 1
999 patchproblems += 1
1000 a = open(fa).read()
1000 a = open(fa).read()
1001 if a != b:
1001 if a != b:
1002 ui.write(_(" patch test failed!\n"))
1002 ui.write(_(" patch test failed!\n"))
1003 patchproblems += 1
1003 patchproblems += 1
1004
1004
1005 if patchproblems:
1005 if patchproblems:
1006 if ui.config('ui', 'patch'):
1006 if ui.config('ui', 'patch'):
1007 ui.write(_(" (Current patch tool may be incompatible with patch,"
1007 ui.write(_(" (Current patch tool may be incompatible with patch,"
1008 " or misconfigured. Please check your .hgrc file)\n"))
1008 " or misconfigured. Please check your .hgrc file)\n"))
1009 else:
1009 else:
1010 ui.write(_(" Internal patcher failure, please report this error"
1010 ui.write(_(" Internal patcher failure, please report this error"
1011 " to http://mercurial.selenic.com/bts/\n"))
1011 " to http://mercurial.selenic.com/bts/\n"))
1012 problems += patchproblems
1012 problems += patchproblems
1013
1013
1014 os.unlink(fa)
1014 os.unlink(fa)
1015 os.unlink(fd)
1015 os.unlink(fd)
1016
1016
1017 # editor
1017 # editor
1018 ui.status(_("Checking commit editor...\n"))
1018 ui.status(_("Checking commit editor...\n"))
1019 editor = ui.geteditor()
1019 editor = ui.geteditor()
1020 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1020 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1021 if not cmdpath:
1021 if not cmdpath:
1022 if editor == 'vi':
1022 if editor == 'vi':
1023 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1023 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1024 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1024 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1025 else:
1025 else:
1026 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1026 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1027 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1027 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1028 problems += 1
1028 problems += 1
1029
1029
1030 # check username
1030 # check username
1031 ui.status(_("Checking username...\n"))
1031 ui.status(_("Checking username...\n"))
1032 user = os.environ.get("HGUSER")
1032 user = os.environ.get("HGUSER")
1033 if user is None:
1033 if user is None:
1034 user = ui.config("ui", "username")
1034 user = ui.config("ui", "username")
1035 if user is None:
1035 if user is None:
1036 user = os.environ.get("EMAIL")
1036 user = os.environ.get("EMAIL")
1037 if not user:
1037 if not user:
1038 ui.warn(" ")
1038 ui.warn(" ")
1039 ui.username()
1039 ui.username()
1040 ui.write(_(" (specify a username in your .hgrc file)\n"))
1040 ui.write(_(" (specify a username in your .hgrc file)\n"))
1041
1041
1042 if not problems:
1042 if not problems:
1043 ui.status(_("No problems detected\n"))
1043 ui.status(_("No problems detected\n"))
1044 else:
1044 else:
1045 ui.write(_("%s problems detected,"
1045 ui.write(_("%s problems detected,"
1046 " please check your install!\n") % problems)
1046 " please check your install!\n") % problems)
1047
1047
1048 return problems
1048 return problems
1049
1049
1050 def debugrename(ui, repo, file1, *pats, **opts):
1050 def debugrename(ui, repo, file1, *pats, **opts):
1051 """dump rename information"""
1051 """dump rename information"""
1052
1052
1053 ctx = repo[opts.get('rev')]
1053 ctx = repo[opts.get('rev')]
1054 m = cmdutil.match(repo, (file1,) + pats, opts)
1054 m = cmdutil.match(repo, (file1,) + pats, opts)
1055 for abs in ctx.walk(m):
1055 for abs in ctx.walk(m):
1056 fctx = ctx[abs]
1056 fctx = ctx[abs]
1057 o = fctx.filelog().renamed(fctx.filenode())
1057 o = fctx.filelog().renamed(fctx.filenode())
1058 rel = m.rel(abs)
1058 rel = m.rel(abs)
1059 if o:
1059 if o:
1060 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1060 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1061 else:
1061 else:
1062 ui.write(_("%s not renamed\n") % rel)
1062 ui.write(_("%s not renamed\n") % rel)
1063
1063
1064 def debugwalk(ui, repo, *pats, **opts):
1064 def debugwalk(ui, repo, *pats, **opts):
1065 """show how files match on given patterns"""
1065 """show how files match on given patterns"""
1066 m = cmdutil.match(repo, pats, opts)
1066 m = cmdutil.match(repo, pats, opts)
1067 items = list(repo.walk(m))
1067 items = list(repo.walk(m))
1068 if not items:
1068 if not items:
1069 return
1069 return
1070 fmt = 'f %%-%ds %%-%ds %%s' % (
1070 fmt = 'f %%-%ds %%-%ds %%s' % (
1071 max([len(abs) for abs in items]),
1071 max([len(abs) for abs in items]),
1072 max([len(m.rel(abs)) for abs in items]))
1072 max([len(m.rel(abs)) for abs in items]))
1073 for abs in items:
1073 for abs in items:
1074 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1074 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1075 ui.write("%s\n" % line.rstrip())
1075 ui.write("%s\n" % line.rstrip())
1076
1076
1077 def diff(ui, repo, *pats, **opts):
1077 def diff(ui, repo, *pats, **opts):
1078 """diff repository (or selected files)
1078 """diff repository (or selected files)
1079
1079
1080 Show differences between revisions for the specified files.
1080 Show differences between revisions for the specified files.
1081
1081
1082 Differences between files are shown using the unified diff format.
1082 Differences between files are shown using the unified diff format.
1083
1083
1084 NOTE: diff may generate unexpected results for merges, as it will
1084 NOTE: diff may generate unexpected results for merges, as it will
1085 default to comparing against the working directory's first parent
1085 default to comparing against the working directory's first parent
1086 changeset if no revisions are specified.
1086 changeset if no revisions are specified.
1087
1087
1088 When two revision arguments are given, then changes are shown
1088 When two revision arguments are given, then changes are shown
1089 between those revisions. If only one revision is specified then
1089 between those revisions. If only one revision is specified then
1090 that revision is compared to the working directory, and, when no
1090 that revision is compared to the working directory, and, when no
1091 revisions are specified, the working directory files are compared
1091 revisions are specified, the working directory files are compared
1092 to its parent.
1092 to its parent.
1093
1093
1094 Without the -a/--text option, diff will avoid generating diffs of
1094 Without the -a/--text option, diff will avoid generating diffs of
1095 files it detects as binary. With -a, diff will generate a diff
1095 files it detects as binary. With -a, diff will generate a diff
1096 anyway, probably with undesirable results.
1096 anyway, probably with undesirable results.
1097
1097
1098 Use the -g/--git option to generate diffs in the git extended diff
1098 Use the -g/--git option to generate diffs in the git extended diff
1099 format. For more information, read 'hg help diffs'.
1099 format. For more information, read 'hg help diffs'.
1100 """
1100 """
1101
1101
1102 revs = opts.get('rev')
1102 revs = opts.get('rev')
1103 change = opts.get('change')
1103 change = opts.get('change')
1104 stat = opts.get('stat')
1104 stat = opts.get('stat')
1105 inv = opts.get('inverse')
1105
1106
1106 if revs and change:
1107 if revs and change:
1107 msg = _('cannot specify --rev and --change at the same time')
1108 msg = _('cannot specify --rev and --change at the same time')
1108 raise util.Abort(msg)
1109 raise util.Abort(msg)
1109 elif change:
1110 elif change:
1110 node2 = repo.lookup(change)
1111 node2 = repo.lookup(change)
1111 node1 = repo[node2].parents()[0].node()
1112 node1 = repo[node2].parents()[0].node()
1112 else:
1113 else:
1113 node1, node2 = cmdutil.revpair(repo, revs)
1114 node1, node2 = cmdutil.revpair(repo, revs)
1114
1115
1116 if inv:
1117 node1, node2 = node2, node1
1118
1115 if stat:
1119 if stat:
1116 opts['unified'] = '0'
1120 opts['unified'] = '0'
1117 diffopts = patch.diffopts(ui, opts)
1121 diffopts = patch.diffopts(ui, opts)
1118
1122
1119 m = cmdutil.match(repo, pats, opts)
1123 m = cmdutil.match(repo, pats, opts)
1120 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1124 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1121 if stat:
1125 if stat:
1122 width = ui.interactive() and util.termwidth() or 80
1126 width = ui.interactive() and util.termwidth() or 80
1123 ui.write(patch.diffstat(util.iterlines(it), width=width,
1127 ui.write(patch.diffstat(util.iterlines(it), width=width,
1124 git=diffopts.git))
1128 git=diffopts.git))
1125 else:
1129 else:
1126 for chunk in it:
1130 for chunk in it:
1127 ui.write(chunk)
1131 ui.write(chunk)
1128
1132
1129 def export(ui, repo, *changesets, **opts):
1133 def export(ui, repo, *changesets, **opts):
1130 """dump the header and diffs for one or more changesets
1134 """dump the header and diffs for one or more changesets
1131
1135
1132 Print the changeset header and diffs for one or more revisions.
1136 Print the changeset header and diffs for one or more revisions.
1133
1137
1134 The information shown in the changeset header is: author,
1138 The information shown in the changeset header is: author,
1135 changeset hash, parent(s) and commit comment.
1139 changeset hash, parent(s) and commit comment.
1136
1140
1137 NOTE: export may generate unexpected diff output for merge
1141 NOTE: export may generate unexpected diff output for merge
1138 changesets, as it will compare the merge changeset against its
1142 changesets, as it will compare the merge changeset against its
1139 first parent only.
1143 first parent only.
1140
1144
1141 Output may be to a file, in which case the name of the file is
1145 Output may be to a file, in which case the name of the file is
1142 given using a format string. The formatting rules are as follows::
1146 given using a format string. The formatting rules are as follows::
1143
1147
1144 %% literal "%" character
1148 %% literal "%" character
1145 %H changeset hash (40 bytes of hexadecimal)
1149 %H changeset hash (40 bytes of hexadecimal)
1146 %N number of patches being generated
1150 %N number of patches being generated
1147 %R changeset revision number
1151 %R changeset revision number
1148 %b basename of the exporting repository
1152 %b basename of the exporting repository
1149 %h short-form changeset hash (12 bytes of hexadecimal)
1153 %h short-form changeset hash (12 bytes of hexadecimal)
1150 %n zero-padded sequence number, starting at 1
1154 %n zero-padded sequence number, starting at 1
1151 %r zero-padded changeset revision number
1155 %r zero-padded changeset revision number
1152
1156
1153 Without the -a/--text option, export will avoid generating diffs
1157 Without the -a/--text option, export will avoid generating diffs
1154 of files it detects as binary. With -a, export will generate a
1158 of files it detects as binary. With -a, export will generate a
1155 diff anyway, probably with undesirable results.
1159 diff anyway, probably with undesirable results.
1156
1160
1157 Use the -g/--git option to generate diffs in the git extended diff
1161 Use the -g/--git option to generate diffs in the git extended diff
1158 format. See 'hg help diffs' for more information.
1162 format. See 'hg help diffs' for more information.
1159
1163
1160 With the --switch-parent option, the diff will be against the
1164 With the --switch-parent option, the diff will be against the
1161 second parent. It can be useful to review a merge.
1165 second parent. It can be useful to review a merge.
1162 """
1166 """
1163 if not changesets:
1167 if not changesets:
1164 raise util.Abort(_("export requires at least one changeset"))
1168 raise util.Abort(_("export requires at least one changeset"))
1165 revs = cmdutil.revrange(repo, changesets)
1169 revs = cmdutil.revrange(repo, changesets)
1166 if len(revs) > 1:
1170 if len(revs) > 1:
1167 ui.note(_('exporting patches:\n'))
1171 ui.note(_('exporting patches:\n'))
1168 else:
1172 else:
1169 ui.note(_('exporting patch:\n'))
1173 ui.note(_('exporting patch:\n'))
1170 patch.export(repo, revs, template=opts.get('output'),
1174 patch.export(repo, revs, template=opts.get('output'),
1171 switch_parent=opts.get('switch_parent'),
1175 switch_parent=opts.get('switch_parent'),
1172 opts=patch.diffopts(ui, opts))
1176 opts=patch.diffopts(ui, opts))
1173
1177
1174 def forget(ui, repo, *pats, **opts):
1178 def forget(ui, repo, *pats, **opts):
1175 """forget the specified files on the next commit
1179 """forget the specified files on the next commit
1176
1180
1177 Mark the specified files so they will no longer be tracked
1181 Mark the specified files so they will no longer be tracked
1178 after the next commit.
1182 after the next commit.
1179
1183
1180 This only removes files from the current branch, not from the
1184 This only removes files from the current branch, not from the
1181 entire project history, and it does not delete them from the
1185 entire project history, and it does not delete them from the
1182 working directory.
1186 working directory.
1183
1187
1184 To undo a forget before the next commit, see hg add.
1188 To undo a forget before the next commit, see hg add.
1185 """
1189 """
1186
1190
1187 if not pats:
1191 if not pats:
1188 raise util.Abort(_('no files specified'))
1192 raise util.Abort(_('no files specified'))
1189
1193
1190 m = cmdutil.match(repo, pats, opts)
1194 m = cmdutil.match(repo, pats, opts)
1191 s = repo.status(match=m, clean=True)
1195 s = repo.status(match=m, clean=True)
1192 forget = sorted(s[0] + s[1] + s[3] + s[6])
1196 forget = sorted(s[0] + s[1] + s[3] + s[6])
1193
1197
1194 for f in m.files():
1198 for f in m.files():
1195 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1199 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1196 ui.warn(_('not removing %s: file is already untracked\n')
1200 ui.warn(_('not removing %s: file is already untracked\n')
1197 % m.rel(f))
1201 % m.rel(f))
1198
1202
1199 for f in forget:
1203 for f in forget:
1200 if ui.verbose or not m.exact(f):
1204 if ui.verbose or not m.exact(f):
1201 ui.status(_('removing %s\n') % m.rel(f))
1205 ui.status(_('removing %s\n') % m.rel(f))
1202
1206
1203 repo.remove(forget, unlink=False)
1207 repo.remove(forget, unlink=False)
1204
1208
1205 def grep(ui, repo, pattern, *pats, **opts):
1209 def grep(ui, repo, pattern, *pats, **opts):
1206 """search for a pattern in specified files and revisions
1210 """search for a pattern in specified files and revisions
1207
1211
1208 Search revisions of files for a regular expression.
1212 Search revisions of files for a regular expression.
1209
1213
1210 This command behaves differently than Unix grep. It only accepts
1214 This command behaves differently than Unix grep. It only accepts
1211 Python/Perl regexps. It searches repository history, not the
1215 Python/Perl regexps. It searches repository history, not the
1212 working directory. It always prints the revision number in which a
1216 working directory. It always prints the revision number in which a
1213 match appears.
1217 match appears.
1214
1218
1215 By default, grep only prints output for the first revision of a
1219 By default, grep only prints output for the first revision of a
1216 file in which it finds a match. To get it to print every revision
1220 file in which it finds a match. To get it to print every revision
1217 that contains a change in match status ("-" for a match that
1221 that contains a change in match status ("-" for a match that
1218 becomes a non-match, or "+" for a non-match that becomes a match),
1222 becomes a non-match, or "+" for a non-match that becomes a match),
1219 use the --all flag.
1223 use the --all flag.
1220 """
1224 """
1221 reflags = 0
1225 reflags = 0
1222 if opts.get('ignore_case'):
1226 if opts.get('ignore_case'):
1223 reflags |= re.I
1227 reflags |= re.I
1224 try:
1228 try:
1225 regexp = re.compile(pattern, reflags)
1229 regexp = re.compile(pattern, reflags)
1226 except Exception, inst:
1230 except Exception, inst:
1227 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1231 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1228 return None
1232 return None
1229 sep, eol = ':', '\n'
1233 sep, eol = ':', '\n'
1230 if opts.get('print0'):
1234 if opts.get('print0'):
1231 sep = eol = '\0'
1235 sep = eol = '\0'
1232
1236
1233 getfile = util.lrucachefunc(repo.file)
1237 getfile = util.lrucachefunc(repo.file)
1234
1238
1235 def matchlines(body):
1239 def matchlines(body):
1236 begin = 0
1240 begin = 0
1237 linenum = 0
1241 linenum = 0
1238 while True:
1242 while True:
1239 match = regexp.search(body, begin)
1243 match = regexp.search(body, begin)
1240 if not match:
1244 if not match:
1241 break
1245 break
1242 mstart, mend = match.span()
1246 mstart, mend = match.span()
1243 linenum += body.count('\n', begin, mstart) + 1
1247 linenum += body.count('\n', begin, mstart) + 1
1244 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1248 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1245 begin = body.find('\n', mend) + 1 or len(body)
1249 begin = body.find('\n', mend) + 1 or len(body)
1246 lend = begin - 1
1250 lend = begin - 1
1247 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1251 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1248
1252
1249 class linestate(object):
1253 class linestate(object):
1250 def __init__(self, line, linenum, colstart, colend):
1254 def __init__(self, line, linenum, colstart, colend):
1251 self.line = line
1255 self.line = line
1252 self.linenum = linenum
1256 self.linenum = linenum
1253 self.colstart = colstart
1257 self.colstart = colstart
1254 self.colend = colend
1258 self.colend = colend
1255
1259
1256 def __hash__(self):
1260 def __hash__(self):
1257 return hash((self.linenum, self.line))
1261 return hash((self.linenum, self.line))
1258
1262
1259 def __eq__(self, other):
1263 def __eq__(self, other):
1260 return self.line == other.line
1264 return self.line == other.line
1261
1265
1262 matches = {}
1266 matches = {}
1263 copies = {}
1267 copies = {}
1264 def grepbody(fn, rev, body):
1268 def grepbody(fn, rev, body):
1265 matches[rev].setdefault(fn, [])
1269 matches[rev].setdefault(fn, [])
1266 m = matches[rev][fn]
1270 m = matches[rev][fn]
1267 for lnum, cstart, cend, line in matchlines(body):
1271 for lnum, cstart, cend, line in matchlines(body):
1268 s = linestate(line, lnum, cstart, cend)
1272 s = linestate(line, lnum, cstart, cend)
1269 m.append(s)
1273 m.append(s)
1270
1274
1271 def difflinestates(a, b):
1275 def difflinestates(a, b):
1272 sm = difflib.SequenceMatcher(None, a, b)
1276 sm = difflib.SequenceMatcher(None, a, b)
1273 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1277 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1274 if tag == 'insert':
1278 if tag == 'insert':
1275 for i in xrange(blo, bhi):
1279 for i in xrange(blo, bhi):
1276 yield ('+', b[i])
1280 yield ('+', b[i])
1277 elif tag == 'delete':
1281 elif tag == 'delete':
1278 for i in xrange(alo, ahi):
1282 for i in xrange(alo, ahi):
1279 yield ('-', a[i])
1283 yield ('-', a[i])
1280 elif tag == 'replace':
1284 elif tag == 'replace':
1281 for i in xrange(alo, ahi):
1285 for i in xrange(alo, ahi):
1282 yield ('-', a[i])
1286 yield ('-', a[i])
1283 for i in xrange(blo, bhi):
1287 for i in xrange(blo, bhi):
1284 yield ('+', b[i])
1288 yield ('+', b[i])
1285
1289
1286 def display(fn, ctx, pstates, states):
1290 def display(fn, ctx, pstates, states):
1287 rev = ctx.rev()
1291 rev = ctx.rev()
1288 datefunc = ui.quiet and util.shortdate or util.datestr
1292 datefunc = ui.quiet and util.shortdate or util.datestr
1289 found = False
1293 found = False
1290 filerevmatches = {}
1294 filerevmatches = {}
1291 if opts.get('all'):
1295 if opts.get('all'):
1292 iter = difflinestates(pstates, states)
1296 iter = difflinestates(pstates, states)
1293 else:
1297 else:
1294 iter = [('', l) for l in states]
1298 iter = [('', l) for l in states]
1295 for change, l in iter:
1299 for change, l in iter:
1296 cols = [fn, str(rev)]
1300 cols = [fn, str(rev)]
1297 if opts.get('line_number'):
1301 if opts.get('line_number'):
1298 cols.append(str(l.linenum))
1302 cols.append(str(l.linenum))
1299 if opts.get('all'):
1303 if opts.get('all'):
1300 cols.append(change)
1304 cols.append(change)
1301 if opts.get('user'):
1305 if opts.get('user'):
1302 cols.append(ui.shortuser(ctx.user()))
1306 cols.append(ui.shortuser(ctx.user()))
1303 if opts.get('date'):
1307 if opts.get('date'):
1304 cols.append(datefunc(ctx.date()))
1308 cols.append(datefunc(ctx.date()))
1305 if opts.get('files_with_matches'):
1309 if opts.get('files_with_matches'):
1306 c = (fn, rev)
1310 c = (fn, rev)
1307 if c in filerevmatches:
1311 if c in filerevmatches:
1308 continue
1312 continue
1309 filerevmatches[c] = 1
1313 filerevmatches[c] = 1
1310 else:
1314 else:
1311 cols.append(l.line)
1315 cols.append(l.line)
1312 ui.write(sep.join(cols), eol)
1316 ui.write(sep.join(cols), eol)
1313 found = True
1317 found = True
1314 return found
1318 return found
1315
1319
1316 skip = {}
1320 skip = {}
1317 revfiles = {}
1321 revfiles = {}
1318 matchfn = cmdutil.match(repo, pats, opts)
1322 matchfn = cmdutil.match(repo, pats, opts)
1319 found = False
1323 found = False
1320 follow = opts.get('follow')
1324 follow = opts.get('follow')
1321
1325
1322 def prep(ctx, fns):
1326 def prep(ctx, fns):
1323 rev = ctx.rev()
1327 rev = ctx.rev()
1324 pctx = ctx.parents()[0]
1328 pctx = ctx.parents()[0]
1325 parent = pctx.rev()
1329 parent = pctx.rev()
1326 matches.setdefault(rev, {})
1330 matches.setdefault(rev, {})
1327 matches.setdefault(parent, {})
1331 matches.setdefault(parent, {})
1328 files = revfiles.setdefault(rev, [])
1332 files = revfiles.setdefault(rev, [])
1329 for fn in fns:
1333 for fn in fns:
1330 flog = getfile(fn)
1334 flog = getfile(fn)
1331 try:
1335 try:
1332 fnode = ctx.filenode(fn)
1336 fnode = ctx.filenode(fn)
1333 except error.LookupError:
1337 except error.LookupError:
1334 continue
1338 continue
1335
1339
1336 copied = flog.renamed(fnode)
1340 copied = flog.renamed(fnode)
1337 copy = follow and copied and copied[0]
1341 copy = follow and copied and copied[0]
1338 if copy:
1342 if copy:
1339 copies.setdefault(rev, {})[fn] = copy
1343 copies.setdefault(rev, {})[fn] = copy
1340 if fn in skip:
1344 if fn in skip:
1341 if copy:
1345 if copy:
1342 skip[copy] = True
1346 skip[copy] = True
1343 continue
1347 continue
1344 files.append(fn)
1348 files.append(fn)
1345
1349
1346 if fn not in matches[rev]:
1350 if fn not in matches[rev]:
1347 grepbody(fn, rev, flog.read(fnode))
1351 grepbody(fn, rev, flog.read(fnode))
1348
1352
1349 pfn = copy or fn
1353 pfn = copy or fn
1350 if pfn not in matches[parent]:
1354 if pfn not in matches[parent]:
1351 try:
1355 try:
1352 fnode = pctx.filenode(pfn)
1356 fnode = pctx.filenode(pfn)
1353 grepbody(pfn, parent, flog.read(fnode))
1357 grepbody(pfn, parent, flog.read(fnode))
1354 except error.LookupError:
1358 except error.LookupError:
1355 pass
1359 pass
1356
1360
1357 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1361 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1358 rev = ctx.rev()
1362 rev = ctx.rev()
1359 parent = ctx.parents()[0].rev()
1363 parent = ctx.parents()[0].rev()
1360 for fn in sorted(revfiles.get(rev, [])):
1364 for fn in sorted(revfiles.get(rev, [])):
1361 states = matches[rev][fn]
1365 states = matches[rev][fn]
1362 copy = copies.get(rev, {}).get(fn)
1366 copy = copies.get(rev, {}).get(fn)
1363 if fn in skip:
1367 if fn in skip:
1364 if copy:
1368 if copy:
1365 skip[copy] = True
1369 skip[copy] = True
1366 continue
1370 continue
1367 pstates = matches.get(parent, {}).get(copy or fn, [])
1371 pstates = matches.get(parent, {}).get(copy or fn, [])
1368 if pstates or states:
1372 if pstates or states:
1369 r = display(fn, ctx, pstates, states)
1373 r = display(fn, ctx, pstates, states)
1370 found = found or r
1374 found = found or r
1371 if r and not opts.get('all'):
1375 if r and not opts.get('all'):
1372 skip[fn] = True
1376 skip[fn] = True
1373 if copy:
1377 if copy:
1374 skip[copy] = True
1378 skip[copy] = True
1375 del matches[rev]
1379 del matches[rev]
1376 del revfiles[rev]
1380 del revfiles[rev]
1377
1381
1378 def heads(ui, repo, *branchrevs, **opts):
1382 def heads(ui, repo, *branchrevs, **opts):
1379 """show current repository heads or show branch heads
1383 """show current repository heads or show branch heads
1380
1384
1381 With no arguments, show all repository head changesets.
1385 With no arguments, show all repository head changesets.
1382
1386
1383 Repository "heads" are changesets with no child changesets. They are
1387 Repository "heads" are changesets with no child changesets. They are
1384 where development generally takes place and are the usual targets
1388 where development generally takes place and are the usual targets
1385 for update and merge operations.
1389 for update and merge operations.
1386
1390
1387 If one or more REV is given, the "branch heads" will be shown for
1391 If one or more REV is given, the "branch heads" will be shown for
1388 the named branch associated with the specified changeset(s).
1392 the named branch associated with the specified changeset(s).
1389
1393
1390 Branch heads are changesets on a named branch with no descendants on
1394 Branch heads are changesets on a named branch with no descendants on
1391 the same branch. A branch head could be a "true" (repository) head,
1395 the same branch. A branch head could be a "true" (repository) head,
1392 or it could be the last changeset on that branch before it was
1396 or it could be the last changeset on that branch before it was
1393 merged into another branch, or it could be the last changeset on the
1397 merged into another branch, or it could be the last changeset on the
1394 branch before a new branch was created. If none of the branch heads
1398 branch before a new branch was created. If none of the branch heads
1395 are true heads, the branch is considered inactive.
1399 are true heads, the branch is considered inactive.
1396
1400
1397 If -c/--closed is specified, also show branch heads marked closed
1401 If -c/--closed is specified, also show branch heads marked closed
1398 (see hg commit --close-branch).
1402 (see hg commit --close-branch).
1399
1403
1400 If STARTREV is specified, only those heads that are descendants of
1404 If STARTREV is specified, only those heads that are descendants of
1401 STARTREV will be displayed.
1405 STARTREV will be displayed.
1402 """
1406 """
1403 if opts.get('rev'):
1407 if opts.get('rev'):
1404 start = repo.lookup(opts['rev'])
1408 start = repo.lookup(opts['rev'])
1405 else:
1409 else:
1406 start = None
1410 start = None
1407 closed = opts.get('closed')
1411 closed = opts.get('closed')
1408 hideinactive, _heads = opts.get('active'), None
1412 hideinactive, _heads = opts.get('active'), None
1409 if not branchrevs:
1413 if not branchrevs:
1410 if closed:
1414 if closed:
1411 raise error.Abort(_('you must specify a branch to use --closed'))
1415 raise error.Abort(_('you must specify a branch to use --closed'))
1412 # Assume we're looking repo-wide heads if no revs were specified.
1416 # Assume we're looking repo-wide heads if no revs were specified.
1413 heads = repo.heads(start)
1417 heads = repo.heads(start)
1414 else:
1418 else:
1415 if hideinactive:
1419 if hideinactive:
1416 _heads = repo.heads(start)
1420 _heads = repo.heads(start)
1417 heads = []
1421 heads = []
1418 visitedset = set()
1422 visitedset = set()
1419 for branchrev in branchrevs:
1423 for branchrev in branchrevs:
1420 branch = repo[encoding.fromlocal(branchrev)].branch()
1424 branch = repo[encoding.fromlocal(branchrev)].branch()
1421 encodedbranch = encoding.tolocal(branch)
1425 encodedbranch = encoding.tolocal(branch)
1422 if branch in visitedset:
1426 if branch in visitedset:
1423 continue
1427 continue
1424 visitedset.add(branch)
1428 visitedset.add(branch)
1425 bheads = repo.branchheads(branch, start, closed=closed)
1429 bheads = repo.branchheads(branch, start, closed=closed)
1426 if not bheads:
1430 if not bheads:
1427 if not opts.get('rev'):
1431 if not opts.get('rev'):
1428 ui.warn(_("no open branch heads on branch %s\n") % encodedbranch)
1432 ui.warn(_("no open branch heads on branch %s\n") % encodedbranch)
1429 elif branch != branchrev:
1433 elif branch != branchrev:
1430 ui.warn(_("no changes on branch %s containing %s are "
1434 ui.warn(_("no changes on branch %s containing %s are "
1431 "reachable from %s\n")
1435 "reachable from %s\n")
1432 % (encodedbranch, branchrev, opts.get('rev')))
1436 % (encodedbranch, branchrev, opts.get('rev')))
1433 else:
1437 else:
1434 ui.warn(_("no changes on branch %s are reachable from %s\n")
1438 ui.warn(_("no changes on branch %s are reachable from %s\n")
1435 % (encodedbranch, opts.get('rev')))
1439 % (encodedbranch, opts.get('rev')))
1436 if hideinactive:
1440 if hideinactive:
1437 bheads = [bhead for bhead in bheads if bhead in _heads]
1441 bheads = [bhead for bhead in bheads if bhead in _heads]
1438 heads.extend(bheads)
1442 heads.extend(bheads)
1439 if not heads:
1443 if not heads:
1440 return 1
1444 return 1
1441 displayer = cmdutil.show_changeset(ui, repo, opts)
1445 displayer = cmdutil.show_changeset(ui, repo, opts)
1442 for n in heads:
1446 for n in heads:
1443 displayer.show(repo[n])
1447 displayer.show(repo[n])
1444
1448
1445 def help_(ui, name=None, with_version=False):
1449 def help_(ui, name=None, with_version=False):
1446 """show help for a given topic or a help overview
1450 """show help for a given topic or a help overview
1447
1451
1448 With no arguments, print a list of commands with short help messages.
1452 With no arguments, print a list of commands with short help messages.
1449
1453
1450 Given a topic, extension, or command name, print help for that
1454 Given a topic, extension, or command name, print help for that
1451 topic."""
1455 topic."""
1452 option_lists = []
1456 option_lists = []
1453 textwidth = util.termwidth() - 2
1457 textwidth = util.termwidth() - 2
1454
1458
1455 def addglobalopts(aliases):
1459 def addglobalopts(aliases):
1456 if ui.verbose:
1460 if ui.verbose:
1457 option_lists.append((_("global options:"), globalopts))
1461 option_lists.append((_("global options:"), globalopts))
1458 if name == 'shortlist':
1462 if name == 'shortlist':
1459 option_lists.append((_('use "hg help" for the full list '
1463 option_lists.append((_('use "hg help" for the full list '
1460 'of commands'), ()))
1464 'of commands'), ()))
1461 else:
1465 else:
1462 if name == 'shortlist':
1466 if name == 'shortlist':
1463 msg = _('use "hg help" for the full list of commands '
1467 msg = _('use "hg help" for the full list of commands '
1464 'or "hg -v" for details')
1468 'or "hg -v" for details')
1465 elif aliases:
1469 elif aliases:
1466 msg = _('use "hg -v help%s" to show aliases and '
1470 msg = _('use "hg -v help%s" to show aliases and '
1467 'global options') % (name and " " + name or "")
1471 'global options') % (name and " " + name or "")
1468 else:
1472 else:
1469 msg = _('use "hg -v help %s" to show global options') % name
1473 msg = _('use "hg -v help %s" to show global options') % name
1470 option_lists.append((msg, ()))
1474 option_lists.append((msg, ()))
1471
1475
1472 def helpcmd(name):
1476 def helpcmd(name):
1473 if with_version:
1477 if with_version:
1474 version_(ui)
1478 version_(ui)
1475 ui.write('\n')
1479 ui.write('\n')
1476
1480
1477 try:
1481 try:
1478 aliases, i = cmdutil.findcmd(name, table, False)
1482 aliases, i = cmdutil.findcmd(name, table, False)
1479 except error.AmbiguousCommand, inst:
1483 except error.AmbiguousCommand, inst:
1480 # py3k fix: except vars can't be used outside the scope of the
1484 # py3k fix: except vars can't be used outside the scope of the
1481 # except block, nor can be used inside a lambda. python issue4617
1485 # except block, nor can be used inside a lambda. python issue4617
1482 prefix = inst.args[0]
1486 prefix = inst.args[0]
1483 select = lambda c: c.lstrip('^').startswith(prefix)
1487 select = lambda c: c.lstrip('^').startswith(prefix)
1484 helplist(_('list of commands:\n\n'), select)
1488 helplist(_('list of commands:\n\n'), select)
1485 return
1489 return
1486
1490
1487 # synopsis
1491 # synopsis
1488 if len(i) > 2:
1492 if len(i) > 2:
1489 if i[2].startswith('hg'):
1493 if i[2].startswith('hg'):
1490 ui.write("%s\n" % i[2])
1494 ui.write("%s\n" % i[2])
1491 else:
1495 else:
1492 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1496 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1493 else:
1497 else:
1494 ui.write('hg %s\n' % aliases[0])
1498 ui.write('hg %s\n' % aliases[0])
1495
1499
1496 # aliases
1500 # aliases
1497 if not ui.quiet and len(aliases) > 1:
1501 if not ui.quiet and len(aliases) > 1:
1498 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1502 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1499
1503
1500 # description
1504 # description
1501 doc = gettext(i[0].__doc__)
1505 doc = gettext(i[0].__doc__)
1502 if not doc:
1506 if not doc:
1503 doc = _("(no help text available)")
1507 doc = _("(no help text available)")
1504 if ui.quiet:
1508 if ui.quiet:
1505 doc = doc.splitlines()[0]
1509 doc = doc.splitlines()[0]
1506 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1510 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1507
1511
1508 if not ui.quiet:
1512 if not ui.quiet:
1509 # options
1513 # options
1510 if i[1]:
1514 if i[1]:
1511 option_lists.append((_("options:\n"), i[1]))
1515 option_lists.append((_("options:\n"), i[1]))
1512
1516
1513 addglobalopts(False)
1517 addglobalopts(False)
1514
1518
1515 def helplist(header, select=None):
1519 def helplist(header, select=None):
1516 h = {}
1520 h = {}
1517 cmds = {}
1521 cmds = {}
1518 for c, e in table.iteritems():
1522 for c, e in table.iteritems():
1519 f = c.split("|", 1)[0]
1523 f = c.split("|", 1)[0]
1520 if select and not select(f):
1524 if select and not select(f):
1521 continue
1525 continue
1522 if (not select and name != 'shortlist' and
1526 if (not select and name != 'shortlist' and
1523 e[0].__module__ != __name__):
1527 e[0].__module__ != __name__):
1524 continue
1528 continue
1525 if name == "shortlist" and not f.startswith("^"):
1529 if name == "shortlist" and not f.startswith("^"):
1526 continue
1530 continue
1527 f = f.lstrip("^")
1531 f = f.lstrip("^")
1528 if not ui.debugflag and f.startswith("debug"):
1532 if not ui.debugflag and f.startswith("debug"):
1529 continue
1533 continue
1530 doc = e[0].__doc__
1534 doc = e[0].__doc__
1531 if doc and 'DEPRECATED' in doc and not ui.verbose:
1535 if doc and 'DEPRECATED' in doc and not ui.verbose:
1532 continue
1536 continue
1533 doc = gettext(doc)
1537 doc = gettext(doc)
1534 if not doc:
1538 if not doc:
1535 doc = _("(no help text available)")
1539 doc = _("(no help text available)")
1536 h[f] = doc.splitlines()[0].rstrip()
1540 h[f] = doc.splitlines()[0].rstrip()
1537 cmds[f] = c.lstrip("^")
1541 cmds[f] = c.lstrip("^")
1538
1542
1539 if not h:
1543 if not h:
1540 ui.status(_('no commands defined\n'))
1544 ui.status(_('no commands defined\n'))
1541 return
1545 return
1542
1546
1543 ui.status(header)
1547 ui.status(header)
1544 fns = sorted(h)
1548 fns = sorted(h)
1545 m = max(map(len, fns))
1549 m = max(map(len, fns))
1546 for f in fns:
1550 for f in fns:
1547 if ui.verbose:
1551 if ui.verbose:
1548 commands = cmds[f].replace("|",", ")
1552 commands = cmds[f].replace("|",", ")
1549 ui.write(" %s:\n %s\n"%(commands, h[f]))
1553 ui.write(" %s:\n %s\n"%(commands, h[f]))
1550 else:
1554 else:
1551 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1555 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1552
1556
1553 if name != 'shortlist':
1557 if name != 'shortlist':
1554 exts, maxlength = extensions.enabled()
1558 exts, maxlength = extensions.enabled()
1555 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1559 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1556 if text:
1560 if text:
1557 ui.write("\n%s\n" % minirst.format(text, textwidth))
1561 ui.write("\n%s\n" % minirst.format(text, textwidth))
1558
1562
1559 if not ui.quiet:
1563 if not ui.quiet:
1560 addglobalopts(True)
1564 addglobalopts(True)
1561
1565
1562 def helptopic(name):
1566 def helptopic(name):
1563 for names, header, doc in help.helptable:
1567 for names, header, doc in help.helptable:
1564 if name in names:
1568 if name in names:
1565 break
1569 break
1566 else:
1570 else:
1567 raise error.UnknownCommand(name)
1571 raise error.UnknownCommand(name)
1568
1572
1569 # description
1573 # description
1570 if not doc:
1574 if not doc:
1571 doc = _("(no help text available)")
1575 doc = _("(no help text available)")
1572 if hasattr(doc, '__call__'):
1576 if hasattr(doc, '__call__'):
1573 doc = doc()
1577 doc = doc()
1574
1578
1575 ui.write("%s\n\n" % header)
1579 ui.write("%s\n\n" % header)
1576 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1580 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1577
1581
1578 def helpext(name):
1582 def helpext(name):
1579 try:
1583 try:
1580 mod = extensions.find(name)
1584 mod = extensions.find(name)
1581 except KeyError:
1585 except KeyError:
1582 raise error.UnknownCommand(name)
1586 raise error.UnknownCommand(name)
1583
1587
1584 doc = gettext(mod.__doc__) or _('no help text available')
1588 doc = gettext(mod.__doc__) or _('no help text available')
1585 if '\n' not in doc:
1589 if '\n' not in doc:
1586 head, tail = doc, ""
1590 head, tail = doc, ""
1587 else:
1591 else:
1588 head, tail = doc.split('\n', 1)
1592 head, tail = doc.split('\n', 1)
1589 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1593 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1590 if tail:
1594 if tail:
1591 ui.write(minirst.format(tail, textwidth))
1595 ui.write(minirst.format(tail, textwidth))
1592 ui.status('\n\n')
1596 ui.status('\n\n')
1593
1597
1594 try:
1598 try:
1595 ct = mod.cmdtable
1599 ct = mod.cmdtable
1596 except AttributeError:
1600 except AttributeError:
1597 ct = {}
1601 ct = {}
1598
1602
1599 modcmds = set([c.split('|', 1)[0] for c in ct])
1603 modcmds = set([c.split('|', 1)[0] for c in ct])
1600 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1604 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1601
1605
1602 if name and name != 'shortlist':
1606 if name and name != 'shortlist':
1603 i = None
1607 i = None
1604 for f in (helptopic, helpcmd, helpext):
1608 for f in (helptopic, helpcmd, helpext):
1605 try:
1609 try:
1606 f(name)
1610 f(name)
1607 i = None
1611 i = None
1608 break
1612 break
1609 except error.UnknownCommand, inst:
1613 except error.UnknownCommand, inst:
1610 i = inst
1614 i = inst
1611 if i:
1615 if i:
1612 raise i
1616 raise i
1613
1617
1614 else:
1618 else:
1615 # program name
1619 # program name
1616 if ui.verbose or with_version:
1620 if ui.verbose or with_version:
1617 version_(ui)
1621 version_(ui)
1618 else:
1622 else:
1619 ui.status(_("Mercurial Distributed SCM\n"))
1623 ui.status(_("Mercurial Distributed SCM\n"))
1620 ui.status('\n')
1624 ui.status('\n')
1621
1625
1622 # list of commands
1626 # list of commands
1623 if name == "shortlist":
1627 if name == "shortlist":
1624 header = _('basic commands:\n\n')
1628 header = _('basic commands:\n\n')
1625 else:
1629 else:
1626 header = _('list of commands:\n\n')
1630 header = _('list of commands:\n\n')
1627
1631
1628 helplist(header)
1632 helplist(header)
1629
1633
1630 # list all option lists
1634 # list all option lists
1631 opt_output = []
1635 opt_output = []
1632 for title, options in option_lists:
1636 for title, options in option_lists:
1633 opt_output.append(("\n%s" % title, None))
1637 opt_output.append(("\n%s" % title, None))
1634 for shortopt, longopt, default, desc in options:
1638 for shortopt, longopt, default, desc in options:
1635 if _("DEPRECATED") in desc and not ui.verbose: continue
1639 if _("DEPRECATED") in desc and not ui.verbose: continue
1636 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1640 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1637 longopt and " --%s" % longopt),
1641 longopt and " --%s" % longopt),
1638 "%s%s" % (desc,
1642 "%s%s" % (desc,
1639 default
1643 default
1640 and _(" (default: %s)") % default
1644 and _(" (default: %s)") % default
1641 or "")))
1645 or "")))
1642
1646
1643 if not name:
1647 if not name:
1644 ui.write(_("\nadditional help topics:\n\n"))
1648 ui.write(_("\nadditional help topics:\n\n"))
1645 topics = []
1649 topics = []
1646 for names, header, doc in help.helptable:
1650 for names, header, doc in help.helptable:
1647 topics.append((sorted(names, key=len, reverse=True)[0], header))
1651 topics.append((sorted(names, key=len, reverse=True)[0], header))
1648 topics_len = max([len(s[0]) for s in topics])
1652 topics_len = max([len(s[0]) for s in topics])
1649 for t, desc in topics:
1653 for t, desc in topics:
1650 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1654 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1651
1655
1652 if opt_output:
1656 if opt_output:
1653 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1657 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1654 for first, second in opt_output:
1658 for first, second in opt_output:
1655 if second:
1659 if second:
1656 second = util.wrap(second, opts_len + 3)
1660 second = util.wrap(second, opts_len + 3)
1657 ui.write(" %-*s %s\n" % (opts_len, first, second))
1661 ui.write(" %-*s %s\n" % (opts_len, first, second))
1658 else:
1662 else:
1659 ui.write("%s\n" % first)
1663 ui.write("%s\n" % first)
1660
1664
1661 def identify(ui, repo, source=None,
1665 def identify(ui, repo, source=None,
1662 rev=None, num=None, id=None, branch=None, tags=None):
1666 rev=None, num=None, id=None, branch=None, tags=None):
1663 """identify the working copy or specified revision
1667 """identify the working copy or specified revision
1664
1668
1665 With no revision, print a summary of the current state of the
1669 With no revision, print a summary of the current state of the
1666 repository.
1670 repository.
1667
1671
1668 Specifying a path to a repository root or Mercurial bundle will
1672 Specifying a path to a repository root or Mercurial bundle will
1669 cause lookup to operate on that repository/bundle.
1673 cause lookup to operate on that repository/bundle.
1670
1674
1671 This summary identifies the repository state using one or two
1675 This summary identifies the repository state using one or two
1672 parent hash identifiers, followed by a "+" if there are
1676 parent hash identifiers, followed by a "+" if there are
1673 uncommitted changes in the working directory, a list of tags for
1677 uncommitted changes in the working directory, a list of tags for
1674 this revision and a branch name for non-default branches.
1678 this revision and a branch name for non-default branches.
1675 """
1679 """
1676
1680
1677 if not repo and not source:
1681 if not repo and not source:
1678 raise util.Abort(_("There is no Mercurial repository here "
1682 raise util.Abort(_("There is no Mercurial repository here "
1679 "(.hg not found)"))
1683 "(.hg not found)"))
1680
1684
1681 hexfunc = ui.debugflag and hex or short
1685 hexfunc = ui.debugflag and hex or short
1682 default = not (num or id or branch or tags)
1686 default = not (num or id or branch or tags)
1683 output = []
1687 output = []
1684
1688
1685 revs = []
1689 revs = []
1686 if source:
1690 if source:
1687 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1691 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1688 repo = hg.repository(ui, source)
1692 repo = hg.repository(ui, source)
1689
1693
1690 if not repo.local():
1694 if not repo.local():
1691 if not rev and revs:
1695 if not rev and revs:
1692 rev = revs[0]
1696 rev = revs[0]
1693 if not rev:
1697 if not rev:
1694 rev = "tip"
1698 rev = "tip"
1695 if num or branch or tags:
1699 if num or branch or tags:
1696 raise util.Abort(
1700 raise util.Abort(
1697 "can't query remote revision number, branch, or tags")
1701 "can't query remote revision number, branch, or tags")
1698 output = [hexfunc(repo.lookup(rev))]
1702 output = [hexfunc(repo.lookup(rev))]
1699 elif not rev:
1703 elif not rev:
1700 ctx = repo[None]
1704 ctx = repo[None]
1701 parents = ctx.parents()
1705 parents = ctx.parents()
1702 changed = False
1706 changed = False
1703 if default or id or num:
1707 if default or id or num:
1704 changed = ctx.files() + ctx.deleted()
1708 changed = ctx.files() + ctx.deleted()
1705 if default or id:
1709 if default or id:
1706 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1710 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1707 (changed) and "+" or "")]
1711 (changed) and "+" or "")]
1708 if num:
1712 if num:
1709 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1713 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1710 (changed) and "+" or ""))
1714 (changed) and "+" or ""))
1711 else:
1715 else:
1712 ctx = repo[rev]
1716 ctx = repo[rev]
1713 if default or id:
1717 if default or id:
1714 output = [hexfunc(ctx.node())]
1718 output = [hexfunc(ctx.node())]
1715 if num:
1719 if num:
1716 output.append(str(ctx.rev()))
1720 output.append(str(ctx.rev()))
1717
1721
1718 if repo.local() and default and not ui.quiet:
1722 if repo.local() and default and not ui.quiet:
1719 b = encoding.tolocal(ctx.branch())
1723 b = encoding.tolocal(ctx.branch())
1720 if b != 'default':
1724 if b != 'default':
1721 output.append("(%s)" % b)
1725 output.append("(%s)" % b)
1722
1726
1723 # multiple tags for a single parent separated by '/'
1727 # multiple tags for a single parent separated by '/'
1724 t = "/".join(ctx.tags())
1728 t = "/".join(ctx.tags())
1725 if t:
1729 if t:
1726 output.append(t)
1730 output.append(t)
1727
1731
1728 if branch:
1732 if branch:
1729 output.append(encoding.tolocal(ctx.branch()))
1733 output.append(encoding.tolocal(ctx.branch()))
1730
1734
1731 if tags:
1735 if tags:
1732 output.extend(ctx.tags())
1736 output.extend(ctx.tags())
1733
1737
1734 ui.write("%s\n" % ' '.join(output))
1738 ui.write("%s\n" % ' '.join(output))
1735
1739
1736 def import_(ui, repo, patch1, *patches, **opts):
1740 def import_(ui, repo, patch1, *patches, **opts):
1737 """import an ordered set of patches
1741 """import an ordered set of patches
1738
1742
1739 Import a list of patches and commit them individually (unless
1743 Import a list of patches and commit them individually (unless
1740 --no-commit is specified).
1744 --no-commit is specified).
1741
1745
1742 If there are outstanding changes in the working directory, import
1746 If there are outstanding changes in the working directory, import
1743 will abort unless given the -f/--force flag.
1747 will abort unless given the -f/--force flag.
1744
1748
1745 You can import a patch straight from a mail message. Even patches
1749 You can import a patch straight from a mail message. Even patches
1746 as attachments work (to use the body part, it must have type
1750 as attachments work (to use the body part, it must have type
1747 text/plain or text/x-patch). From and Subject headers of email
1751 text/plain or text/x-patch). From and Subject headers of email
1748 message are used as default committer and commit message. All
1752 message are used as default committer and commit message. All
1749 text/plain body parts before first diff are added to commit
1753 text/plain body parts before first diff are added to commit
1750 message.
1754 message.
1751
1755
1752 If the imported patch was generated by hg export, user and
1756 If the imported patch was generated by hg export, user and
1753 description from patch override values from message headers and
1757 description from patch override values from message headers and
1754 body. Values given on command line with -m/--message and -u/--user
1758 body. Values given on command line with -m/--message and -u/--user
1755 override these.
1759 override these.
1756
1760
1757 If --exact is specified, import will set the working directory to
1761 If --exact is specified, import will set the working directory to
1758 the parent of each patch before applying it, and will abort if the
1762 the parent of each patch before applying it, and will abort if the
1759 resulting changeset has a different ID than the one recorded in
1763 resulting changeset has a different ID than the one recorded in
1760 the patch. This may happen due to character set problems or other
1764 the patch. This may happen due to character set problems or other
1761 deficiencies in the text patch format.
1765 deficiencies in the text patch format.
1762
1766
1763 With -s/--similarity, hg will attempt to discover renames and
1767 With -s/--similarity, hg will attempt to discover renames and
1764 copies in the patch in the same way as 'addremove'.
1768 copies in the patch in the same way as 'addremove'.
1765
1769
1766 To read a patch from standard input, use "-" as the patch name. If
1770 To read a patch from standard input, use "-" as the patch name. If
1767 a URL is specified, the patch will be downloaded from it.
1771 a URL is specified, the patch will be downloaded from it.
1768 See 'hg help dates' for a list of formats valid for -d/--date.
1772 See 'hg help dates' for a list of formats valid for -d/--date.
1769 """
1773 """
1770 patches = (patch1,) + patches
1774 patches = (patch1,) + patches
1771
1775
1772 date = opts.get('date')
1776 date = opts.get('date')
1773 if date:
1777 if date:
1774 opts['date'] = util.parsedate(date)
1778 opts['date'] = util.parsedate(date)
1775
1779
1776 try:
1780 try:
1777 sim = float(opts.get('similarity') or 0)
1781 sim = float(opts.get('similarity') or 0)
1778 except ValueError:
1782 except ValueError:
1779 raise util.Abort(_('similarity must be a number'))
1783 raise util.Abort(_('similarity must be a number'))
1780 if sim < 0 or sim > 100:
1784 if sim < 0 or sim > 100:
1781 raise util.Abort(_('similarity must be between 0 and 100'))
1785 raise util.Abort(_('similarity must be between 0 and 100'))
1782
1786
1783 if opts.get('exact') or not opts.get('force'):
1787 if opts.get('exact') or not opts.get('force'):
1784 cmdutil.bail_if_changed(repo)
1788 cmdutil.bail_if_changed(repo)
1785
1789
1786 d = opts["base"]
1790 d = opts["base"]
1787 strip = opts["strip"]
1791 strip = opts["strip"]
1788 wlock = lock = None
1792 wlock = lock = None
1789 try:
1793 try:
1790 wlock = repo.wlock()
1794 wlock = repo.wlock()
1791 lock = repo.lock()
1795 lock = repo.lock()
1792 for p in patches:
1796 for p in patches:
1793 pf = os.path.join(d, p)
1797 pf = os.path.join(d, p)
1794
1798
1795 if pf == '-':
1799 if pf == '-':
1796 ui.status(_("applying patch from stdin\n"))
1800 ui.status(_("applying patch from stdin\n"))
1797 pf = sys.stdin
1801 pf = sys.stdin
1798 else:
1802 else:
1799 ui.status(_("applying %s\n") % p)
1803 ui.status(_("applying %s\n") % p)
1800 pf = url.open(ui, pf)
1804 pf = url.open(ui, pf)
1801 data = patch.extract(ui, pf)
1805 data = patch.extract(ui, pf)
1802 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1806 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1803
1807
1804 if tmpname is None:
1808 if tmpname is None:
1805 raise util.Abort(_('no diffs found'))
1809 raise util.Abort(_('no diffs found'))
1806
1810
1807 try:
1811 try:
1808 cmdline_message = cmdutil.logmessage(opts)
1812 cmdline_message = cmdutil.logmessage(opts)
1809 if cmdline_message:
1813 if cmdline_message:
1810 # pickup the cmdline msg
1814 # pickup the cmdline msg
1811 message = cmdline_message
1815 message = cmdline_message
1812 elif message:
1816 elif message:
1813 # pickup the patch msg
1817 # pickup the patch msg
1814 message = message.strip()
1818 message = message.strip()
1815 else:
1819 else:
1816 # launch the editor
1820 # launch the editor
1817 message = None
1821 message = None
1818 ui.debug('message:\n%s\n' % message)
1822 ui.debug('message:\n%s\n' % message)
1819
1823
1820 wp = repo.parents()
1824 wp = repo.parents()
1821 if opts.get('exact'):
1825 if opts.get('exact'):
1822 if not nodeid or not p1:
1826 if not nodeid or not p1:
1823 raise util.Abort(_('not a Mercurial patch'))
1827 raise util.Abort(_('not a Mercurial patch'))
1824 p1 = repo.lookup(p1)
1828 p1 = repo.lookup(p1)
1825 p2 = repo.lookup(p2 or hex(nullid))
1829 p2 = repo.lookup(p2 or hex(nullid))
1826
1830
1827 if p1 != wp[0].node():
1831 if p1 != wp[0].node():
1828 hg.clean(repo, p1)
1832 hg.clean(repo, p1)
1829 repo.dirstate.setparents(p1, p2)
1833 repo.dirstate.setparents(p1, p2)
1830 elif p2:
1834 elif p2:
1831 try:
1835 try:
1832 p1 = repo.lookup(p1)
1836 p1 = repo.lookup(p1)
1833 p2 = repo.lookup(p2)
1837 p2 = repo.lookup(p2)
1834 if p1 == wp[0].node():
1838 if p1 == wp[0].node():
1835 repo.dirstate.setparents(p1, p2)
1839 repo.dirstate.setparents(p1, p2)
1836 except error.RepoError:
1840 except error.RepoError:
1837 pass
1841 pass
1838 if opts.get('exact') or opts.get('import_branch'):
1842 if opts.get('exact') or opts.get('import_branch'):
1839 repo.dirstate.setbranch(branch or 'default')
1843 repo.dirstate.setbranch(branch or 'default')
1840
1844
1841 files = {}
1845 files = {}
1842 try:
1846 try:
1843 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1847 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1844 files=files, eolmode=None)
1848 files=files, eolmode=None)
1845 finally:
1849 finally:
1846 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1850 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1847 if not opts.get('no_commit'):
1851 if not opts.get('no_commit'):
1848 m = cmdutil.matchfiles(repo, files or [])
1852 m = cmdutil.matchfiles(repo, files or [])
1849 n = repo.commit(message, opts.get('user') or user,
1853 n = repo.commit(message, opts.get('user') or user,
1850 opts.get('date') or date, match=m,
1854 opts.get('date') or date, match=m,
1851 editor=cmdutil.commiteditor)
1855 editor=cmdutil.commiteditor)
1852 if opts.get('exact'):
1856 if opts.get('exact'):
1853 if hex(n) != nodeid:
1857 if hex(n) != nodeid:
1854 repo.rollback()
1858 repo.rollback()
1855 raise util.Abort(_('patch is damaged'
1859 raise util.Abort(_('patch is damaged'
1856 ' or loses information'))
1860 ' or loses information'))
1857 # Force a dirstate write so that the next transaction
1861 # Force a dirstate write so that the next transaction
1858 # backups an up-do-date file.
1862 # backups an up-do-date file.
1859 repo.dirstate.write()
1863 repo.dirstate.write()
1860 finally:
1864 finally:
1861 os.unlink(tmpname)
1865 os.unlink(tmpname)
1862 finally:
1866 finally:
1863 release(lock, wlock)
1867 release(lock, wlock)
1864
1868
1865 def incoming(ui, repo, source="default", **opts):
1869 def incoming(ui, repo, source="default", **opts):
1866 """show new changesets found in source
1870 """show new changesets found in source
1867
1871
1868 Show new changesets found in the specified path/URL or the default
1872 Show new changesets found in the specified path/URL or the default
1869 pull location. These are the changesets that would have been pulled
1873 pull location. These are the changesets that would have been pulled
1870 if a pull at the time you issued this command.
1874 if a pull at the time you issued this command.
1871
1875
1872 For remote repository, using --bundle avoids downloading the
1876 For remote repository, using --bundle avoids downloading the
1873 changesets twice if the incoming is followed by a pull.
1877 changesets twice if the incoming is followed by a pull.
1874
1878
1875 See pull for valid source format details.
1879 See pull for valid source format details.
1876 """
1880 """
1877 limit = cmdutil.loglimit(opts)
1881 limit = cmdutil.loglimit(opts)
1878 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1882 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1879 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1883 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1880 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1884 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1881 if revs:
1885 if revs:
1882 revs = [other.lookup(rev) for rev in revs]
1886 revs = [other.lookup(rev) for rev in revs]
1883 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1887 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1884 force=opts["force"])
1888 force=opts["force"])
1885 if not incoming:
1889 if not incoming:
1886 try:
1890 try:
1887 os.unlink(opts["bundle"])
1891 os.unlink(opts["bundle"])
1888 except:
1892 except:
1889 pass
1893 pass
1890 ui.status(_("no changes found\n"))
1894 ui.status(_("no changes found\n"))
1891 return 1
1895 return 1
1892
1896
1893 cleanup = None
1897 cleanup = None
1894 try:
1898 try:
1895 fname = opts["bundle"]
1899 fname = opts["bundle"]
1896 if fname or not other.local():
1900 if fname or not other.local():
1897 # create a bundle (uncompressed if other repo is not local)
1901 # create a bundle (uncompressed if other repo is not local)
1898
1902
1899 if revs is None and other.capable('changegroupsubset'):
1903 if revs is None and other.capable('changegroupsubset'):
1900 revs = rheads
1904 revs = rheads
1901
1905
1902 if revs is None:
1906 if revs is None:
1903 cg = other.changegroup(incoming, "incoming")
1907 cg = other.changegroup(incoming, "incoming")
1904 else:
1908 else:
1905 cg = other.changegroupsubset(incoming, revs, 'incoming')
1909 cg = other.changegroupsubset(incoming, revs, 'incoming')
1906 bundletype = other.local() and "HG10BZ" or "HG10UN"
1910 bundletype = other.local() and "HG10BZ" or "HG10UN"
1907 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1911 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1908 # keep written bundle?
1912 # keep written bundle?
1909 if opts["bundle"]:
1913 if opts["bundle"]:
1910 cleanup = None
1914 cleanup = None
1911 if not other.local():
1915 if not other.local():
1912 # use the created uncompressed bundlerepo
1916 # use the created uncompressed bundlerepo
1913 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1917 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1914
1918
1915 o = other.changelog.nodesbetween(incoming, revs)[0]
1919 o = other.changelog.nodesbetween(incoming, revs)[0]
1916 if opts.get('newest_first'):
1920 if opts.get('newest_first'):
1917 o.reverse()
1921 o.reverse()
1918 displayer = cmdutil.show_changeset(ui, other, opts)
1922 displayer = cmdutil.show_changeset(ui, other, opts)
1919 count = 0
1923 count = 0
1920 for n in o:
1924 for n in o:
1921 if count >= limit:
1925 if count >= limit:
1922 break
1926 break
1923 parents = [p for p in other.changelog.parents(n) if p != nullid]
1927 parents = [p for p in other.changelog.parents(n) if p != nullid]
1924 if opts.get('no_merges') and len(parents) == 2:
1928 if opts.get('no_merges') and len(parents) == 2:
1925 continue
1929 continue
1926 count += 1
1930 count += 1
1927 displayer.show(other[n])
1931 displayer.show(other[n])
1928 finally:
1932 finally:
1929 if hasattr(other, 'close'):
1933 if hasattr(other, 'close'):
1930 other.close()
1934 other.close()
1931 if cleanup:
1935 if cleanup:
1932 os.unlink(cleanup)
1936 os.unlink(cleanup)
1933
1937
1934 def init(ui, dest=".", **opts):
1938 def init(ui, dest=".", **opts):
1935 """create a new repository in the given directory
1939 """create a new repository in the given directory
1936
1940
1937 Initialize a new repository in the given directory. If the given
1941 Initialize a new repository in the given directory. If the given
1938 directory does not exist, it will be created.
1942 directory does not exist, it will be created.
1939
1943
1940 If no directory is given, the current directory is used.
1944 If no directory is given, the current directory is used.
1941
1945
1942 It is possible to specify an ssh:// URL as the destination.
1946 It is possible to specify an ssh:// URL as the destination.
1943 See 'hg help urls' for more information.
1947 See 'hg help urls' for more information.
1944 """
1948 """
1945 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1949 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1946
1950
1947 def locate(ui, repo, *pats, **opts):
1951 def locate(ui, repo, *pats, **opts):
1948 """locate files matching specific patterns
1952 """locate files matching specific patterns
1949
1953
1950 Print files under Mercurial control in the working directory whose
1954 Print files under Mercurial control in the working directory whose
1951 names match the given patterns.
1955 names match the given patterns.
1952
1956
1953 By default, this command searches all directories in the working
1957 By default, this command searches all directories in the working
1954 directory. To search just the current directory and its
1958 directory. To search just the current directory and its
1955 subdirectories, use "--include .".
1959 subdirectories, use "--include .".
1956
1960
1957 If no patterns are given to match, this command prints the names
1961 If no patterns are given to match, this command prints the names
1958 of all files under Mercurial control in the working directory.
1962 of all files under Mercurial control in the working directory.
1959
1963
1960 If you want to feed the output of this command into the "xargs"
1964 If you want to feed the output of this command into the "xargs"
1961 command, use the -0 option to both this command and "xargs". This
1965 command, use the -0 option to both this command and "xargs". This
1962 will avoid the problem of "xargs" treating single filenames that
1966 will avoid the problem of "xargs" treating single filenames that
1963 contain whitespace as multiple filenames.
1967 contain whitespace as multiple filenames.
1964 """
1968 """
1965 end = opts.get('print0') and '\0' or '\n'
1969 end = opts.get('print0') and '\0' or '\n'
1966 rev = opts.get('rev') or None
1970 rev = opts.get('rev') or None
1967
1971
1968 ret = 1
1972 ret = 1
1969 m = cmdutil.match(repo, pats, opts, default='relglob')
1973 m = cmdutil.match(repo, pats, opts, default='relglob')
1970 m.bad = lambda x,y: False
1974 m.bad = lambda x,y: False
1971 for abs in repo[rev].walk(m):
1975 for abs in repo[rev].walk(m):
1972 if not rev and abs not in repo.dirstate:
1976 if not rev and abs not in repo.dirstate:
1973 continue
1977 continue
1974 if opts.get('fullpath'):
1978 if opts.get('fullpath'):
1975 ui.write(repo.wjoin(abs), end)
1979 ui.write(repo.wjoin(abs), end)
1976 else:
1980 else:
1977 ui.write(((pats and m.rel(abs)) or abs), end)
1981 ui.write(((pats and m.rel(abs)) or abs), end)
1978 ret = 0
1982 ret = 0
1979
1983
1980 return ret
1984 return ret
1981
1985
1982 def log(ui, repo, *pats, **opts):
1986 def log(ui, repo, *pats, **opts):
1983 """show revision history of entire repository or files
1987 """show revision history of entire repository or files
1984
1988
1985 Print the revision history of the specified files or the entire
1989 Print the revision history of the specified files or the entire
1986 project.
1990 project.
1987
1991
1988 File history is shown without following rename or copy history of
1992 File history is shown without following rename or copy history of
1989 files. Use -f/--follow with a filename to follow history across
1993 files. Use -f/--follow with a filename to follow history across
1990 renames and copies. --follow without a filename will only show
1994 renames and copies. --follow without a filename will only show
1991 ancestors or descendants of the starting revision. --follow-first
1995 ancestors or descendants of the starting revision. --follow-first
1992 only follows the first parent of merge revisions.
1996 only follows the first parent of merge revisions.
1993
1997
1994 If no revision range is specified, the default is tip:0 unless
1998 If no revision range is specified, the default is tip:0 unless
1995 --follow is set, in which case the working directory parent is
1999 --follow is set, in which case the working directory parent is
1996 used as the starting revision.
2000 used as the starting revision.
1997
2001
1998 See 'hg help dates' for a list of formats valid for -d/--date.
2002 See 'hg help dates' for a list of formats valid for -d/--date.
1999
2003
2000 By default this command prints revision number and changeset id,
2004 By default this command prints revision number and changeset id,
2001 tags, non-trivial parents, user, date and time, and a summary for
2005 tags, non-trivial parents, user, date and time, and a summary for
2002 each commit. When the -v/--verbose switch is used, the list of
2006 each commit. When the -v/--verbose switch is used, the list of
2003 changed files and full commit message are shown.
2007 changed files and full commit message are shown.
2004
2008
2005 NOTE: log -p/--patch may generate unexpected diff output for merge
2009 NOTE: log -p/--patch may generate unexpected diff output for merge
2006 changesets, as it will only compare the merge changeset against
2010 changesets, as it will only compare the merge changeset against
2007 its first parent. Also, only files different from BOTH parents
2011 its first parent. Also, only files different from BOTH parents
2008 will appear in files:.
2012 will appear in files:.
2009 """
2013 """
2010
2014
2011 matchfn = cmdutil.match(repo, pats, opts)
2015 matchfn = cmdutil.match(repo, pats, opts)
2012 limit = cmdutil.loglimit(opts)
2016 limit = cmdutil.loglimit(opts)
2013 count = 0
2017 count = 0
2014
2018
2015 if opts.get('copies') and opts.get('rev'):
2019 if opts.get('copies') and opts.get('rev'):
2016 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2020 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2017 else:
2021 else:
2018 endrev = len(repo)
2022 endrev = len(repo)
2019 rcache = {}
2023 rcache = {}
2020 ncache = {}
2024 ncache = {}
2021 def getrenamed(fn, rev):
2025 def getrenamed(fn, rev):
2022 '''looks up all renames for a file (up to endrev) the first
2026 '''looks up all renames for a file (up to endrev) the first
2023 time the file is given. It indexes on the changerev and only
2027 time the file is given. It indexes on the changerev and only
2024 parses the manifest if linkrev != changerev.
2028 parses the manifest if linkrev != changerev.
2025 Returns rename info for fn at changerev rev.'''
2029 Returns rename info for fn at changerev rev.'''
2026 if fn not in rcache:
2030 if fn not in rcache:
2027 rcache[fn] = {}
2031 rcache[fn] = {}
2028 ncache[fn] = {}
2032 ncache[fn] = {}
2029 fl = repo.file(fn)
2033 fl = repo.file(fn)
2030 for i in fl:
2034 for i in fl:
2031 node = fl.node(i)
2035 node = fl.node(i)
2032 lr = fl.linkrev(i)
2036 lr = fl.linkrev(i)
2033 renamed = fl.renamed(node)
2037 renamed = fl.renamed(node)
2034 rcache[fn][lr] = renamed
2038 rcache[fn][lr] = renamed
2035 if renamed:
2039 if renamed:
2036 ncache[fn][node] = renamed
2040 ncache[fn][node] = renamed
2037 if lr >= endrev:
2041 if lr >= endrev:
2038 break
2042 break
2039 if rev in rcache[fn]:
2043 if rev in rcache[fn]:
2040 return rcache[fn][rev]
2044 return rcache[fn][rev]
2041
2045
2042 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2046 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2043 # filectx logic.
2047 # filectx logic.
2044
2048
2045 try:
2049 try:
2046 return repo[rev][fn].renamed()
2050 return repo[rev][fn].renamed()
2047 except error.LookupError:
2051 except error.LookupError:
2048 pass
2052 pass
2049 return None
2053 return None
2050
2054
2051 df = False
2055 df = False
2052 if opts["date"]:
2056 if opts["date"]:
2053 df = util.matchdate(opts["date"])
2057 df = util.matchdate(opts["date"])
2054
2058
2055 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2059 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2056 def prep(ctx, fns):
2060 def prep(ctx, fns):
2057 rev = ctx.rev()
2061 rev = ctx.rev()
2058 parents = [p for p in repo.changelog.parentrevs(rev)
2062 parents = [p for p in repo.changelog.parentrevs(rev)
2059 if p != nullrev]
2063 if p != nullrev]
2060 if opts.get('no_merges') and len(parents) == 2:
2064 if opts.get('no_merges') and len(parents) == 2:
2061 return
2065 return
2062 if opts.get('only_merges') and len(parents) != 2:
2066 if opts.get('only_merges') and len(parents) != 2:
2063 return
2067 return
2064 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2068 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2065 return
2069 return
2066 if df and not df(ctx.date()[0]):
2070 if df and not df(ctx.date()[0]):
2067 return
2071 return
2068 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2072 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2069 return
2073 return
2070 if opts.get('keyword'):
2074 if opts.get('keyword'):
2071 for k in [kw.lower() for kw in opts['keyword']]:
2075 for k in [kw.lower() for kw in opts['keyword']]:
2072 if (k in ctx.user().lower() or
2076 if (k in ctx.user().lower() or
2073 k in ctx.description().lower() or
2077 k in ctx.description().lower() or
2074 k in " ".join(ctx.files()).lower()):
2078 k in " ".join(ctx.files()).lower()):
2075 break
2079 break
2076 else:
2080 else:
2077 return
2081 return
2078
2082
2079 copies = []
2083 copies = []
2080 if opts.get('copies') and rev:
2084 if opts.get('copies') and rev:
2081 for fn in ctx.files():
2085 for fn in ctx.files():
2082 rename = getrenamed(fn, rev)
2086 rename = getrenamed(fn, rev)
2083 if rename:
2087 if rename:
2084 copies.append((fn, rename[0]))
2088 copies.append((fn, rename[0]))
2085
2089
2086 displayer.show(ctx, copies=copies)
2090 displayer.show(ctx, copies=copies)
2087
2091
2088 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2092 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2089 if count == limit:
2093 if count == limit:
2090 break
2094 break
2091 if displayer.flush(ctx.rev()):
2095 if displayer.flush(ctx.rev()):
2092 count += 1
2096 count += 1
2093
2097
2094 def manifest(ui, repo, node=None, rev=None):
2098 def manifest(ui, repo, node=None, rev=None):
2095 """output the current or given revision of the project manifest
2099 """output the current or given revision of the project manifest
2096
2100
2097 Print a list of version controlled files for the given revision.
2101 Print a list of version controlled files for the given revision.
2098 If no revision is given, the first parent of the working directory
2102 If no revision is given, the first parent of the working directory
2099 is used, or the null revision if no revision is checked out.
2103 is used, or the null revision if no revision is checked out.
2100
2104
2101 With -v, print file permissions, symlink and executable bits.
2105 With -v, print file permissions, symlink and executable bits.
2102 With --debug, print file revision hashes.
2106 With --debug, print file revision hashes.
2103 """
2107 """
2104
2108
2105 if rev and node:
2109 if rev and node:
2106 raise util.Abort(_("please specify just one revision"))
2110 raise util.Abort(_("please specify just one revision"))
2107
2111
2108 if not node:
2112 if not node:
2109 node = rev
2113 node = rev
2110
2114
2111 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2115 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2112 ctx = repo[node]
2116 ctx = repo[node]
2113 for f in ctx:
2117 for f in ctx:
2114 if ui.debugflag:
2118 if ui.debugflag:
2115 ui.write("%40s " % hex(ctx.manifest()[f]))
2119 ui.write("%40s " % hex(ctx.manifest()[f]))
2116 if ui.verbose:
2120 if ui.verbose:
2117 ui.write(decor[ctx.flags(f)])
2121 ui.write(decor[ctx.flags(f)])
2118 ui.write("%s\n" % f)
2122 ui.write("%s\n" % f)
2119
2123
2120 def merge(ui, repo, node=None, **opts):
2124 def merge(ui, repo, node=None, **opts):
2121 """merge working directory with another revision
2125 """merge working directory with another revision
2122
2126
2123 The current working directory is updated with all changes made in
2127 The current working directory is updated with all changes made in
2124 the requested revision since the last common predecessor revision.
2128 the requested revision since the last common predecessor revision.
2125
2129
2126 Files that changed between either parent are marked as changed for
2130 Files that changed between either parent are marked as changed for
2127 the next commit and a commit must be performed before any further
2131 the next commit and a commit must be performed before any further
2128 updates to the repository are allowed. The next commit will have
2132 updates to the repository are allowed. The next commit will have
2129 two parents.
2133 two parents.
2130
2134
2131 If no revision is specified, the working directory's parent is a
2135 If no revision is specified, the working directory's parent is a
2132 head revision, and the current branch contains exactly one other
2136 head revision, and the current branch contains exactly one other
2133 head, the other head is merged with by default. Otherwise, an
2137 head, the other head is merged with by default. Otherwise, an
2134 explicit revision with which to merge with must be provided.
2138 explicit revision with which to merge with must be provided.
2135 """
2139 """
2136
2140
2137 if opts.get('rev') and node:
2141 if opts.get('rev') and node:
2138 raise util.Abort(_("please specify just one revision"))
2142 raise util.Abort(_("please specify just one revision"))
2139 if not node:
2143 if not node:
2140 node = opts.get('rev')
2144 node = opts.get('rev')
2141
2145
2142 if not node:
2146 if not node:
2143 branch = repo.changectx(None).branch()
2147 branch = repo.changectx(None).branch()
2144 bheads = repo.branchheads(branch)
2148 bheads = repo.branchheads(branch)
2145 if len(bheads) > 2:
2149 if len(bheads) > 2:
2146 raise util.Abort(_("branch '%s' has %d heads - "
2150 raise util.Abort(_("branch '%s' has %d heads - "
2147 "please merge with an explicit rev") %
2151 "please merge with an explicit rev") %
2148 (branch, len(bheads)))
2152 (branch, len(bheads)))
2149
2153
2150 parent = repo.dirstate.parents()[0]
2154 parent = repo.dirstate.parents()[0]
2151 if len(bheads) == 1:
2155 if len(bheads) == 1:
2152 if len(repo.heads()) > 1:
2156 if len(repo.heads()) > 1:
2153 raise util.Abort(_("branch '%s' has one head - "
2157 raise util.Abort(_("branch '%s' has one head - "
2154 "please merge with an explicit rev") %
2158 "please merge with an explicit rev") %
2155 branch)
2159 branch)
2156 msg = _('there is nothing to merge')
2160 msg = _('there is nothing to merge')
2157 if parent != repo.lookup(repo[None].branch()):
2161 if parent != repo.lookup(repo[None].branch()):
2158 msg = _('%s - use "hg update" instead') % msg
2162 msg = _('%s - use "hg update" instead') % msg
2159 raise util.Abort(msg)
2163 raise util.Abort(msg)
2160
2164
2161 if parent not in bheads:
2165 if parent not in bheads:
2162 raise util.Abort(_('working dir not at a head rev - '
2166 raise util.Abort(_('working dir not at a head rev - '
2163 'use "hg update" or merge with an explicit rev'))
2167 'use "hg update" or merge with an explicit rev'))
2164 node = parent == bheads[0] and bheads[-1] or bheads[0]
2168 node = parent == bheads[0] and bheads[-1] or bheads[0]
2165
2169
2166 if opts.get('preview'):
2170 if opts.get('preview'):
2167 p1 = repo['.']
2171 p1 = repo['.']
2168 p2 = repo[node]
2172 p2 = repo[node]
2169 common = p1.ancestor(p2)
2173 common = p1.ancestor(p2)
2170 roots, heads = [common.node()], [p2.node()]
2174 roots, heads = [common.node()], [p2.node()]
2171 displayer = cmdutil.show_changeset(ui, repo, opts)
2175 displayer = cmdutil.show_changeset(ui, repo, opts)
2172 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2176 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2173 if node not in roots:
2177 if node not in roots:
2174 displayer.show(repo[node])
2178 displayer.show(repo[node])
2175 return 0
2179 return 0
2176
2180
2177 return hg.merge(repo, node, force=opts.get('force'))
2181 return hg.merge(repo, node, force=opts.get('force'))
2178
2182
2179 def outgoing(ui, repo, dest=None, **opts):
2183 def outgoing(ui, repo, dest=None, **opts):
2180 """show changesets not found in destination
2184 """show changesets not found in destination
2181
2185
2182 Show changesets not found in the specified destination repository
2186 Show changesets not found in the specified destination repository
2183 or the default push location. These are the changesets that would
2187 or the default push location. These are the changesets that would
2184 be pushed if a push was requested.
2188 be pushed if a push was requested.
2185
2189
2186 See pull for valid destination format details.
2190 See pull for valid destination format details.
2187 """
2191 """
2188 limit = cmdutil.loglimit(opts)
2192 limit = cmdutil.loglimit(opts)
2189 dest, revs, checkout = hg.parseurl(
2193 dest, revs, checkout = hg.parseurl(
2190 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2194 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2191 if revs:
2195 if revs:
2192 revs = [repo.lookup(rev) for rev in revs]
2196 revs = [repo.lookup(rev) for rev in revs]
2193
2197
2194 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2198 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2195 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2199 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2196 o = repo.findoutgoing(other, force=opts.get('force'))
2200 o = repo.findoutgoing(other, force=opts.get('force'))
2197 if not o:
2201 if not o:
2198 ui.status(_("no changes found\n"))
2202 ui.status(_("no changes found\n"))
2199 return 1
2203 return 1
2200 o = repo.changelog.nodesbetween(o, revs)[0]
2204 o = repo.changelog.nodesbetween(o, revs)[0]
2201 if opts.get('newest_first'):
2205 if opts.get('newest_first'):
2202 o.reverse()
2206 o.reverse()
2203 displayer = cmdutil.show_changeset(ui, repo, opts)
2207 displayer = cmdutil.show_changeset(ui, repo, opts)
2204 count = 0
2208 count = 0
2205 for n in o:
2209 for n in o:
2206 if count >= limit:
2210 if count >= limit:
2207 break
2211 break
2208 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2212 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2209 if opts.get('no_merges') and len(parents) == 2:
2213 if opts.get('no_merges') and len(parents) == 2:
2210 continue
2214 continue
2211 count += 1
2215 count += 1
2212 displayer.show(repo[n])
2216 displayer.show(repo[n])
2213
2217
2214 def parents(ui, repo, file_=None, **opts):
2218 def parents(ui, repo, file_=None, **opts):
2215 """show the parents of the working directory or revision
2219 """show the parents of the working directory or revision
2216
2220
2217 Print the working directory's parent revisions. If a revision is
2221 Print the working directory's parent revisions. If a revision is
2218 given via -r/--rev, the parent of that revision will be printed.
2222 given via -r/--rev, the parent of that revision will be printed.
2219 If a file argument is given, the revision in which the file was
2223 If a file argument is given, the revision in which the file was
2220 last changed (before the working directory revision or the
2224 last changed (before the working directory revision or the
2221 argument to --rev if given) is printed.
2225 argument to --rev if given) is printed.
2222 """
2226 """
2223 rev = opts.get('rev')
2227 rev = opts.get('rev')
2224 if rev:
2228 if rev:
2225 ctx = repo[rev]
2229 ctx = repo[rev]
2226 else:
2230 else:
2227 ctx = repo[None]
2231 ctx = repo[None]
2228
2232
2229 if file_:
2233 if file_:
2230 m = cmdutil.match(repo, (file_,), opts)
2234 m = cmdutil.match(repo, (file_,), opts)
2231 if m.anypats() or len(m.files()) != 1:
2235 if m.anypats() or len(m.files()) != 1:
2232 raise util.Abort(_('can only specify an explicit filename'))
2236 raise util.Abort(_('can only specify an explicit filename'))
2233 file_ = m.files()[0]
2237 file_ = m.files()[0]
2234 filenodes = []
2238 filenodes = []
2235 for cp in ctx.parents():
2239 for cp in ctx.parents():
2236 if not cp:
2240 if not cp:
2237 continue
2241 continue
2238 try:
2242 try:
2239 filenodes.append(cp.filenode(file_))
2243 filenodes.append(cp.filenode(file_))
2240 except error.LookupError:
2244 except error.LookupError:
2241 pass
2245 pass
2242 if not filenodes:
2246 if not filenodes:
2243 raise util.Abort(_("'%s' not found in manifest!") % file_)
2247 raise util.Abort(_("'%s' not found in manifest!") % file_)
2244 fl = repo.file(file_)
2248 fl = repo.file(file_)
2245 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2249 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2246 else:
2250 else:
2247 p = [cp.node() for cp in ctx.parents()]
2251 p = [cp.node() for cp in ctx.parents()]
2248
2252
2249 displayer = cmdutil.show_changeset(ui, repo, opts)
2253 displayer = cmdutil.show_changeset(ui, repo, opts)
2250 for n in p:
2254 for n in p:
2251 if n != nullid:
2255 if n != nullid:
2252 displayer.show(repo[n])
2256 displayer.show(repo[n])
2253
2257
2254 def paths(ui, repo, search=None):
2258 def paths(ui, repo, search=None):
2255 """show aliases for remote repositories
2259 """show aliases for remote repositories
2256
2260
2257 Show definition of symbolic path name NAME. If no name is given,
2261 Show definition of symbolic path name NAME. If no name is given,
2258 show definition of all available names.
2262 show definition of all available names.
2259
2263
2260 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2264 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2261 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2265 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2262
2266
2263 See 'hg help urls' for more information.
2267 See 'hg help urls' for more information.
2264 """
2268 """
2265 if search:
2269 if search:
2266 for name, path in ui.configitems("paths"):
2270 for name, path in ui.configitems("paths"):
2267 if name == search:
2271 if name == search:
2268 ui.write("%s\n" % url.hidepassword(path))
2272 ui.write("%s\n" % url.hidepassword(path))
2269 return
2273 return
2270 ui.warn(_("not found!\n"))
2274 ui.warn(_("not found!\n"))
2271 return 1
2275 return 1
2272 else:
2276 else:
2273 for name, path in ui.configitems("paths"):
2277 for name, path in ui.configitems("paths"):
2274 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2278 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2275
2279
2276 def postincoming(ui, repo, modheads, optupdate, checkout):
2280 def postincoming(ui, repo, modheads, optupdate, checkout):
2277 if modheads == 0:
2281 if modheads == 0:
2278 return
2282 return
2279 if optupdate:
2283 if optupdate:
2280 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2284 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2281 return hg.update(repo, checkout)
2285 return hg.update(repo, checkout)
2282 else:
2286 else:
2283 ui.status(_("not updating, since new heads added\n"))
2287 ui.status(_("not updating, since new heads added\n"))
2284 if modheads > 1:
2288 if modheads > 1:
2285 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2289 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2286 else:
2290 else:
2287 ui.status(_("(run 'hg update' to get a working copy)\n"))
2291 ui.status(_("(run 'hg update' to get a working copy)\n"))
2288
2292
2289 def pull(ui, repo, source="default", **opts):
2293 def pull(ui, repo, source="default", **opts):
2290 """pull changes from the specified source
2294 """pull changes from the specified source
2291
2295
2292 Pull changes from a remote repository to a local one.
2296 Pull changes from a remote repository to a local one.
2293
2297
2294 This finds all changes from the repository at the specified path
2298 This finds all changes from the repository at the specified path
2295 or URL and adds them to a local repository (the current one unless
2299 or URL and adds them to a local repository (the current one unless
2296 -R is specified). By default, this does not update the copy of the
2300 -R is specified). By default, this does not update the copy of the
2297 project in the working directory.
2301 project in the working directory.
2298
2302
2299 Use hg incoming if you want to see what would have been added by a
2303 Use hg incoming if you want to see what would have been added by a
2300 pull at the time you issued this command. If you then decide to
2304 pull at the time you issued this command. If you then decide to
2301 added those changes to the repository, you should use pull -r X
2305 added those changes to the repository, you should use pull -r X
2302 where X is the last changeset listed by hg incoming.
2306 where X is the last changeset listed by hg incoming.
2303
2307
2304 If SOURCE is omitted, the 'default' path will be used.
2308 If SOURCE is omitted, the 'default' path will be used.
2305 See 'hg help urls' for more information.
2309 See 'hg help urls' for more information.
2306 """
2310 """
2307 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2311 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2308 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2312 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2309 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2313 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2310 if revs:
2314 if revs:
2311 try:
2315 try:
2312 revs = [other.lookup(rev) for rev in revs]
2316 revs = [other.lookup(rev) for rev in revs]
2313 except error.CapabilityError:
2317 except error.CapabilityError:
2314 err = _("Other repository doesn't support revision lookup, "
2318 err = _("Other repository doesn't support revision lookup, "
2315 "so a rev cannot be specified.")
2319 "so a rev cannot be specified.")
2316 raise util.Abort(err)
2320 raise util.Abort(err)
2317
2321
2318 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2322 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2319 if checkout:
2323 if checkout:
2320 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2324 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2321 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2325 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2322
2326
2323 def push(ui, repo, dest=None, **opts):
2327 def push(ui, repo, dest=None, **opts):
2324 """push changes to the specified destination
2328 """push changes to the specified destination
2325
2329
2326 Push changes from the local repository to the given destination.
2330 Push changes from the local repository to the given destination.
2327
2331
2328 This is the symmetrical operation for pull. It moves changes from
2332 This is the symmetrical operation for pull. It moves changes from
2329 the current repository to a different one. If the destination is
2333 the current repository to a different one. If the destination is
2330 local this is identical to a pull in that directory from the
2334 local this is identical to a pull in that directory from the
2331 current one.
2335 current one.
2332
2336
2333 By default, push will refuse to run if it detects the result would
2337 By default, push will refuse to run if it detects the result would
2334 increase the number of remote heads. This generally indicates the
2338 increase the number of remote heads. This generally indicates the
2335 user forgot to pull and merge before pushing.
2339 user forgot to pull and merge before pushing.
2336
2340
2337 If -r/--rev is used, the named revision and all its ancestors will
2341 If -r/--rev is used, the named revision and all its ancestors will
2338 be pushed to the remote repository.
2342 be pushed to the remote repository.
2339
2343
2340 Please see 'hg help urls' for important details about ssh://
2344 Please see 'hg help urls' for important details about ssh://
2341 URLs. If DESTINATION is omitted, a default path will be used.
2345 URLs. If DESTINATION is omitted, a default path will be used.
2342 """
2346 """
2343 dest, revs, checkout = hg.parseurl(
2347 dest, revs, checkout = hg.parseurl(
2344 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2348 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2345 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2349 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2346 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2350 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2347 if revs:
2351 if revs:
2348 revs = [repo.lookup(rev) for rev in revs]
2352 revs = [repo.lookup(rev) for rev in revs]
2349
2353
2350 # push subrepos depth-first for coherent ordering
2354 # push subrepos depth-first for coherent ordering
2351 c = repo['']
2355 c = repo['']
2352 subs = c.substate # only repos that are committed
2356 subs = c.substate # only repos that are committed
2353 for s in sorted(subs):
2357 for s in sorted(subs):
2354 c.sub(s).push(opts.get('force'))
2358 c.sub(s).push(opts.get('force'))
2355
2359
2356 r = repo.push(other, opts.get('force'), revs=revs)
2360 r = repo.push(other, opts.get('force'), revs=revs)
2357 return r == 0
2361 return r == 0
2358
2362
2359 def recover(ui, repo):
2363 def recover(ui, repo):
2360 """roll back an interrupted transaction
2364 """roll back an interrupted transaction
2361
2365
2362 Recover from an interrupted commit or pull.
2366 Recover from an interrupted commit or pull.
2363
2367
2364 This command tries to fix the repository status after an
2368 This command tries to fix the repository status after an
2365 interrupted operation. It should only be necessary when Mercurial
2369 interrupted operation. It should only be necessary when Mercurial
2366 suggests it.
2370 suggests it.
2367 """
2371 """
2368 if repo.recover():
2372 if repo.recover():
2369 return hg.verify(repo)
2373 return hg.verify(repo)
2370 return 1
2374 return 1
2371
2375
2372 def remove(ui, repo, *pats, **opts):
2376 def remove(ui, repo, *pats, **opts):
2373 """remove the specified files on the next commit
2377 """remove the specified files on the next commit
2374
2378
2375 Schedule the indicated files for removal from the repository.
2379 Schedule the indicated files for removal from the repository.
2376
2380
2377 This only removes files from the current branch, not from the
2381 This only removes files from the current branch, not from the
2378 entire project history. -A/--after can be used to remove only
2382 entire project history. -A/--after can be used to remove only
2379 files that have already been deleted, -f/--force can be used to
2383 files that have already been deleted, -f/--force can be used to
2380 force deletion, and -Af can be used to remove files from the next
2384 force deletion, and -Af can be used to remove files from the next
2381 revision without deleting them from the working directory.
2385 revision without deleting them from the working directory.
2382
2386
2383 The following table details the behavior of remove for different
2387 The following table details the behavior of remove for different
2384 file states (columns) and option combinations (rows). The file
2388 file states (columns) and option combinations (rows). The file
2385 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2389 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2386 reported by hg status). The actions are Warn, Remove (from branch)
2390 reported by hg status). The actions are Warn, Remove (from branch)
2387 and Delete (from disk)::
2391 and Delete (from disk)::
2388
2392
2389 A C M !
2393 A C M !
2390 none W RD W R
2394 none W RD W R
2391 -f R RD RD R
2395 -f R RD RD R
2392 -A W W W R
2396 -A W W W R
2393 -Af R R R R
2397 -Af R R R R
2394
2398
2395 This command schedules the files to be removed at the next commit.
2399 This command schedules the files to be removed at the next commit.
2396 To undo a remove before that, see hg revert.
2400 To undo a remove before that, see hg revert.
2397 """
2401 """
2398
2402
2399 after, force = opts.get('after'), opts.get('force')
2403 after, force = opts.get('after'), opts.get('force')
2400 if not pats and not after:
2404 if not pats and not after:
2401 raise util.Abort(_('no files specified'))
2405 raise util.Abort(_('no files specified'))
2402
2406
2403 m = cmdutil.match(repo, pats, opts)
2407 m = cmdutil.match(repo, pats, opts)
2404 s = repo.status(match=m, clean=True)
2408 s = repo.status(match=m, clean=True)
2405 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2409 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2406
2410
2407 for f in m.files():
2411 for f in m.files():
2408 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2412 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2409 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2413 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2410
2414
2411 def warn(files, reason):
2415 def warn(files, reason):
2412 for f in files:
2416 for f in files:
2413 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2417 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2414 % (m.rel(f), reason))
2418 % (m.rel(f), reason))
2415
2419
2416 if force:
2420 if force:
2417 remove, forget = modified + deleted + clean, added
2421 remove, forget = modified + deleted + clean, added
2418 elif after:
2422 elif after:
2419 remove, forget = deleted, []
2423 remove, forget = deleted, []
2420 warn(modified + added + clean, _('still exists'))
2424 warn(modified + added + clean, _('still exists'))
2421 else:
2425 else:
2422 remove, forget = deleted + clean, []
2426 remove, forget = deleted + clean, []
2423 warn(modified, _('is modified'))
2427 warn(modified, _('is modified'))
2424 warn(added, _('has been marked for add'))
2428 warn(added, _('has been marked for add'))
2425
2429
2426 for f in sorted(remove + forget):
2430 for f in sorted(remove + forget):
2427 if ui.verbose or not m.exact(f):
2431 if ui.verbose or not m.exact(f):
2428 ui.status(_('removing %s\n') % m.rel(f))
2432 ui.status(_('removing %s\n') % m.rel(f))
2429
2433
2430 repo.forget(forget)
2434 repo.forget(forget)
2431 repo.remove(remove, unlink=not after)
2435 repo.remove(remove, unlink=not after)
2432
2436
2433 def rename(ui, repo, *pats, **opts):
2437 def rename(ui, repo, *pats, **opts):
2434 """rename files; equivalent of copy + remove
2438 """rename files; equivalent of copy + remove
2435
2439
2436 Mark dest as copies of sources; mark sources for deletion. If dest
2440 Mark dest as copies of sources; mark sources for deletion. If dest
2437 is a directory, copies are put in that directory. If dest is a
2441 is a directory, copies are put in that directory. If dest is a
2438 file, there can only be one source.
2442 file, there can only be one source.
2439
2443
2440 By default, this command copies the contents of files as they
2444 By default, this command copies the contents of files as they
2441 exist in the working directory. If invoked with -A/--after, the
2445 exist in the working directory. If invoked with -A/--after, the
2442 operation is recorded, but no copying is performed.
2446 operation is recorded, but no copying is performed.
2443
2447
2444 This command takes effect at the next commit. To undo a rename
2448 This command takes effect at the next commit. To undo a rename
2445 before that, see hg revert.
2449 before that, see hg revert.
2446 """
2450 """
2447 wlock = repo.wlock(False)
2451 wlock = repo.wlock(False)
2448 try:
2452 try:
2449 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2453 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2450 finally:
2454 finally:
2451 wlock.release()
2455 wlock.release()
2452
2456
2453 def resolve(ui, repo, *pats, **opts):
2457 def resolve(ui, repo, *pats, **opts):
2454 """retry file merges from a merge or update
2458 """retry file merges from a merge or update
2455
2459
2456 This command can cleanly retry unresolved file merges using file
2460 This command can cleanly retry unresolved file merges using file
2457 revisions preserved from the last update or merge. To attempt to
2461 revisions preserved from the last update or merge. To attempt to
2458 resolve all unresolved files, use the -a/--all switch.
2462 resolve all unresolved files, use the -a/--all switch.
2459
2463
2460 If a conflict is resolved manually, please note that the changes
2464 If a conflict is resolved manually, please note that the changes
2461 will be overwritten if the merge is retried with resolve. The
2465 will be overwritten if the merge is retried with resolve. The
2462 -m/--mark switch should be used to mark the file as resolved.
2466 -m/--mark switch should be used to mark the file as resolved.
2463
2467
2464 This command also allows listing resolved files and manually
2468 This command also allows listing resolved files and manually
2465 indicating whether or not files are resolved. All files must be
2469 indicating whether or not files are resolved. All files must be
2466 marked as resolved before a commit is permitted.
2470 marked as resolved before a commit is permitted.
2467
2471
2468 The codes used to show the status of files are::
2472 The codes used to show the status of files are::
2469
2473
2470 U = unresolved
2474 U = unresolved
2471 R = resolved
2475 R = resolved
2472 """
2476 """
2473
2477
2474 all, mark, unmark, show, nostatus = \
2478 all, mark, unmark, show, nostatus = \
2475 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2479 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2476
2480
2477 if (show and (mark or unmark)) or (mark and unmark):
2481 if (show and (mark or unmark)) or (mark and unmark):
2478 raise util.Abort(_("too many options specified"))
2482 raise util.Abort(_("too many options specified"))
2479 if pats and all:
2483 if pats and all:
2480 raise util.Abort(_("can't specify --all and patterns"))
2484 raise util.Abort(_("can't specify --all and patterns"))
2481 if not (all or pats or show or mark or unmark):
2485 if not (all or pats or show or mark or unmark):
2482 raise util.Abort(_('no files or directories specified; '
2486 raise util.Abort(_('no files or directories specified; '
2483 'use --all to remerge all files'))
2487 'use --all to remerge all files'))
2484
2488
2485 ms = merge_.mergestate(repo)
2489 ms = merge_.mergestate(repo)
2486 m = cmdutil.match(repo, pats, opts)
2490 m = cmdutil.match(repo, pats, opts)
2487
2491
2488 for f in ms:
2492 for f in ms:
2489 if m(f):
2493 if m(f):
2490 if show:
2494 if show:
2491 if nostatus:
2495 if nostatus:
2492 ui.write("%s\n" % f)
2496 ui.write("%s\n" % f)
2493 else:
2497 else:
2494 ui.write("%s %s\n" % (ms[f].upper(), f))
2498 ui.write("%s %s\n" % (ms[f].upper(), f))
2495 elif mark:
2499 elif mark:
2496 ms.mark(f, "r")
2500 ms.mark(f, "r")
2497 elif unmark:
2501 elif unmark:
2498 ms.mark(f, "u")
2502 ms.mark(f, "u")
2499 else:
2503 else:
2500 wctx = repo[None]
2504 wctx = repo[None]
2501 mctx = wctx.parents()[-1]
2505 mctx = wctx.parents()[-1]
2502
2506
2503 # backup pre-resolve (merge uses .orig for its own purposes)
2507 # backup pre-resolve (merge uses .orig for its own purposes)
2504 a = repo.wjoin(f)
2508 a = repo.wjoin(f)
2505 util.copyfile(a, a + ".resolve")
2509 util.copyfile(a, a + ".resolve")
2506
2510
2507 # resolve file
2511 # resolve file
2508 ms.resolve(f, wctx, mctx)
2512 ms.resolve(f, wctx, mctx)
2509
2513
2510 # replace filemerge's .orig file with our resolve file
2514 # replace filemerge's .orig file with our resolve file
2511 util.rename(a + ".resolve", a + ".orig")
2515 util.rename(a + ".resolve", a + ".orig")
2512
2516
2513 def revert(ui, repo, *pats, **opts):
2517 def revert(ui, repo, *pats, **opts):
2514 """restore individual files or directories to an earlier state
2518 """restore individual files or directories to an earlier state
2515
2519
2516 (Use update -r to check out earlier revisions, revert does not
2520 (Use update -r to check out earlier revisions, revert does not
2517 change the working directory parents.)
2521 change the working directory parents.)
2518
2522
2519 With no revision specified, revert the named files or directories
2523 With no revision specified, revert the named files or directories
2520 to the contents they had in the parent of the working directory.
2524 to the contents they had in the parent of the working directory.
2521 This restores the contents of the affected files to an unmodified
2525 This restores the contents of the affected files to an unmodified
2522 state and unschedules adds, removes, copies, and renames. If the
2526 state and unschedules adds, removes, copies, and renames. If the
2523 working directory has two parents, you must explicitly specify the
2527 working directory has two parents, you must explicitly specify the
2524 revision to revert to.
2528 revision to revert to.
2525
2529
2526 Using the -r/--rev option, revert the given files or directories
2530 Using the -r/--rev option, revert the given files or directories
2527 to their contents as of a specific revision. This can be helpful
2531 to their contents as of a specific revision. This can be helpful
2528 to "roll back" some or all of an earlier change. See 'hg help
2532 to "roll back" some or all of an earlier change. See 'hg help
2529 dates' for a list of formats valid for -d/--date.
2533 dates' for a list of formats valid for -d/--date.
2530
2534
2531 Revert modifies the working directory. It does not commit any
2535 Revert modifies the working directory. It does not commit any
2532 changes, or change the parent of the working directory. If you
2536 changes, or change the parent of the working directory. If you
2533 revert to a revision other than the parent of the working
2537 revert to a revision other than the parent of the working
2534 directory, the reverted files will thus appear modified
2538 directory, the reverted files will thus appear modified
2535 afterwards.
2539 afterwards.
2536
2540
2537 If a file has been deleted, it is restored. If the executable mode
2541 If a file has been deleted, it is restored. If the executable mode
2538 of a file was changed, it is reset.
2542 of a file was changed, it is reset.
2539
2543
2540 If names are given, all files matching the names are reverted.
2544 If names are given, all files matching the names are reverted.
2541 If no arguments are given, no files are reverted.
2545 If no arguments are given, no files are reverted.
2542
2546
2543 Modified files are saved with a .orig suffix before reverting.
2547 Modified files are saved with a .orig suffix before reverting.
2544 To disable these backups, use --no-backup.
2548 To disable these backups, use --no-backup.
2545 """
2549 """
2546
2550
2547 if opts["date"]:
2551 if opts["date"]:
2548 if opts["rev"]:
2552 if opts["rev"]:
2549 raise util.Abort(_("you can't specify a revision and a date"))
2553 raise util.Abort(_("you can't specify a revision and a date"))
2550 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2554 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2551
2555
2552 if not pats and not opts.get('all'):
2556 if not pats and not opts.get('all'):
2553 raise util.Abort(_('no files or directories specified; '
2557 raise util.Abort(_('no files or directories specified; '
2554 'use --all to revert the whole repo'))
2558 'use --all to revert the whole repo'))
2555
2559
2556 parent, p2 = repo.dirstate.parents()
2560 parent, p2 = repo.dirstate.parents()
2557 if not opts.get('rev') and p2 != nullid:
2561 if not opts.get('rev') and p2 != nullid:
2558 raise util.Abort(_('uncommitted merge - please provide a '
2562 raise util.Abort(_('uncommitted merge - please provide a '
2559 'specific revision'))
2563 'specific revision'))
2560 ctx = repo[opts.get('rev')]
2564 ctx = repo[opts.get('rev')]
2561 node = ctx.node()
2565 node = ctx.node()
2562 mf = ctx.manifest()
2566 mf = ctx.manifest()
2563 if node == parent:
2567 if node == parent:
2564 pmf = mf
2568 pmf = mf
2565 else:
2569 else:
2566 pmf = None
2570 pmf = None
2567
2571
2568 # need all matching names in dirstate and manifest of target rev,
2572 # need all matching names in dirstate and manifest of target rev,
2569 # so have to walk both. do not print errors if files exist in one
2573 # so have to walk both. do not print errors if files exist in one
2570 # but not other.
2574 # but not other.
2571
2575
2572 names = {}
2576 names = {}
2573
2577
2574 wlock = repo.wlock()
2578 wlock = repo.wlock()
2575 try:
2579 try:
2576 # walk dirstate.
2580 # walk dirstate.
2577
2581
2578 m = cmdutil.match(repo, pats, opts)
2582 m = cmdutil.match(repo, pats, opts)
2579 m.bad = lambda x,y: False
2583 m.bad = lambda x,y: False
2580 for abs in repo.walk(m):
2584 for abs in repo.walk(m):
2581 names[abs] = m.rel(abs), m.exact(abs)
2585 names[abs] = m.rel(abs), m.exact(abs)
2582
2586
2583 # walk target manifest.
2587 # walk target manifest.
2584
2588
2585 def badfn(path, msg):
2589 def badfn(path, msg):
2586 if path in names:
2590 if path in names:
2587 return
2591 return
2588 path_ = path + '/'
2592 path_ = path + '/'
2589 for f in names:
2593 for f in names:
2590 if f.startswith(path_):
2594 if f.startswith(path_):
2591 return
2595 return
2592 ui.warn("%s: %s\n" % (m.rel(path), msg))
2596 ui.warn("%s: %s\n" % (m.rel(path), msg))
2593
2597
2594 m = cmdutil.match(repo, pats, opts)
2598 m = cmdutil.match(repo, pats, opts)
2595 m.bad = badfn
2599 m.bad = badfn
2596 for abs in repo[node].walk(m):
2600 for abs in repo[node].walk(m):
2597 if abs not in names:
2601 if abs not in names:
2598 names[abs] = m.rel(abs), m.exact(abs)
2602 names[abs] = m.rel(abs), m.exact(abs)
2599
2603
2600 m = cmdutil.matchfiles(repo, names)
2604 m = cmdutil.matchfiles(repo, names)
2601 changes = repo.status(match=m)[:4]
2605 changes = repo.status(match=m)[:4]
2602 modified, added, removed, deleted = map(set, changes)
2606 modified, added, removed, deleted = map(set, changes)
2603
2607
2604 # if f is a rename, also revert the source
2608 # if f is a rename, also revert the source
2605 cwd = repo.getcwd()
2609 cwd = repo.getcwd()
2606 for f in added:
2610 for f in added:
2607 src = repo.dirstate.copied(f)
2611 src = repo.dirstate.copied(f)
2608 if src and src not in names and repo.dirstate[src] == 'r':
2612 if src and src not in names and repo.dirstate[src] == 'r':
2609 removed.add(src)
2613 removed.add(src)
2610 names[src] = (repo.pathto(src, cwd), True)
2614 names[src] = (repo.pathto(src, cwd), True)
2611
2615
2612 def removeforget(abs):
2616 def removeforget(abs):
2613 if repo.dirstate[abs] == 'a':
2617 if repo.dirstate[abs] == 'a':
2614 return _('forgetting %s\n')
2618 return _('forgetting %s\n')
2615 return _('removing %s\n')
2619 return _('removing %s\n')
2616
2620
2617 revert = ([], _('reverting %s\n'))
2621 revert = ([], _('reverting %s\n'))
2618 add = ([], _('adding %s\n'))
2622 add = ([], _('adding %s\n'))
2619 remove = ([], removeforget)
2623 remove = ([], removeforget)
2620 undelete = ([], _('undeleting %s\n'))
2624 undelete = ([], _('undeleting %s\n'))
2621
2625
2622 disptable = (
2626 disptable = (
2623 # dispatch table:
2627 # dispatch table:
2624 # file state
2628 # file state
2625 # action if in target manifest
2629 # action if in target manifest
2626 # action if not in target manifest
2630 # action if not in target manifest
2627 # make backup if in target manifest
2631 # make backup if in target manifest
2628 # make backup if not in target manifest
2632 # make backup if not in target manifest
2629 (modified, revert, remove, True, True),
2633 (modified, revert, remove, True, True),
2630 (added, revert, remove, True, False),
2634 (added, revert, remove, True, False),
2631 (removed, undelete, None, False, False),
2635 (removed, undelete, None, False, False),
2632 (deleted, revert, remove, False, False),
2636 (deleted, revert, remove, False, False),
2633 )
2637 )
2634
2638
2635 for abs, (rel, exact) in sorted(names.items()):
2639 for abs, (rel, exact) in sorted(names.items()):
2636 mfentry = mf.get(abs)
2640 mfentry = mf.get(abs)
2637 target = repo.wjoin(abs)
2641 target = repo.wjoin(abs)
2638 def handle(xlist, dobackup):
2642 def handle(xlist, dobackup):
2639 xlist[0].append(abs)
2643 xlist[0].append(abs)
2640 if dobackup and not opts.get('no_backup') and util.lexists(target):
2644 if dobackup and not opts.get('no_backup') and util.lexists(target):
2641 bakname = "%s.orig" % rel
2645 bakname = "%s.orig" % rel
2642 ui.note(_('saving current version of %s as %s\n') %
2646 ui.note(_('saving current version of %s as %s\n') %
2643 (rel, bakname))
2647 (rel, bakname))
2644 if not opts.get('dry_run'):
2648 if not opts.get('dry_run'):
2645 util.copyfile(target, bakname)
2649 util.copyfile(target, bakname)
2646 if ui.verbose or not exact:
2650 if ui.verbose or not exact:
2647 msg = xlist[1]
2651 msg = xlist[1]
2648 if not isinstance(msg, basestring):
2652 if not isinstance(msg, basestring):
2649 msg = msg(abs)
2653 msg = msg(abs)
2650 ui.status(msg % rel)
2654 ui.status(msg % rel)
2651 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2655 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2652 if abs not in table: continue
2656 if abs not in table: continue
2653 # file has changed in dirstate
2657 # file has changed in dirstate
2654 if mfentry:
2658 if mfentry:
2655 handle(hitlist, backuphit)
2659 handle(hitlist, backuphit)
2656 elif misslist is not None:
2660 elif misslist is not None:
2657 handle(misslist, backupmiss)
2661 handle(misslist, backupmiss)
2658 break
2662 break
2659 else:
2663 else:
2660 if abs not in repo.dirstate:
2664 if abs not in repo.dirstate:
2661 if mfentry:
2665 if mfentry:
2662 handle(add, True)
2666 handle(add, True)
2663 elif exact:
2667 elif exact:
2664 ui.warn(_('file not managed: %s\n') % rel)
2668 ui.warn(_('file not managed: %s\n') % rel)
2665 continue
2669 continue
2666 # file has not changed in dirstate
2670 # file has not changed in dirstate
2667 if node == parent:
2671 if node == parent:
2668 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2672 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2669 continue
2673 continue
2670 if pmf is None:
2674 if pmf is None:
2671 # only need parent manifest in this unlikely case,
2675 # only need parent manifest in this unlikely case,
2672 # so do not read by default
2676 # so do not read by default
2673 pmf = repo[parent].manifest()
2677 pmf = repo[parent].manifest()
2674 if abs in pmf:
2678 if abs in pmf:
2675 if mfentry:
2679 if mfentry:
2676 # if version of file is same in parent and target
2680 # if version of file is same in parent and target
2677 # manifests, do nothing
2681 # manifests, do nothing
2678 if (pmf[abs] != mfentry or
2682 if (pmf[abs] != mfentry or
2679 pmf.flags(abs) != mf.flags(abs)):
2683 pmf.flags(abs) != mf.flags(abs)):
2680 handle(revert, False)
2684 handle(revert, False)
2681 else:
2685 else:
2682 handle(remove, False)
2686 handle(remove, False)
2683
2687
2684 if not opts.get('dry_run'):
2688 if not opts.get('dry_run'):
2685 def checkout(f):
2689 def checkout(f):
2686 fc = ctx[f]
2690 fc = ctx[f]
2687 repo.wwrite(f, fc.data(), fc.flags())
2691 repo.wwrite(f, fc.data(), fc.flags())
2688
2692
2689 audit_path = util.path_auditor(repo.root)
2693 audit_path = util.path_auditor(repo.root)
2690 for f in remove[0]:
2694 for f in remove[0]:
2691 if repo.dirstate[f] == 'a':
2695 if repo.dirstate[f] == 'a':
2692 repo.dirstate.forget(f)
2696 repo.dirstate.forget(f)
2693 continue
2697 continue
2694 audit_path(f)
2698 audit_path(f)
2695 try:
2699 try:
2696 util.unlink(repo.wjoin(f))
2700 util.unlink(repo.wjoin(f))
2697 except OSError:
2701 except OSError:
2698 pass
2702 pass
2699 repo.dirstate.remove(f)
2703 repo.dirstate.remove(f)
2700
2704
2701 normal = None
2705 normal = None
2702 if node == parent:
2706 if node == parent:
2703 # We're reverting to our parent. If possible, we'd like status
2707 # We're reverting to our parent. If possible, we'd like status
2704 # to report the file as clean. We have to use normallookup for
2708 # to report the file as clean. We have to use normallookup for
2705 # merges to avoid losing information about merged/dirty files.
2709 # merges to avoid losing information about merged/dirty files.
2706 if p2 != nullid:
2710 if p2 != nullid:
2707 normal = repo.dirstate.normallookup
2711 normal = repo.dirstate.normallookup
2708 else:
2712 else:
2709 normal = repo.dirstate.normal
2713 normal = repo.dirstate.normal
2710 for f in revert[0]:
2714 for f in revert[0]:
2711 checkout(f)
2715 checkout(f)
2712 if normal:
2716 if normal:
2713 normal(f)
2717 normal(f)
2714
2718
2715 for f in add[0]:
2719 for f in add[0]:
2716 checkout(f)
2720 checkout(f)
2717 repo.dirstate.add(f)
2721 repo.dirstate.add(f)
2718
2722
2719 normal = repo.dirstate.normallookup
2723 normal = repo.dirstate.normallookup
2720 if node == parent and p2 == nullid:
2724 if node == parent and p2 == nullid:
2721 normal = repo.dirstate.normal
2725 normal = repo.dirstate.normal
2722 for f in undelete[0]:
2726 for f in undelete[0]:
2723 checkout(f)
2727 checkout(f)
2724 normal(f)
2728 normal(f)
2725
2729
2726 finally:
2730 finally:
2727 wlock.release()
2731 wlock.release()
2728
2732
2729 def rollback(ui, repo):
2733 def rollback(ui, repo):
2730 """roll back the last transaction
2734 """roll back the last transaction
2731
2735
2732 This command should be used with care. There is only one level of
2736 This command should be used with care. There is only one level of
2733 rollback, and there is no way to undo a rollback. It will also
2737 rollback, and there is no way to undo a rollback. It will also
2734 restore the dirstate at the time of the last transaction, losing
2738 restore the dirstate at the time of the last transaction, losing
2735 any dirstate changes since that time. This command does not alter
2739 any dirstate changes since that time. This command does not alter
2736 the working directory.
2740 the working directory.
2737
2741
2738 Transactions are used to encapsulate the effects of all commands
2742 Transactions are used to encapsulate the effects of all commands
2739 that create new changesets or propagate existing changesets into a
2743 that create new changesets or propagate existing changesets into a
2740 repository. For example, the following commands are transactional,
2744 repository. For example, the following commands are transactional,
2741 and their effects can be rolled back::
2745 and their effects can be rolled back::
2742
2746
2743 commit
2747 commit
2744 import
2748 import
2745 pull
2749 pull
2746 push (with this repository as destination)
2750 push (with this repository as destination)
2747 unbundle
2751 unbundle
2748
2752
2749 This command is not intended for use on public repositories. Once
2753 This command is not intended for use on public repositories. Once
2750 changes are visible for pull by other users, rolling a transaction
2754 changes are visible for pull by other users, rolling a transaction
2751 back locally is ineffective (someone else may already have pulled
2755 back locally is ineffective (someone else may already have pulled
2752 the changes). Furthermore, a race is possible with readers of the
2756 the changes). Furthermore, a race is possible with readers of the
2753 repository; for example an in-progress pull from the repository
2757 repository; for example an in-progress pull from the repository
2754 may fail if a rollback is performed.
2758 may fail if a rollback is performed.
2755 """
2759 """
2756 repo.rollback()
2760 repo.rollback()
2757
2761
2758 def root(ui, repo):
2762 def root(ui, repo):
2759 """print the root (top) of the current working directory
2763 """print the root (top) of the current working directory
2760
2764
2761 Print the root directory of the current repository.
2765 Print the root directory of the current repository.
2762 """
2766 """
2763 ui.write(repo.root + "\n")
2767 ui.write(repo.root + "\n")
2764
2768
2765 def serve(ui, repo, **opts):
2769 def serve(ui, repo, **opts):
2766 """export the repository via HTTP
2770 """export the repository via HTTP
2767
2771
2768 Start a local HTTP repository browser and pull server.
2772 Start a local HTTP repository browser and pull server.
2769
2773
2770 By default, the server logs accesses to stdout and errors to
2774 By default, the server logs accesses to stdout and errors to
2771 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2775 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2772 files.
2776 files.
2773 """
2777 """
2774
2778
2775 if opts["stdio"]:
2779 if opts["stdio"]:
2776 if repo is None:
2780 if repo is None:
2777 raise error.RepoError(_("There is no Mercurial repository here"
2781 raise error.RepoError(_("There is no Mercurial repository here"
2778 " (.hg not found)"))
2782 " (.hg not found)"))
2779 s = sshserver.sshserver(ui, repo)
2783 s = sshserver.sshserver(ui, repo)
2780 s.serve_forever()
2784 s.serve_forever()
2781
2785
2782 baseui = repo and repo.baseui or ui
2786 baseui = repo and repo.baseui or ui
2783 optlist = ("name templates style address port prefix ipv6"
2787 optlist = ("name templates style address port prefix ipv6"
2784 " accesslog errorlog webdir_conf certificate encoding")
2788 " accesslog errorlog webdir_conf certificate encoding")
2785 for o in optlist.split():
2789 for o in optlist.split():
2786 if opts.get(o, None):
2790 if opts.get(o, None):
2787 baseui.setconfig("web", o, str(opts[o]))
2791 baseui.setconfig("web", o, str(opts[o]))
2788 if (repo is not None) and (repo.ui != baseui):
2792 if (repo is not None) and (repo.ui != baseui):
2789 repo.ui.setconfig("web", o, str(opts[o]))
2793 repo.ui.setconfig("web", o, str(opts[o]))
2790
2794
2791 if repo is None and not ui.config("web", "webdir_conf"):
2795 if repo is None and not ui.config("web", "webdir_conf"):
2792 raise error.RepoError(_("There is no Mercurial repository here"
2796 raise error.RepoError(_("There is no Mercurial repository here"
2793 " (.hg not found)"))
2797 " (.hg not found)"))
2794
2798
2795 class service(object):
2799 class service(object):
2796 def init(self):
2800 def init(self):
2797 util.set_signal_handler()
2801 util.set_signal_handler()
2798 self.httpd = server.create_server(baseui, repo)
2802 self.httpd = server.create_server(baseui, repo)
2799
2803
2800 if not ui.verbose: return
2804 if not ui.verbose: return
2801
2805
2802 if self.httpd.prefix:
2806 if self.httpd.prefix:
2803 prefix = self.httpd.prefix.strip('/') + '/'
2807 prefix = self.httpd.prefix.strip('/') + '/'
2804 else:
2808 else:
2805 prefix = ''
2809 prefix = ''
2806
2810
2807 port = ':%d' % self.httpd.port
2811 port = ':%d' % self.httpd.port
2808 if port == ':80':
2812 if port == ':80':
2809 port = ''
2813 port = ''
2810
2814
2811 bindaddr = self.httpd.addr
2815 bindaddr = self.httpd.addr
2812 if bindaddr == '0.0.0.0':
2816 if bindaddr == '0.0.0.0':
2813 bindaddr = '*'
2817 bindaddr = '*'
2814 elif ':' in bindaddr: # IPv6
2818 elif ':' in bindaddr: # IPv6
2815 bindaddr = '[%s]' % bindaddr
2819 bindaddr = '[%s]' % bindaddr
2816
2820
2817 fqaddr = self.httpd.fqaddr
2821 fqaddr = self.httpd.fqaddr
2818 if ':' in fqaddr:
2822 if ':' in fqaddr:
2819 fqaddr = '[%s]' % fqaddr
2823 fqaddr = '[%s]' % fqaddr
2820 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2824 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2821 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2825 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2822
2826
2823 def run(self):
2827 def run(self):
2824 self.httpd.serve_forever()
2828 self.httpd.serve_forever()
2825
2829
2826 service = service()
2830 service = service()
2827
2831
2828 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2832 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2829
2833
2830 def status(ui, repo, *pats, **opts):
2834 def status(ui, repo, *pats, **opts):
2831 """show changed files in the working directory
2835 """show changed files in the working directory
2832
2836
2833 Show status of files in the repository. If names are given, only
2837 Show status of files in the repository. If names are given, only
2834 files that match are shown. Files that are clean or ignored or
2838 files that match are shown. Files that are clean or ignored or
2835 the source of a copy/move operation, are not listed unless
2839 the source of a copy/move operation, are not listed unless
2836 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2840 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2837 Unless options described with "show only ..." are given, the
2841 Unless options described with "show only ..." are given, the
2838 options -mardu are used.
2842 options -mardu are used.
2839
2843
2840 Option -q/--quiet hides untracked (unknown and ignored) files
2844 Option -q/--quiet hides untracked (unknown and ignored) files
2841 unless explicitly requested with -u/--unknown or -i/--ignored.
2845 unless explicitly requested with -u/--unknown or -i/--ignored.
2842
2846
2843 NOTE: status may appear to disagree with diff if permissions have
2847 NOTE: status may appear to disagree with diff if permissions have
2844 changed or a merge has occurred. The standard diff format does not
2848 changed or a merge has occurred. The standard diff format does not
2845 report permission changes and diff only reports changes relative
2849 report permission changes and diff only reports changes relative
2846 to one merge parent.
2850 to one merge parent.
2847
2851
2848 If one revision is given, it is used as the base revision.
2852 If one revision is given, it is used as the base revision.
2849 If two revisions are given, the differences between them are
2853 If two revisions are given, the differences between them are
2850 shown.
2854 shown.
2851
2855
2852 The codes used to show the status of files are::
2856 The codes used to show the status of files are::
2853
2857
2854 M = modified
2858 M = modified
2855 A = added
2859 A = added
2856 R = removed
2860 R = removed
2857 C = clean
2861 C = clean
2858 ! = missing (deleted by non-hg command, but still tracked)
2862 ! = missing (deleted by non-hg command, but still tracked)
2859 ? = not tracked
2863 ? = not tracked
2860 I = ignored
2864 I = ignored
2861 = origin of the previous file listed as A (added)
2865 = origin of the previous file listed as A (added)
2862 """
2866 """
2863
2867
2864 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2868 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2865 cwd = (pats and repo.getcwd()) or ''
2869 cwd = (pats and repo.getcwd()) or ''
2866 end = opts.get('print0') and '\0' or '\n'
2870 end = opts.get('print0') and '\0' or '\n'
2867 copy = {}
2871 copy = {}
2868 states = 'modified added removed deleted unknown ignored clean'.split()
2872 states = 'modified added removed deleted unknown ignored clean'.split()
2869 show = [k for k in states if opts.get(k)]
2873 show = [k for k in states if opts.get(k)]
2870 if opts.get('all'):
2874 if opts.get('all'):
2871 show += ui.quiet and (states[:4] + ['clean']) or states
2875 show += ui.quiet and (states[:4] + ['clean']) or states
2872 if not show:
2876 if not show:
2873 show = ui.quiet and states[:4] or states[:5]
2877 show = ui.quiet and states[:4] or states[:5]
2874
2878
2875 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2879 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2876 'ignored' in show, 'clean' in show, 'unknown' in show)
2880 'ignored' in show, 'clean' in show, 'unknown' in show)
2877 changestates = zip(states, 'MAR!?IC', stat)
2881 changestates = zip(states, 'MAR!?IC', stat)
2878
2882
2879 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2883 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2880 ctxn = repo[nullid]
2884 ctxn = repo[nullid]
2881 ctx1 = repo[node1]
2885 ctx1 = repo[node1]
2882 ctx2 = repo[node2]
2886 ctx2 = repo[node2]
2883 added = stat[1]
2887 added = stat[1]
2884 if node2 is None:
2888 if node2 is None:
2885 added = stat[0] + stat[1] # merged?
2889 added = stat[0] + stat[1] # merged?
2886
2890
2887 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2891 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2888 if k in added:
2892 if k in added:
2889 copy[k] = v
2893 copy[k] = v
2890 elif v in added:
2894 elif v in added:
2891 copy[v] = k
2895 copy[v] = k
2892
2896
2893 for state, char, files in changestates:
2897 for state, char, files in changestates:
2894 if state in show:
2898 if state in show:
2895 format = "%s %%s%s" % (char, end)
2899 format = "%s %%s%s" % (char, end)
2896 if opts.get('no_status'):
2900 if opts.get('no_status'):
2897 format = "%%s%s" % end
2901 format = "%%s%s" % end
2898
2902
2899 for f in files:
2903 for f in files:
2900 ui.write(format % repo.pathto(f, cwd))
2904 ui.write(format % repo.pathto(f, cwd))
2901 if f in copy:
2905 if f in copy:
2902 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2906 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2903
2907
2904 def summary(ui, repo, **opts):
2908 def summary(ui, repo, **opts):
2905 """summarize working directory state
2909 """summarize working directory state
2906
2910
2907 This generates a brief summary of the working directory state,
2911 This generates a brief summary of the working directory state,
2908 including parents, branch, commit status, and available updates.
2912 including parents, branch, commit status, and available updates.
2909
2913
2910 With the --remote option, this will check the default paths for
2914 With the --remote option, this will check the default paths for
2911 incoming and outgoing changes. This can be time-consuming.
2915 incoming and outgoing changes. This can be time-consuming.
2912 """
2916 """
2913
2917
2914 ctx = repo[None]
2918 ctx = repo[None]
2915 parents = ctx.parents()
2919 parents = ctx.parents()
2916 pnode = parents[0].node()
2920 pnode = parents[0].node()
2917 tags = repo.tags()
2921 tags = repo.tags()
2918
2922
2919 for p in parents:
2923 for p in parents:
2920 t = ' '.join([t for t in tags if tags[t] == p.node()])
2924 t = ' '.join([t for t in tags if tags[t] == p.node()])
2921 if p.rev() == -1:
2925 if p.rev() == -1:
2922 if not len(repo):
2926 if not len(repo):
2923 t += _(' (empty repository)')
2927 t += _(' (empty repository)')
2924 else:
2928 else:
2925 t += _(' (no revision checked out)')
2929 t += _(' (no revision checked out)')
2926 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2930 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2927 if p.description():
2931 if p.description():
2928 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2932 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2929
2933
2930 branch = ctx.branch()
2934 branch = ctx.branch()
2931 bheads = repo.branchheads(branch)
2935 bheads = repo.branchheads(branch)
2932 ui.status(_('branch: %s\n') % branch)
2936 ui.status(_('branch: %s\n') % branch)
2933
2937
2934 st = list(repo.status(unknown=True))[:7]
2938 st = list(repo.status(unknown=True))[:7]
2935 ms = merge_.mergestate(repo)
2939 ms = merge_.mergestate(repo)
2936 st.append([f for f in ms if f == 'u'])
2940 st.append([f for f in ms if f == 'u'])
2937 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2941 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2938 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2942 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2939 _('%d unresolved')]
2943 _('%d unresolved')]
2940 t = []
2944 t = []
2941 for s,l in zip(st, labels):
2945 for s,l in zip(st, labels):
2942 if s:
2946 if s:
2943 t.append(l % len(s))
2947 t.append(l % len(s))
2944
2948
2945 t = ', '.join(t)
2949 t = ', '.join(t)
2946
2950
2947 if len(parents) > 1:
2951 if len(parents) > 1:
2948 t += _(' (merge)')
2952 t += _(' (merge)')
2949 elif branch != parents[0].branch():
2953 elif branch != parents[0].branch():
2950 t += _(' (new branch)')
2954 t += _(' (new branch)')
2951 elif (not st[0] and not st[1] and not st[2]):
2955 elif (not st[0] and not st[1] and not st[2]):
2952 t += _(' (clean)')
2956 t += _(' (clean)')
2953 elif pnode not in bheads:
2957 elif pnode not in bheads:
2954 t += _(' (new branch head)')
2958 t += _(' (new branch head)')
2955
2959
2956 if 'clean' in t:
2960 if 'clean' in t:
2957 ui.status(_('commit: %s\n') % t.strip())
2961 ui.status(_('commit: %s\n') % t.strip())
2958 else:
2962 else:
2959 ui.write(_('commit: %s\n') % t.strip())
2963 ui.write(_('commit: %s\n') % t.strip())
2960
2964
2961 # all ancestors of branch heads - all ancestors of parent = new csets
2965 # all ancestors of branch heads - all ancestors of parent = new csets
2962 new = [0] * len(repo)
2966 new = [0] * len(repo)
2963 cl = repo.changelog
2967 cl = repo.changelog
2964 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
2968 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
2965 new[a] = 1
2969 new[a] = 1
2966 for a in cl.ancestors(*[p.rev() for p in parents]):
2970 for a in cl.ancestors(*[p.rev() for p in parents]):
2967 new[a] = 0
2971 new[a] = 0
2968 new = sum(new)
2972 new = sum(new)
2969
2973
2970 if new == 0:
2974 if new == 0:
2971 ui.status(_('update: (current)\n'))
2975 ui.status(_('update: (current)\n'))
2972 elif pnode not in bheads:
2976 elif pnode not in bheads:
2973 ui.write(_('update: %d new changesets (update)\n') % new)
2977 ui.write(_('update: %d new changesets (update)\n') % new)
2974 else:
2978 else:
2975 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2979 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2976 (new, len(bheads)))
2980 (new, len(bheads)))
2977
2981
2978 if opts.get('remote'):
2982 if opts.get('remote'):
2979 t = []
2983 t = []
2980 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2984 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2981 opts.get('rev'))
2985 opts.get('rev'))
2982 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2986 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2983 ui.debug('comparing with %s\n' % url.hidepassword(source))
2987 ui.debug('comparing with %s\n' % url.hidepassword(source))
2984 repo.ui.pushbuffer()
2988 repo.ui.pushbuffer()
2985 common, incoming, rheads = repo.findcommonincoming(other)
2989 common, incoming, rheads = repo.findcommonincoming(other)
2986 repo.ui.popbuffer()
2990 repo.ui.popbuffer()
2987 if incoming:
2991 if incoming:
2988 t.append(_('1 or more incoming'))
2992 t.append(_('1 or more incoming'))
2989
2993
2990 dest, revs, checkout = hg.parseurl(
2994 dest, revs, checkout = hg.parseurl(
2991 ui.expandpath('default-push', 'default'))
2995 ui.expandpath('default-push', 'default'))
2992 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2996 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2993 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2997 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2994 repo.ui.pushbuffer()
2998 repo.ui.pushbuffer()
2995 o = repo.findoutgoing(other)
2999 o = repo.findoutgoing(other)
2996 repo.ui.popbuffer()
3000 repo.ui.popbuffer()
2997 o = repo.changelog.nodesbetween(o, revs)[0]
3001 o = repo.changelog.nodesbetween(o, revs)[0]
2998 if o:
3002 if o:
2999 t.append(_('%d outgoing') % len(o))
3003 t.append(_('%d outgoing') % len(o))
3000
3004
3001 if t:
3005 if t:
3002 ui.write(_('remote: %s\n') % (', '.join(t)))
3006 ui.write(_('remote: %s\n') % (', '.join(t)))
3003 else:
3007 else:
3004 ui.status(_('remote: (synced)\n'))
3008 ui.status(_('remote: (synced)\n'))
3005
3009
3006 def tag(ui, repo, name1, *names, **opts):
3010 def tag(ui, repo, name1, *names, **opts):
3007 """add one or more tags for the current or given revision
3011 """add one or more tags for the current or given revision
3008
3012
3009 Name a particular revision using <name>.
3013 Name a particular revision using <name>.
3010
3014
3011 Tags are used to name particular revisions of the repository and are
3015 Tags are used to name particular revisions of the repository and are
3012 very useful to compare different revisions, to go back to significant
3016 very useful to compare different revisions, to go back to significant
3013 earlier versions or to mark branch points as releases, etc.
3017 earlier versions or to mark branch points as releases, etc.
3014
3018
3015 If no revision is given, the parent of the working directory is
3019 If no revision is given, the parent of the working directory is
3016 used, or tip if no revision is checked out.
3020 used, or tip if no revision is checked out.
3017
3021
3018 To facilitate version control, distribution, and merging of tags,
3022 To facilitate version control, distribution, and merging of tags,
3019 they are stored as a file named ".hgtags" which is managed
3023 they are stored as a file named ".hgtags" which is managed
3020 similarly to other project files and can be hand-edited if
3024 similarly to other project files and can be hand-edited if
3021 necessary. The file '.hg/localtags' is used for local tags (not
3025 necessary. The file '.hg/localtags' is used for local tags (not
3022 shared among repositories).
3026 shared among repositories).
3023
3027
3024 See 'hg help dates' for a list of formats valid for -d/--date.
3028 See 'hg help dates' for a list of formats valid for -d/--date.
3025 """
3029 """
3026
3030
3027 rev_ = "."
3031 rev_ = "."
3028 names = (name1,) + names
3032 names = (name1,) + names
3029 if len(names) != len(set(names)):
3033 if len(names) != len(set(names)):
3030 raise util.Abort(_('tag names must be unique'))
3034 raise util.Abort(_('tag names must be unique'))
3031 for n in names:
3035 for n in names:
3032 if n in ['tip', '.', 'null']:
3036 if n in ['tip', '.', 'null']:
3033 raise util.Abort(_('the name \'%s\' is reserved') % n)
3037 raise util.Abort(_('the name \'%s\' is reserved') % n)
3034 if opts.get('rev') and opts.get('remove'):
3038 if opts.get('rev') and opts.get('remove'):
3035 raise util.Abort(_("--rev and --remove are incompatible"))
3039 raise util.Abort(_("--rev and --remove are incompatible"))
3036 if opts.get('rev'):
3040 if opts.get('rev'):
3037 rev_ = opts['rev']
3041 rev_ = opts['rev']
3038 message = opts.get('message')
3042 message = opts.get('message')
3039 if opts.get('remove'):
3043 if opts.get('remove'):
3040 expectedtype = opts.get('local') and 'local' or 'global'
3044 expectedtype = opts.get('local') and 'local' or 'global'
3041 for n in names:
3045 for n in names:
3042 if not repo.tagtype(n):
3046 if not repo.tagtype(n):
3043 raise util.Abort(_('tag \'%s\' does not exist') % n)
3047 raise util.Abort(_('tag \'%s\' does not exist') % n)
3044 if repo.tagtype(n) != expectedtype:
3048 if repo.tagtype(n) != expectedtype:
3045 if expectedtype == 'global':
3049 if expectedtype == 'global':
3046 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3050 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3047 else:
3051 else:
3048 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3052 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3049 rev_ = nullid
3053 rev_ = nullid
3050 if not message:
3054 if not message:
3051 # we don't translate commit messages
3055 # we don't translate commit messages
3052 message = 'Removed tag %s' % ', '.join(names)
3056 message = 'Removed tag %s' % ', '.join(names)
3053 elif not opts.get('force'):
3057 elif not opts.get('force'):
3054 for n in names:
3058 for n in names:
3055 if n in repo.tags():
3059 if n in repo.tags():
3056 raise util.Abort(_('tag \'%s\' already exists '
3060 raise util.Abort(_('tag \'%s\' already exists '
3057 '(use -f to force)') % n)
3061 '(use -f to force)') % n)
3058 if not rev_ and repo.dirstate.parents()[1] != nullid:
3062 if not rev_ and repo.dirstate.parents()[1] != nullid:
3059 raise util.Abort(_('uncommitted merge - please provide a '
3063 raise util.Abort(_('uncommitted merge - please provide a '
3060 'specific revision'))
3064 'specific revision'))
3061 r = repo[rev_].node()
3065 r = repo[rev_].node()
3062
3066
3063 if not message:
3067 if not message:
3064 # we don't translate commit messages
3068 # we don't translate commit messages
3065 message = ('Added tag %s for changeset %s' %
3069 message = ('Added tag %s for changeset %s' %
3066 (', '.join(names), short(r)))
3070 (', '.join(names), short(r)))
3067
3071
3068 date = opts.get('date')
3072 date = opts.get('date')
3069 if date:
3073 if date:
3070 date = util.parsedate(date)
3074 date = util.parsedate(date)
3071
3075
3072 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3076 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3073
3077
3074 def tags(ui, repo):
3078 def tags(ui, repo):
3075 """list repository tags
3079 """list repository tags
3076
3080
3077 This lists both regular and local tags. When the -v/--verbose
3081 This lists both regular and local tags. When the -v/--verbose
3078 switch is used, a third column "local" is printed for local tags.
3082 switch is used, a third column "local" is printed for local tags.
3079 """
3083 """
3080
3084
3081 hexfunc = ui.debugflag and hex or short
3085 hexfunc = ui.debugflag and hex or short
3082 tagtype = ""
3086 tagtype = ""
3083
3087
3084 for t, n in reversed(repo.tagslist()):
3088 for t, n in reversed(repo.tagslist()):
3085 if ui.quiet:
3089 if ui.quiet:
3086 ui.write("%s\n" % t)
3090 ui.write("%s\n" % t)
3087 continue
3091 continue
3088
3092
3089 try:
3093 try:
3090 hn = hexfunc(n)
3094 hn = hexfunc(n)
3091 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3095 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3092 except error.LookupError:
3096 except error.LookupError:
3093 r = " ?:%s" % hn
3097 r = " ?:%s" % hn
3094 else:
3098 else:
3095 spaces = " " * (30 - encoding.colwidth(t))
3099 spaces = " " * (30 - encoding.colwidth(t))
3096 if ui.verbose:
3100 if ui.verbose:
3097 if repo.tagtype(t) == 'local':
3101 if repo.tagtype(t) == 'local':
3098 tagtype = " local"
3102 tagtype = " local"
3099 else:
3103 else:
3100 tagtype = ""
3104 tagtype = ""
3101 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3105 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3102
3106
3103 def tip(ui, repo, **opts):
3107 def tip(ui, repo, **opts):
3104 """show the tip revision
3108 """show the tip revision
3105
3109
3106 The tip revision (usually just called the tip) is the changeset
3110 The tip revision (usually just called the tip) is the changeset
3107 most recently added to the repository (and therefore the most
3111 most recently added to the repository (and therefore the most
3108 recently changed head).
3112 recently changed head).
3109
3113
3110 If you have just made a commit, that commit will be the tip. If
3114 If you have just made a commit, that commit will be the tip. If
3111 you have just pulled changes from another repository, the tip of
3115 you have just pulled changes from another repository, the tip of
3112 that repository becomes the current tip. The "tip" tag is special
3116 that repository becomes the current tip. The "tip" tag is special
3113 and cannot be renamed or assigned to a different changeset.
3117 and cannot be renamed or assigned to a different changeset.
3114 """
3118 """
3115 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3119 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3116
3120
3117 def unbundle(ui, repo, fname1, *fnames, **opts):
3121 def unbundle(ui, repo, fname1, *fnames, **opts):
3118 """apply one or more changegroup files
3122 """apply one or more changegroup files
3119
3123
3120 Apply one or more compressed changegroup files generated by the
3124 Apply one or more compressed changegroup files generated by the
3121 bundle command.
3125 bundle command.
3122 """
3126 """
3123 fnames = (fname1,) + fnames
3127 fnames = (fname1,) + fnames
3124
3128
3125 lock = repo.lock()
3129 lock = repo.lock()
3126 try:
3130 try:
3127 for fname in fnames:
3131 for fname in fnames:
3128 f = url.open(ui, fname)
3132 f = url.open(ui, fname)
3129 gen = changegroup.readbundle(f, fname)
3133 gen = changegroup.readbundle(f, fname)
3130 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3134 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3131 finally:
3135 finally:
3132 lock.release()
3136 lock.release()
3133
3137
3134 return postincoming(ui, repo, modheads, opts.get('update'), None)
3138 return postincoming(ui, repo, modheads, opts.get('update'), None)
3135
3139
3136 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3140 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3137 """update working directory
3141 """update working directory
3138
3142
3139 Update the repository's working directory to the specified
3143 Update the repository's working directory to the specified
3140 changeset.
3144 changeset.
3141
3145
3142 If no changeset is specified, attempt to update to the head of the
3146 If no changeset is specified, attempt to update to the head of the
3143 current branch. If this head is a descendant of the working
3147 current branch. If this head is a descendant of the working
3144 directory's parent, update to it, otherwise abort.
3148 directory's parent, update to it, otherwise abort.
3145
3149
3146 The following rules apply when the working directory contains
3150 The following rules apply when the working directory contains
3147 uncommitted changes:
3151 uncommitted changes:
3148
3152
3149 1. If neither -c/--check nor -C/--clean is specified, uncommitted
3153 1. If neither -c/--check nor -C/--clean is specified, uncommitted
3150 changes are merged into the requested changeset, and the merged result
3154 changes are merged into the requested changeset, and the merged result
3151 is left uncommitted. Updating and merging will occur only if the
3155 is left uncommitted. Updating and merging will occur only if the
3152 requested changeset is an ancestor or descendant of the parent
3156 requested changeset is an ancestor or descendant of the parent
3153 changeset. Otherwise, the update is aborted and the uncommitted changes
3157 changeset. Otherwise, the update is aborted and the uncommitted changes
3154 are preserved.
3158 are preserved.
3155
3159
3156 2. With the -c/--check option, the update is aborted and the
3160 2. With the -c/--check option, the update is aborted and the
3157 uncommitted changes are preserved.
3161 uncommitted changes are preserved.
3158
3162
3159 3. With the -C/--clean option, uncommitted changes are discarded and
3163 3. With the -C/--clean option, uncommitted changes are discarded and
3160 the working directory is updated to the requested changeset.
3164 the working directory is updated to the requested changeset.
3161
3165
3162 Use null as the changeset to remove the working directory (like 'hg
3166 Use null as the changeset to remove the working directory (like 'hg
3163 clone -U').
3167 clone -U').
3164
3168
3165 If you want to update just one file to an older changeset, use 'hg revert'.
3169 If you want to update just one file to an older changeset, use 'hg revert'.
3166
3170
3167 See 'hg help dates' for a list of formats valid for -d/--date.
3171 See 'hg help dates' for a list of formats valid for -d/--date.
3168 """
3172 """
3169 if rev and node:
3173 if rev and node:
3170 raise util.Abort(_("please specify just one revision"))
3174 raise util.Abort(_("please specify just one revision"))
3171
3175
3172 if not rev:
3176 if not rev:
3173 rev = node
3177 rev = node
3174
3178
3175 if check and clean:
3179 if check and clean:
3176 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3180 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3177
3181
3178 if check:
3182 if check:
3179 # we could use dirty() but we can ignore merge and branch trivia
3183 # we could use dirty() but we can ignore merge and branch trivia
3180 c = repo[None]
3184 c = repo[None]
3181 if c.modified() or c.added() or c.removed():
3185 if c.modified() or c.added() or c.removed():
3182 raise util.Abort(_("uncommitted local changes"))
3186 raise util.Abort(_("uncommitted local changes"))
3183
3187
3184 if date:
3188 if date:
3185 if rev:
3189 if rev:
3186 raise util.Abort(_("you can't specify a revision and a date"))
3190 raise util.Abort(_("you can't specify a revision and a date"))
3187 rev = cmdutil.finddate(ui, repo, date)
3191 rev = cmdutil.finddate(ui, repo, date)
3188
3192
3189 if clean or check:
3193 if clean or check:
3190 return hg.clean(repo, rev)
3194 return hg.clean(repo, rev)
3191 else:
3195 else:
3192 return hg.update(repo, rev)
3196 return hg.update(repo, rev)
3193
3197
3194 def verify(ui, repo):
3198 def verify(ui, repo):
3195 """verify the integrity of the repository
3199 """verify the integrity of the repository
3196
3200
3197 Verify the integrity of the current repository.
3201 Verify the integrity of the current repository.
3198
3202
3199 This will perform an extensive check of the repository's
3203 This will perform an extensive check of the repository's
3200 integrity, validating the hashes and checksums of each entry in
3204 integrity, validating the hashes and checksums of each entry in
3201 the changelog, manifest, and tracked files, as well as the
3205 the changelog, manifest, and tracked files, as well as the
3202 integrity of their crosslinks and indices.
3206 integrity of their crosslinks and indices.
3203 """
3207 """
3204 return hg.verify(repo)
3208 return hg.verify(repo)
3205
3209
3206 def version_(ui):
3210 def version_(ui):
3207 """output version and copyright information"""
3211 """output version and copyright information"""
3208 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3212 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3209 % util.version())
3213 % util.version())
3210 ui.status(_(
3214 ui.status(_(
3211 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3215 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3212 "This is free software; see the source for copying conditions. "
3216 "This is free software; see the source for copying conditions. "
3213 "There is NO\nwarranty; "
3217 "There is NO\nwarranty; "
3214 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3218 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3215 ))
3219 ))
3216
3220
3217 # Command options and aliases are listed here, alphabetically
3221 # Command options and aliases are listed here, alphabetically
3218
3222
3219 globalopts = [
3223 globalopts = [
3220 ('R', 'repository', '',
3224 ('R', 'repository', '',
3221 _('repository root directory or name of overlay bundle file')),
3225 _('repository root directory or name of overlay bundle file')),
3222 ('', 'cwd', '', _('change working directory')),
3226 ('', 'cwd', '', _('change working directory')),
3223 ('y', 'noninteractive', None,
3227 ('y', 'noninteractive', None,
3224 _('do not prompt, assume \'yes\' for any required answers')),
3228 _('do not prompt, assume \'yes\' for any required answers')),
3225 ('q', 'quiet', None, _('suppress output')),
3229 ('q', 'quiet', None, _('suppress output')),
3226 ('v', 'verbose', None, _('enable additional output')),
3230 ('v', 'verbose', None, _('enable additional output')),
3227 ('', 'config', [], _('set/override config option')),
3231 ('', 'config', [], _('set/override config option')),
3228 ('', 'debug', None, _('enable debugging output')),
3232 ('', 'debug', None, _('enable debugging output')),
3229 ('', 'debugger', None, _('start debugger')),
3233 ('', 'debugger', None, _('start debugger')),
3230 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3234 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3231 ('', 'encodingmode', encoding.encodingmode,
3235 ('', 'encodingmode', encoding.encodingmode,
3232 _('set the charset encoding mode')),
3236 _('set the charset encoding mode')),
3233 ('', 'traceback', None, _('always print a traceback on exception')),
3237 ('', 'traceback', None, _('always print a traceback on exception')),
3234 ('', 'time', None, _('time how long the command takes')),
3238 ('', 'time', None, _('time how long the command takes')),
3235 ('', 'profile', None, _('print command execution profile')),
3239 ('', 'profile', None, _('print command execution profile')),
3236 ('', 'version', None, _('output version information and exit')),
3240 ('', 'version', None, _('output version information and exit')),
3237 ('h', 'help', None, _('display help and exit')),
3241 ('h', 'help', None, _('display help and exit')),
3238 ]
3242 ]
3239
3243
3240 dryrunopts = [('n', 'dry-run', None,
3244 dryrunopts = [('n', 'dry-run', None,
3241 _('do not perform actions, just print output'))]
3245 _('do not perform actions, just print output'))]
3242
3246
3243 remoteopts = [
3247 remoteopts = [
3244 ('e', 'ssh', '', _('specify ssh command to use')),
3248 ('e', 'ssh', '', _('specify ssh command to use')),
3245 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3249 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3246 ]
3250 ]
3247
3251
3248 walkopts = [
3252 walkopts = [
3249 ('I', 'include', [], _('include names matching the given patterns')),
3253 ('I', 'include', [], _('include names matching the given patterns')),
3250 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3254 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3251 ]
3255 ]
3252
3256
3253 commitopts = [
3257 commitopts = [
3254 ('m', 'message', '', _('use <text> as commit message')),
3258 ('m', 'message', '', _('use <text> as commit message')),
3255 ('l', 'logfile', '', _('read commit message from <file>')),
3259 ('l', 'logfile', '', _('read commit message from <file>')),
3256 ]
3260 ]
3257
3261
3258 commitopts2 = [
3262 commitopts2 = [
3259 ('d', 'date', '', _('record datecode as commit date')),
3263 ('d', 'date', '', _('record datecode as commit date')),
3260 ('u', 'user', '', _('record the specified user as committer')),
3264 ('u', 'user', '', _('record the specified user as committer')),
3261 ]
3265 ]
3262
3266
3263 templateopts = [
3267 templateopts = [
3264 ('', 'style', '', _('display using template map file')),
3268 ('', 'style', '', _('display using template map file')),
3265 ('', 'template', '', _('display with template')),
3269 ('', 'template', '', _('display with template')),
3266 ]
3270 ]
3267
3271
3268 logopts = [
3272 logopts = [
3269 ('p', 'patch', None, _('show patch')),
3273 ('p', 'patch', None, _('show patch')),
3270 ('g', 'git', None, _('use git extended diff format')),
3274 ('g', 'git', None, _('use git extended diff format')),
3271 ('l', 'limit', '', _('limit number of changes displayed')),
3275 ('l', 'limit', '', _('limit number of changes displayed')),
3272 ('M', 'no-merges', None, _('do not show merges')),
3276 ('M', 'no-merges', None, _('do not show merges')),
3273 ] + templateopts
3277 ] + templateopts
3274
3278
3275 diffopts = [
3279 diffopts = [
3276 ('a', 'text', None, _('treat all files as text')),
3280 ('a', 'text', None, _('treat all files as text')),
3277 ('g', 'git', None, _('use git extended diff format')),
3281 ('g', 'git', None, _('use git extended diff format')),
3278 ('', 'nodates', None, _("don't include dates in diff headers"))
3282 ('', 'nodates', None, _("don't include dates in diff headers"))
3279 ]
3283 ]
3280
3284
3281 diffopts2 = [
3285 diffopts2 = [
3282 ('p', 'show-function', None, _('show which function each change is in')),
3286 ('p', 'show-function', None, _('show which function each change is in')),
3287 ('', 'inverse', None, _('produce a diff that undoes the changes')),
3283 ('w', 'ignore-all-space', None,
3288 ('w', 'ignore-all-space', None,
3284 _('ignore white space when comparing lines')),
3289 _('ignore white space when comparing lines')),
3285 ('b', 'ignore-space-change', None,
3290 ('b', 'ignore-space-change', None,
3286 _('ignore changes in the amount of white space')),
3291 _('ignore changes in the amount of white space')),
3287 ('B', 'ignore-blank-lines', None,
3292 ('B', 'ignore-blank-lines', None,
3288 _('ignore changes whose lines are all blank')),
3293 _('ignore changes whose lines are all blank')),
3289 ('U', 'unified', '', _('number of lines of context to show')),
3294 ('U', 'unified', '', _('number of lines of context to show')),
3290 ('', 'stat', None, _('output diffstat-style summary of changes')),
3295 ('', 'stat', None, _('output diffstat-style summary of changes')),
3291 ]
3296 ]
3292
3297
3293 similarityopts = [
3298 similarityopts = [
3294 ('s', 'similarity', '',
3299 ('s', 'similarity', '',
3295 _('guess renamed files by similarity (0<=s<=100)'))
3300 _('guess renamed files by similarity (0<=s<=100)'))
3296 ]
3301 ]
3297
3302
3298 table = {
3303 table = {
3299 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3304 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3300 "addremove":
3305 "addremove":
3301 (addremove, similarityopts + walkopts + dryrunopts,
3306 (addremove, similarityopts + walkopts + dryrunopts,
3302 _('[OPTION]... [FILE]...')),
3307 _('[OPTION]... [FILE]...')),
3303 "^annotate|blame":
3308 "^annotate|blame":
3304 (annotate,
3309 (annotate,
3305 [('r', 'rev', '', _('annotate the specified revision')),
3310 [('r', 'rev', '', _('annotate the specified revision')),
3306 ('f', 'follow', None, _('follow file copies and renames')),
3311 ('f', 'follow', None, _('follow file copies and renames')),
3307 ('a', 'text', None, _('treat all files as text')),
3312 ('a', 'text', None, _('treat all files as text')),
3308 ('u', 'user', None, _('list the author (long with -v)')),
3313 ('u', 'user', None, _('list the author (long with -v)')),
3309 ('d', 'date', None, _('list the date (short with -q)')),
3314 ('d', 'date', None, _('list the date (short with -q)')),
3310 ('n', 'number', None, _('list the revision number (default)')),
3315 ('n', 'number', None, _('list the revision number (default)')),
3311 ('c', 'changeset', None, _('list the changeset')),
3316 ('c', 'changeset', None, _('list the changeset')),
3312 ('l', 'line-number', None,
3317 ('l', 'line-number', None,
3313 _('show line number at the first appearance'))
3318 _('show line number at the first appearance'))
3314 ] + walkopts,
3319 ] + walkopts,
3315 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3320 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3316 "archive":
3321 "archive":
3317 (archive,
3322 (archive,
3318 [('', 'no-decode', None, _('do not pass files through decoders')),
3323 [('', 'no-decode', None, _('do not pass files through decoders')),
3319 ('p', 'prefix', '', _('directory prefix for files in archive')),
3324 ('p', 'prefix', '', _('directory prefix for files in archive')),
3320 ('r', 'rev', '', _('revision to distribute')),
3325 ('r', 'rev', '', _('revision to distribute')),
3321 ('t', 'type', '', _('type of distribution to create')),
3326 ('t', 'type', '', _('type of distribution to create')),
3322 ] + walkopts,
3327 ] + walkopts,
3323 _('[OPTION]... DEST')),
3328 _('[OPTION]... DEST')),
3324 "backout":
3329 "backout":
3325 (backout,
3330 (backout,
3326 [('', 'merge', None,
3331 [('', 'merge', None,
3327 _('merge with old dirstate parent after backout')),
3332 _('merge with old dirstate parent after backout')),
3328 ('', 'parent', '', _('parent to choose when backing out merge')),
3333 ('', 'parent', '', _('parent to choose when backing out merge')),
3329 ('r', 'rev', '', _('revision to backout')),
3334 ('r', 'rev', '', _('revision to backout')),
3330 ] + walkopts + commitopts + commitopts2,
3335 ] + walkopts + commitopts + commitopts2,
3331 _('[OPTION]... [-r] REV')),
3336 _('[OPTION]... [-r] REV')),
3332 "bisect":
3337 "bisect":
3333 (bisect,
3338 (bisect,
3334 [('r', 'reset', False, _('reset bisect state')),
3339 [('r', 'reset', False, _('reset bisect state')),
3335 ('g', 'good', False, _('mark changeset good')),
3340 ('g', 'good', False, _('mark changeset good')),
3336 ('b', 'bad', False, _('mark changeset bad')),
3341 ('b', 'bad', False, _('mark changeset bad')),
3337 ('s', 'skip', False, _('skip testing changeset')),
3342 ('s', 'skip', False, _('skip testing changeset')),
3338 ('c', 'command', '', _('use command to check changeset state')),
3343 ('c', 'command', '', _('use command to check changeset state')),
3339 ('U', 'noupdate', False, _('do not update to target'))],
3344 ('U', 'noupdate', False, _('do not update to target'))],
3340 _("[-gbsr] [-c CMD] [REV]")),
3345 _("[-gbsr] [-c CMD] [REV]")),
3341 "branch":
3346 "branch":
3342 (branch,
3347 (branch,
3343 [('f', 'force', None,
3348 [('f', 'force', None,
3344 _('set branch name even if it shadows an existing branch')),
3349 _('set branch name even if it shadows an existing branch')),
3345 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3350 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3346 _('[-fC] [NAME]')),
3351 _('[-fC] [NAME]')),
3347 "branches":
3352 "branches":
3348 (branches,
3353 (branches,
3349 [('a', 'active', False,
3354 [('a', 'active', False,
3350 _('show only branches that have unmerged heads')),
3355 _('show only branches that have unmerged heads')),
3351 ('c', 'closed', False,
3356 ('c', 'closed', False,
3352 _('show normal and closed branches'))],
3357 _('show normal and closed branches'))],
3353 _('[-a]')),
3358 _('[-a]')),
3354 "bundle":
3359 "bundle":
3355 (bundle,
3360 (bundle,
3356 [('f', 'force', None,
3361 [('f', 'force', None,
3357 _('run even when remote repository is unrelated')),
3362 _('run even when remote repository is unrelated')),
3358 ('r', 'rev', [],
3363 ('r', 'rev', [],
3359 _('a changeset up to which you would like to bundle')),
3364 _('a changeset up to which you would like to bundle')),
3360 ('', 'base', [],
3365 ('', 'base', [],
3361 _('a base changeset to specify instead of a destination')),
3366 _('a base changeset to specify instead of a destination')),
3362 ('a', 'all', None, _('bundle all changesets in the repository')),
3367 ('a', 'all', None, _('bundle all changesets in the repository')),
3363 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3368 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3364 ] + remoteopts,
3369 ] + remoteopts,
3365 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3370 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3366 "cat":
3371 "cat":
3367 (cat,
3372 (cat,
3368 [('o', 'output', '', _('print output to file with formatted name')),
3373 [('o', 'output', '', _('print output to file with formatted name')),
3369 ('r', 'rev', '', _('print the given revision')),
3374 ('r', 'rev', '', _('print the given revision')),
3370 ('', 'decode', None, _('apply any matching decode filter')),
3375 ('', 'decode', None, _('apply any matching decode filter')),
3371 ] + walkopts,
3376 ] + walkopts,
3372 _('[OPTION]... FILE...')),
3377 _('[OPTION]... FILE...')),
3373 "^clone":
3378 "^clone":
3374 (clone,
3379 (clone,
3375 [('U', 'noupdate', None,
3380 [('U', 'noupdate', None,
3376 _('the clone will only contain a repository (no working copy)')),
3381 _('the clone will only contain a repository (no working copy)')),
3377 ('u', 'updaterev', '',
3382 ('u', 'updaterev', '',
3378 _('revision, tag or branch to check out')),
3383 _('revision, tag or branch to check out')),
3379 ('r', 'rev', [],
3384 ('r', 'rev', [],
3380 _('a changeset you would like to have after cloning')),
3385 _('a changeset you would like to have after cloning')),
3381 ('', 'pull', None, _('use pull protocol to copy metadata')),
3386 ('', 'pull', None, _('use pull protocol to copy metadata')),
3382 ('', 'uncompressed', None,
3387 ('', 'uncompressed', None,
3383 _('use uncompressed transfer (fast over LAN)')),
3388 _('use uncompressed transfer (fast over LAN)')),
3384 ] + remoteopts,
3389 ] + remoteopts,
3385 _('[OPTION]... SOURCE [DEST]')),
3390 _('[OPTION]... SOURCE [DEST]')),
3386 "^commit|ci":
3391 "^commit|ci":
3387 (commit,
3392 (commit,
3388 [('A', 'addremove', None,
3393 [('A', 'addremove', None,
3389 _('mark new/missing files as added/removed before committing')),
3394 _('mark new/missing files as added/removed before committing')),
3390 ('', 'close-branch', None,
3395 ('', 'close-branch', None,
3391 _('mark a branch as closed, hiding it from the branch list')),
3396 _('mark a branch as closed, hiding it from the branch list')),
3392 ] + walkopts + commitopts + commitopts2,
3397 ] + walkopts + commitopts + commitopts2,
3393 _('[OPTION]... [FILE]...')),
3398 _('[OPTION]... [FILE]...')),
3394 "copy|cp":
3399 "copy|cp":
3395 (copy,
3400 (copy,
3396 [('A', 'after', None, _('record a copy that has already occurred')),
3401 [('A', 'after', None, _('record a copy that has already occurred')),
3397 ('f', 'force', None,
3402 ('f', 'force', None,
3398 _('forcibly copy over an existing managed file')),
3403 _('forcibly copy over an existing managed file')),
3399 ] + walkopts + dryrunopts,
3404 ] + walkopts + dryrunopts,
3400 _('[OPTION]... [SOURCE]... DEST')),
3405 _('[OPTION]... [SOURCE]... DEST')),
3401 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3406 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3402 "debugcheckstate": (debugcheckstate, [], ''),
3407 "debugcheckstate": (debugcheckstate, [], ''),
3403 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3408 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3404 "debugcomplete":
3409 "debugcomplete":
3405 (debugcomplete,
3410 (debugcomplete,
3406 [('o', 'options', None, _('show the command options'))],
3411 [('o', 'options', None, _('show the command options'))],
3407 _('[-o] CMD')),
3412 _('[-o] CMD')),
3408 "debugdate":
3413 "debugdate":
3409 (debugdate,
3414 (debugdate,
3410 [('e', 'extended', None, _('try extended date formats'))],
3415 [('e', 'extended', None, _('try extended date formats'))],
3411 _('[-e] DATE [RANGE]')),
3416 _('[-e] DATE [RANGE]')),
3412 "debugdata": (debugdata, [], _('FILE REV')),
3417 "debugdata": (debugdata, [], _('FILE REV')),
3413 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3418 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3414 "debugindex": (debugindex, [], _('FILE')),
3419 "debugindex": (debugindex, [], _('FILE')),
3415 "debugindexdot": (debugindexdot, [], _('FILE')),
3420 "debugindexdot": (debugindexdot, [], _('FILE')),
3416 "debuginstall": (debuginstall, [], ''),
3421 "debuginstall": (debuginstall, [], ''),
3417 "debugrebuildstate":
3422 "debugrebuildstate":
3418 (debugrebuildstate,
3423 (debugrebuildstate,
3419 [('r', 'rev', '', _('revision to rebuild to'))],
3424 [('r', 'rev', '', _('revision to rebuild to'))],
3420 _('[-r REV] [REV]')),
3425 _('[-r REV] [REV]')),
3421 "debugrename":
3426 "debugrename":
3422 (debugrename,
3427 (debugrename,
3423 [('r', 'rev', '', _('revision to debug'))],
3428 [('r', 'rev', '', _('revision to debug'))],
3424 _('[-r REV] FILE')),
3429 _('[-r REV] FILE')),
3425 "debugsetparents":
3430 "debugsetparents":
3426 (debugsetparents, [], _('REV1 [REV2]')),
3431 (debugsetparents, [], _('REV1 [REV2]')),
3427 "debugstate":
3432 "debugstate":
3428 (debugstate,
3433 (debugstate,
3429 [('', 'nodates', None, _('do not display the saved mtime'))],
3434 [('', 'nodates', None, _('do not display the saved mtime'))],
3430 _('[OPTION]...')),
3435 _('[OPTION]...')),
3431 "debugsub":
3436 "debugsub":
3432 (debugsub,
3437 (debugsub,
3433 [('r', 'rev', '', _('revision to check'))],
3438 [('r', 'rev', '', _('revision to check'))],
3434 _('[-r REV] [REV]')),
3439 _('[-r REV] [REV]')),
3435 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3440 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3436 "^diff":
3441 "^diff":
3437 (diff,
3442 (diff,
3438 [('r', 'rev', [], _('revision')),
3443 [('r', 'rev', [], _('revision')),
3439 ('c', 'change', '', _('change made by revision'))
3444 ('c', 'change', '', _('change made by revision'))
3440 ] + diffopts + diffopts2 + walkopts,
3445 ] + diffopts + diffopts2 + walkopts,
3441 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3446 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3442 "^export":
3447 "^export":
3443 (export,
3448 (export,
3444 [('o', 'output', '', _('print output to file with formatted name')),
3449 [('o', 'output', '', _('print output to file with formatted name')),
3445 ('', 'switch-parent', None, _('diff against the second parent'))
3450 ('', 'switch-parent', None, _('diff against the second parent'))
3446 ] + diffopts,
3451 ] + diffopts,
3447 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3452 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3448 "^forget":
3453 "^forget":
3449 (forget,
3454 (forget,
3450 [] + walkopts,
3455 [] + walkopts,
3451 _('[OPTION]... FILE...')),
3456 _('[OPTION]... FILE...')),
3452 "grep":
3457 "grep":
3453 (grep,
3458 (grep,
3454 [('0', 'print0', None, _('end fields with NUL')),
3459 [('0', 'print0', None, _('end fields with NUL')),
3455 ('', 'all', None, _('print all revisions that match')),
3460 ('', 'all', None, _('print all revisions that match')),
3456 ('f', 'follow', None,
3461 ('f', 'follow', None,
3457 _('follow changeset history, or file history across copies and renames')),
3462 _('follow changeset history, or file history across copies and renames')),
3458 ('i', 'ignore-case', None, _('ignore case when matching')),
3463 ('i', 'ignore-case', None, _('ignore case when matching')),
3459 ('l', 'files-with-matches', None,
3464 ('l', 'files-with-matches', None,
3460 _('print only filenames and revisions that match')),
3465 _('print only filenames and revisions that match')),
3461 ('n', 'line-number', None, _('print matching line numbers')),
3466 ('n', 'line-number', None, _('print matching line numbers')),
3462 ('r', 'rev', [], _('search in given revision range')),
3467 ('r', 'rev', [], _('search in given revision range')),
3463 ('u', 'user', None, _('list the author (long with -v)')),
3468 ('u', 'user', None, _('list the author (long with -v)')),
3464 ('d', 'date', None, _('list the date (short with -q)')),
3469 ('d', 'date', None, _('list the date (short with -q)')),
3465 ] + walkopts,
3470 ] + walkopts,
3466 _('[OPTION]... PATTERN [FILE]...')),
3471 _('[OPTION]... PATTERN [FILE]...')),
3467 "heads":
3472 "heads":
3468 (heads,
3473 (heads,
3469 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3474 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3470 ('a', 'active', False,
3475 ('a', 'active', False,
3471 _('show only the active branch heads from open branches')),
3476 _('show only the active branch heads from open branches')),
3472 ('c', 'closed', False,
3477 ('c', 'closed', False,
3473 _('show normal and closed branch heads')),
3478 _('show normal and closed branch heads')),
3474 ] + templateopts,
3479 ] + templateopts,
3475 _('[-r STARTREV] [REV]...')),
3480 _('[-r STARTREV] [REV]...')),
3476 "help": (help_, [], _('[TOPIC]')),
3481 "help": (help_, [], _('[TOPIC]')),
3477 "identify|id":
3482 "identify|id":
3478 (identify,
3483 (identify,
3479 [('r', 'rev', '', _('identify the specified revision')),
3484 [('r', 'rev', '', _('identify the specified revision')),
3480 ('n', 'num', None, _('show local revision number')),
3485 ('n', 'num', None, _('show local revision number')),
3481 ('i', 'id', None, _('show global revision id')),
3486 ('i', 'id', None, _('show global revision id')),
3482 ('b', 'branch', None, _('show branch')),
3487 ('b', 'branch', None, _('show branch')),
3483 ('t', 'tags', None, _('show tags'))],
3488 ('t', 'tags', None, _('show tags'))],
3484 _('[-nibt] [-r REV] [SOURCE]')),
3489 _('[-nibt] [-r REV] [SOURCE]')),
3485 "import|patch":
3490 "import|patch":
3486 (import_,
3491 (import_,
3487 [('p', 'strip', 1,
3492 [('p', 'strip', 1,
3488 _('directory strip option for patch. This has the same '
3493 _('directory strip option for patch. This has the same '
3489 'meaning as the corresponding patch option')),
3494 'meaning as the corresponding patch option')),
3490 ('b', 'base', '', _('base path')),
3495 ('b', 'base', '', _('base path')),
3491 ('f', 'force', None,
3496 ('f', 'force', None,
3492 _('skip check for outstanding uncommitted changes')),
3497 _('skip check for outstanding uncommitted changes')),
3493 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3498 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3494 ('', 'exact', None,
3499 ('', 'exact', None,
3495 _('apply patch to the nodes from which it was generated')),
3500 _('apply patch to the nodes from which it was generated')),
3496 ('', 'import-branch', None,
3501 ('', 'import-branch', None,
3497 _('use any branch information in patch (implied by --exact)'))] +
3502 _('use any branch information in patch (implied by --exact)'))] +
3498 commitopts + commitopts2 + similarityopts,
3503 commitopts + commitopts2 + similarityopts,
3499 _('[OPTION]... PATCH...')),
3504 _('[OPTION]... PATCH...')),
3500 "incoming|in":
3505 "incoming|in":
3501 (incoming,
3506 (incoming,
3502 [('f', 'force', None,
3507 [('f', 'force', None,
3503 _('run even when remote repository is unrelated')),
3508 _('run even when remote repository is unrelated')),
3504 ('n', 'newest-first', None, _('show newest record first')),
3509 ('n', 'newest-first', None, _('show newest record first')),
3505 ('', 'bundle', '', _('file to store the bundles into')),
3510 ('', 'bundle', '', _('file to store the bundles into')),
3506 ('r', 'rev', [],
3511 ('r', 'rev', [],
3507 _('a specific remote revision up to which you would like to pull')),
3512 _('a specific remote revision up to which you would like to pull')),
3508 ] + logopts + remoteopts,
3513 ] + logopts + remoteopts,
3509 _('[-p] [-n] [-M] [-f] [-r REV]...'
3514 _('[-p] [-n] [-M] [-f] [-r REV]...'
3510 ' [--bundle FILENAME] [SOURCE]')),
3515 ' [--bundle FILENAME] [SOURCE]')),
3511 "^init":
3516 "^init":
3512 (init,
3517 (init,
3513 remoteopts,
3518 remoteopts,
3514 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3519 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3515 "locate":
3520 "locate":
3516 (locate,
3521 (locate,
3517 [('r', 'rev', '', _('search the repository as it stood at REV')),
3522 [('r', 'rev', '', _('search the repository as it stood at REV')),
3518 ('0', 'print0', None,
3523 ('0', 'print0', None,
3519 _('end filenames with NUL, for use with xargs')),
3524 _('end filenames with NUL, for use with xargs')),
3520 ('f', 'fullpath', None,
3525 ('f', 'fullpath', None,
3521 _('print complete paths from the filesystem root')),
3526 _('print complete paths from the filesystem root')),
3522 ] + walkopts,
3527 ] + walkopts,
3523 _('[OPTION]... [PATTERN]...')),
3528 _('[OPTION]... [PATTERN]...')),
3524 "^log|history":
3529 "^log|history":
3525 (log,
3530 (log,
3526 [('f', 'follow', None,
3531 [('f', 'follow', None,
3527 _('follow changeset history, or file history across copies and renames')),
3532 _('follow changeset history, or file history across copies and renames')),
3528 ('', 'follow-first', None,
3533 ('', 'follow-first', None,
3529 _('only follow the first parent of merge changesets')),
3534 _('only follow the first parent of merge changesets')),
3530 ('d', 'date', '', _('show revisions matching date spec')),
3535 ('d', 'date', '', _('show revisions matching date spec')),
3531 ('C', 'copies', None, _('show copied files')),
3536 ('C', 'copies', None, _('show copied files')),
3532 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3537 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3533 ('r', 'rev', [], _('show the specified revision or range')),
3538 ('r', 'rev', [], _('show the specified revision or range')),
3534 ('', 'removed', None, _('include revisions where files were removed')),
3539 ('', 'removed', None, _('include revisions where files were removed')),
3535 ('m', 'only-merges', None, _('show only merges')),
3540 ('m', 'only-merges', None, _('show only merges')),
3536 ('u', 'user', [], _('revisions committed by user')),
3541 ('u', 'user', [], _('revisions committed by user')),
3537 ('b', 'only-branch', [],
3542 ('b', 'only-branch', [],
3538 _('show only changesets within the given named branch')),
3543 _('show only changesets within the given named branch')),
3539 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3544 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3540 ] + logopts + walkopts,
3545 ] + logopts + walkopts,
3541 _('[OPTION]... [FILE]')),
3546 _('[OPTION]... [FILE]')),
3542 "manifest":
3547 "manifest":
3543 (manifest,
3548 (manifest,
3544 [('r', 'rev', '', _('revision to display'))],
3549 [('r', 'rev', '', _('revision to display'))],
3545 _('[-r REV]')),
3550 _('[-r REV]')),
3546 "^merge":
3551 "^merge":
3547 (merge,
3552 (merge,
3548 [('f', 'force', None, _('force a merge with outstanding changes')),
3553 [('f', 'force', None, _('force a merge with outstanding changes')),
3549 ('r', 'rev', '', _('revision to merge')),
3554 ('r', 'rev', '', _('revision to merge')),
3550 ('P', 'preview', None,
3555 ('P', 'preview', None,
3551 _('review revisions to merge (no merge is performed)'))],
3556 _('review revisions to merge (no merge is performed)'))],
3552 _('[-f] [[-r] REV]')),
3557 _('[-f] [[-r] REV]')),
3553 "outgoing|out":
3558 "outgoing|out":
3554 (outgoing,
3559 (outgoing,
3555 [('f', 'force', None,
3560 [('f', 'force', None,
3556 _('run even when remote repository is unrelated')),
3561 _('run even when remote repository is unrelated')),
3557 ('r', 'rev', [],
3562 ('r', 'rev', [],
3558 _('a specific revision up to which you would like to push')),
3563 _('a specific revision up to which you would like to push')),
3559 ('n', 'newest-first', None, _('show newest record first')),
3564 ('n', 'newest-first', None, _('show newest record first')),
3560 ] + logopts + remoteopts,
3565 ] + logopts + remoteopts,
3561 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3566 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3562 "parents":
3567 "parents":
3563 (parents,
3568 (parents,
3564 [('r', 'rev', '', _('show parents from the specified revision')),
3569 [('r', 'rev', '', _('show parents from the specified revision')),
3565 ] + templateopts,
3570 ] + templateopts,
3566 _('[-r REV] [FILE]')),
3571 _('[-r REV] [FILE]')),
3567 "paths": (paths, [], _('[NAME]')),
3572 "paths": (paths, [], _('[NAME]')),
3568 "^pull":
3573 "^pull":
3569 (pull,
3574 (pull,
3570 [('u', 'update', None,
3575 [('u', 'update', None,
3571 _('update to new tip if changesets were pulled')),
3576 _('update to new tip if changesets were pulled')),
3572 ('f', 'force', None,
3577 ('f', 'force', None,
3573 _('run even when remote repository is unrelated')),
3578 _('run even when remote repository is unrelated')),
3574 ('r', 'rev', [],
3579 ('r', 'rev', [],
3575 _('a specific remote revision up to which you would like to pull')),
3580 _('a specific remote revision up to which you would like to pull')),
3576 ] + remoteopts,
3581 ] + remoteopts,
3577 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3582 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3578 "^push":
3583 "^push":
3579 (push,
3584 (push,
3580 [('f', 'force', None, _('force push')),
3585 [('f', 'force', None, _('force push')),
3581 ('r', 'rev', [],
3586 ('r', 'rev', [],
3582 _('a specific revision up to which you would like to push')),
3587 _('a specific revision up to which you would like to push')),
3583 ] + remoteopts,
3588 ] + remoteopts,
3584 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3589 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3585 "recover": (recover, []),
3590 "recover": (recover, []),
3586 "^remove|rm":
3591 "^remove|rm":
3587 (remove,
3592 (remove,
3588 [('A', 'after', None, _('record delete for missing files')),
3593 [('A', 'after', None, _('record delete for missing files')),
3589 ('f', 'force', None,
3594 ('f', 'force', None,
3590 _('remove (and delete) file even if added or modified')),
3595 _('remove (and delete) file even if added or modified')),
3591 ] + walkopts,
3596 ] + walkopts,
3592 _('[OPTION]... FILE...')),
3597 _('[OPTION]... FILE...')),
3593 "rename|mv":
3598 "rename|mv":
3594 (rename,
3599 (rename,
3595 [('A', 'after', None, _('record a rename that has already occurred')),
3600 [('A', 'after', None, _('record a rename that has already occurred')),
3596 ('f', 'force', None,
3601 ('f', 'force', None,
3597 _('forcibly copy over an existing managed file')),
3602 _('forcibly copy over an existing managed file')),
3598 ] + walkopts + dryrunopts,
3603 ] + walkopts + dryrunopts,
3599 _('[OPTION]... SOURCE... DEST')),
3604 _('[OPTION]... SOURCE... DEST')),
3600 "resolve":
3605 "resolve":
3601 (resolve,
3606 (resolve,
3602 [('a', 'all', None, _('remerge all unresolved files')),
3607 [('a', 'all', None, _('remerge all unresolved files')),
3603 ('l', 'list', None, _('list state of files needing merge')),
3608 ('l', 'list', None, _('list state of files needing merge')),
3604 ('m', 'mark', None, _('mark files as resolved')),
3609 ('m', 'mark', None, _('mark files as resolved')),
3605 ('u', 'unmark', None, _('unmark files as resolved')),
3610 ('u', 'unmark', None, _('unmark files as resolved')),
3606 ('n', 'no-status', None, _('hide status prefix'))]
3611 ('n', 'no-status', None, _('hide status prefix'))]
3607 + walkopts,
3612 + walkopts,
3608 _('[OPTION]... [FILE]...')),
3613 _('[OPTION]... [FILE]...')),
3609 "revert":
3614 "revert":
3610 (revert,
3615 (revert,
3611 [('a', 'all', None, _('revert all changes when no arguments given')),
3616 [('a', 'all', None, _('revert all changes when no arguments given')),
3612 ('d', 'date', '', _('tipmost revision matching date')),
3617 ('d', 'date', '', _('tipmost revision matching date')),
3613 ('r', 'rev', '', _('revision to revert to')),
3618 ('r', 'rev', '', _('revision to revert to')),
3614 ('', 'no-backup', None, _('do not save backup copies of files')),
3619 ('', 'no-backup', None, _('do not save backup copies of files')),
3615 ] + walkopts + dryrunopts,
3620 ] + walkopts + dryrunopts,
3616 _('[OPTION]... [-r REV] [NAME]...')),
3621 _('[OPTION]... [-r REV] [NAME]...')),
3617 "rollback": (rollback, []),
3622 "rollback": (rollback, []),
3618 "root": (root, []),
3623 "root": (root, []),
3619 "^serve":
3624 "^serve":
3620 (serve,
3625 (serve,
3621 [('A', 'accesslog', '', _('name of access log file to write to')),
3626 [('A', 'accesslog', '', _('name of access log file to write to')),
3622 ('d', 'daemon', None, _('run server in background')),
3627 ('d', 'daemon', None, _('run server in background')),
3623 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3628 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3624 ('E', 'errorlog', '', _('name of error log file to write to')),
3629 ('E', 'errorlog', '', _('name of error log file to write to')),
3625 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3630 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3626 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3631 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3627 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3632 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3628 ('n', 'name', '',
3633 ('n', 'name', '',
3629 _('name to show in web pages (default: working directory)')),
3634 _('name to show in web pages (default: working directory)')),
3630 ('', 'webdir-conf', '', _('name of the webdir config file'
3635 ('', 'webdir-conf', '', _('name of the webdir config file'
3631 ' (serve more than one repository)')),
3636 ' (serve more than one repository)')),
3632 ('', 'pid-file', '', _('name of file to write process ID to')),
3637 ('', 'pid-file', '', _('name of file to write process ID to')),
3633 ('', 'stdio', None, _('for remote clients')),
3638 ('', 'stdio', None, _('for remote clients')),
3634 ('t', 'templates', '', _('web templates to use')),
3639 ('t', 'templates', '', _('web templates to use')),
3635 ('', 'style', '', _('template style to use')),
3640 ('', 'style', '', _('template style to use')),
3636 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3641 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3637 ('', 'certificate', '', _('SSL certificate file'))],
3642 ('', 'certificate', '', _('SSL certificate file'))],
3638 _('[OPTION]...')),
3643 _('[OPTION]...')),
3639 "showconfig|debugconfig":
3644 "showconfig|debugconfig":
3640 (showconfig,
3645 (showconfig,
3641 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3646 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3642 _('[-u] [NAME]...')),
3647 _('[-u] [NAME]...')),
3643 "^summary|sum":
3648 "^summary|sum":
3644 (summary,
3649 (summary,
3645 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3650 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3646 "^status|st":
3651 "^status|st":
3647 (status,
3652 (status,
3648 [('A', 'all', None, _('show status of all files')),
3653 [('A', 'all', None, _('show status of all files')),
3649 ('m', 'modified', None, _('show only modified files')),
3654 ('m', 'modified', None, _('show only modified files')),
3650 ('a', 'added', None, _('show only added files')),
3655 ('a', 'added', None, _('show only added files')),
3651 ('r', 'removed', None, _('show only removed files')),
3656 ('r', 'removed', None, _('show only removed files')),
3652 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3657 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3653 ('c', 'clean', None, _('show only files without changes')),
3658 ('c', 'clean', None, _('show only files without changes')),
3654 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3659 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3655 ('i', 'ignored', None, _('show only ignored files')),
3660 ('i', 'ignored', None, _('show only ignored files')),
3656 ('n', 'no-status', None, _('hide status prefix')),
3661 ('n', 'no-status', None, _('hide status prefix')),
3657 ('C', 'copies', None, _('show source of copied files')),
3662 ('C', 'copies', None, _('show source of copied files')),
3658 ('0', 'print0', None,
3663 ('0', 'print0', None,
3659 _('end filenames with NUL, for use with xargs')),
3664 _('end filenames with NUL, for use with xargs')),
3660 ('', 'rev', [], _('show difference from revision')),
3665 ('', 'rev', [], _('show difference from revision')),
3661 ] + walkopts,
3666 ] + walkopts,
3662 _('[OPTION]... [FILE]...')),
3667 _('[OPTION]... [FILE]...')),
3663 "tag":
3668 "tag":
3664 (tag,
3669 (tag,
3665 [('f', 'force', None, _('replace existing tag')),
3670 [('f', 'force', None, _('replace existing tag')),
3666 ('l', 'local', None, _('make the tag local')),
3671 ('l', 'local', None, _('make the tag local')),
3667 ('r', 'rev', '', _('revision to tag')),
3672 ('r', 'rev', '', _('revision to tag')),
3668 ('', 'remove', None, _('remove a tag')),
3673 ('', 'remove', None, _('remove a tag')),
3669 # -l/--local is already there, commitopts cannot be used
3674 # -l/--local is already there, commitopts cannot be used
3670 ('m', 'message', '', _('use <text> as commit message')),
3675 ('m', 'message', '', _('use <text> as commit message')),
3671 ] + commitopts2,
3676 ] + commitopts2,
3672 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3677 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3673 "tags": (tags, [], ''),
3678 "tags": (tags, [], ''),
3674 "tip":
3679 "tip":
3675 (tip,
3680 (tip,
3676 [('p', 'patch', None, _('show patch')),
3681 [('p', 'patch', None, _('show patch')),
3677 ('g', 'git', None, _('use git extended diff format')),
3682 ('g', 'git', None, _('use git extended diff format')),
3678 ] + templateopts,
3683 ] + templateopts,
3679 _('[-p]')),
3684 _('[-p]')),
3680 "unbundle":
3685 "unbundle":
3681 (unbundle,
3686 (unbundle,
3682 [('u', 'update', None,
3687 [('u', 'update', None,
3683 _('update to new tip if changesets were unbundled'))],
3688 _('update to new tip if changesets were unbundled'))],
3684 _('[-u] FILE...')),
3689 _('[-u] FILE...')),
3685 "^update|up|checkout|co":
3690 "^update|up|checkout|co":
3686 (update,
3691 (update,
3687 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3692 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3688 ('c', 'check', None, _('check for uncommitted changes')),
3693 ('c', 'check', None, _('check for uncommitted changes')),
3689 ('d', 'date', '', _('tipmost revision matching date')),
3694 ('d', 'date', '', _('tipmost revision matching date')),
3690 ('r', 'rev', '', _('revision'))],
3695 ('r', 'rev', '', _('revision'))],
3691 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3696 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3692 "verify": (verify, []),
3697 "verify": (verify, []),
3693 "version": (version_, []),
3698 "version": (version_, []),
3694 }
3699 }
3695
3700
3696 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3701 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3697 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3702 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3698 optionalrepo = ("identify paths serve showconfig debugancestor")
3703 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,1430 +1,1430 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 from i18n import _
9 from i18n import _
10 from node import hex, nullid, short
10 from node import hex, nullid, short
11 import base85, cmdutil, mdiff, util, diffhelpers, copies
11 import base85, cmdutil, mdiff, util, diffhelpers, copies
12 import cStringIO, email.Parser, os, re
12 import cStringIO, email.Parser, os, re
13 import sys, tempfile, zlib
13 import sys, tempfile, zlib
14
14
15 gitre = re.compile('diff --git a/(.*) b/(.*)')
15 gitre = re.compile('diff --git a/(.*) b/(.*)')
16
16
17 class PatchError(Exception):
17 class PatchError(Exception):
18 pass
18 pass
19
19
20 class NoHunks(PatchError):
20 class NoHunks(PatchError):
21 pass
21 pass
22
22
23 # helper functions
23 # helper functions
24
24
25 def copyfile(src, dst, basedir):
25 def copyfile(src, dst, basedir):
26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
27 if os.path.exists(absdst):
27 if os.path.exists(absdst):
28 raise util.Abort(_("cannot create %s: destination already exists") %
28 raise util.Abort(_("cannot create %s: destination already exists") %
29 dst)
29 dst)
30
30
31 dstdir = os.path.dirname(absdst)
31 dstdir = os.path.dirname(absdst)
32 if dstdir and not os.path.isdir(dstdir):
32 if dstdir and not os.path.isdir(dstdir):
33 try:
33 try:
34 os.makedirs(dstdir)
34 os.makedirs(dstdir)
35 except IOError:
35 except IOError:
36 raise util.Abort(
36 raise util.Abort(
37 _("cannot create %s: unable to create destination directory")
37 _("cannot create %s: unable to create destination directory")
38 % dst)
38 % dst)
39
39
40 util.copyfile(abssrc, absdst)
40 util.copyfile(abssrc, absdst)
41
41
42 # public functions
42 # public functions
43
43
44 def extract(ui, fileobj):
44 def extract(ui, fileobj):
45 '''extract patch from data read from fileobj.
45 '''extract patch from data read from fileobj.
46
46
47 patch can be a normal patch or contained in an email message.
47 patch can be a normal patch or contained in an email message.
48
48
49 return tuple (filename, message, user, date, node, p1, p2).
49 return tuple (filename, message, user, date, node, p1, p2).
50 Any item in the returned tuple can be None. If filename is None,
50 Any item in the returned tuple can be None. If filename is None,
51 fileobj did not contain a patch. Caller must unlink filename when done.'''
51 fileobj did not contain a patch. Caller must unlink filename when done.'''
52
52
53 # attempt to detect the start of a patch
53 # attempt to detect the start of a patch
54 # (this heuristic is borrowed from quilt)
54 # (this heuristic is borrowed from quilt)
55 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
55 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
56 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
56 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
57 r'(---|\*\*\*)[ \t])', re.MULTILINE)
57 r'(---|\*\*\*)[ \t])', re.MULTILINE)
58
58
59 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
59 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
60 tmpfp = os.fdopen(fd, 'w')
60 tmpfp = os.fdopen(fd, 'w')
61 try:
61 try:
62 msg = email.Parser.Parser().parse(fileobj)
62 msg = email.Parser.Parser().parse(fileobj)
63
63
64 subject = msg['Subject']
64 subject = msg['Subject']
65 user = msg['From']
65 user = msg['From']
66 if not subject and not user:
66 if not subject and not user:
67 # Not an email, restore parsed headers if any
67 # Not an email, restore parsed headers if any
68 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
68 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
69
69
70 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
70 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
71 # should try to parse msg['Date']
71 # should try to parse msg['Date']
72 date = None
72 date = None
73 nodeid = None
73 nodeid = None
74 branch = None
74 branch = None
75 parents = []
75 parents = []
76
76
77 if subject:
77 if subject:
78 if subject.startswith('[PATCH'):
78 if subject.startswith('[PATCH'):
79 pend = subject.find(']')
79 pend = subject.find(']')
80 if pend >= 0:
80 if pend >= 0:
81 subject = subject[pend+1:].lstrip()
81 subject = subject[pend+1:].lstrip()
82 subject = subject.replace('\n\t', ' ')
82 subject = subject.replace('\n\t', ' ')
83 ui.debug('Subject: %s\n' % subject)
83 ui.debug('Subject: %s\n' % subject)
84 if user:
84 if user:
85 ui.debug('From: %s\n' % user)
85 ui.debug('From: %s\n' % user)
86 diffs_seen = 0
86 diffs_seen = 0
87 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
87 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
88 message = ''
88 message = ''
89 for part in msg.walk():
89 for part in msg.walk():
90 content_type = part.get_content_type()
90 content_type = part.get_content_type()
91 ui.debug('Content-Type: %s\n' % content_type)
91 ui.debug('Content-Type: %s\n' % content_type)
92 if content_type not in ok_types:
92 if content_type not in ok_types:
93 continue
93 continue
94 payload = part.get_payload(decode=True)
94 payload = part.get_payload(decode=True)
95 m = diffre.search(payload)
95 m = diffre.search(payload)
96 if m:
96 if m:
97 hgpatch = False
97 hgpatch = False
98 ignoretext = False
98 ignoretext = False
99
99
100 ui.debug('found patch at byte %d\n' % m.start(0))
100 ui.debug('found patch at byte %d\n' % m.start(0))
101 diffs_seen += 1
101 diffs_seen += 1
102 cfp = cStringIO.StringIO()
102 cfp = cStringIO.StringIO()
103 for line in payload[:m.start(0)].splitlines():
103 for line in payload[:m.start(0)].splitlines():
104 if line.startswith('# HG changeset patch'):
104 if line.startswith('# HG changeset patch'):
105 ui.debug('patch generated by hg export\n')
105 ui.debug('patch generated by hg export\n')
106 hgpatch = True
106 hgpatch = True
107 # drop earlier commit message content
107 # drop earlier commit message content
108 cfp.seek(0)
108 cfp.seek(0)
109 cfp.truncate()
109 cfp.truncate()
110 subject = None
110 subject = None
111 elif hgpatch:
111 elif hgpatch:
112 if line.startswith('# User '):
112 if line.startswith('# User '):
113 user = line[7:]
113 user = line[7:]
114 ui.debug('From: %s\n' % user)
114 ui.debug('From: %s\n' % user)
115 elif line.startswith("# Date "):
115 elif line.startswith("# Date "):
116 date = line[7:]
116 date = line[7:]
117 elif line.startswith("# Branch "):
117 elif line.startswith("# Branch "):
118 branch = line[9:]
118 branch = line[9:]
119 elif line.startswith("# Node ID "):
119 elif line.startswith("# Node ID "):
120 nodeid = line[10:]
120 nodeid = line[10:]
121 elif line.startswith("# Parent "):
121 elif line.startswith("# Parent "):
122 parents.append(line[10:])
122 parents.append(line[10:])
123 elif line == '---' and gitsendmail:
123 elif line == '---' and gitsendmail:
124 ignoretext = True
124 ignoretext = True
125 if not line.startswith('# ') and not ignoretext:
125 if not line.startswith('# ') and not ignoretext:
126 cfp.write(line)
126 cfp.write(line)
127 cfp.write('\n')
127 cfp.write('\n')
128 message = cfp.getvalue()
128 message = cfp.getvalue()
129 if tmpfp:
129 if tmpfp:
130 tmpfp.write(payload)
130 tmpfp.write(payload)
131 if not payload.endswith('\n'):
131 if not payload.endswith('\n'):
132 tmpfp.write('\n')
132 tmpfp.write('\n')
133 elif not diffs_seen and message and content_type == 'text/plain':
133 elif not diffs_seen and message and content_type == 'text/plain':
134 message += '\n' + payload
134 message += '\n' + payload
135 except:
135 except:
136 tmpfp.close()
136 tmpfp.close()
137 os.unlink(tmpname)
137 os.unlink(tmpname)
138 raise
138 raise
139
139
140 if subject and not message.startswith(subject):
140 if subject and not message.startswith(subject):
141 message = '%s\n%s' % (subject, message)
141 message = '%s\n%s' % (subject, message)
142 tmpfp.close()
142 tmpfp.close()
143 if not diffs_seen:
143 if not diffs_seen:
144 os.unlink(tmpname)
144 os.unlink(tmpname)
145 return None, message, user, date, branch, None, None, None
145 return None, message, user, date, branch, None, None, None
146 p1 = parents and parents.pop(0) or None
146 p1 = parents and parents.pop(0) or None
147 p2 = parents and parents.pop(0) or None
147 p2 = parents and parents.pop(0) or None
148 return tmpname, message, user, date, branch, nodeid, p1, p2
148 return tmpname, message, user, date, branch, nodeid, p1, p2
149
149
150 GP_PATCH = 1 << 0 # we have to run patch
150 GP_PATCH = 1 << 0 # we have to run patch
151 GP_FILTER = 1 << 1 # there's some copy/rename operation
151 GP_FILTER = 1 << 1 # there's some copy/rename operation
152 GP_BINARY = 1 << 2 # there's a binary patch
152 GP_BINARY = 1 << 2 # there's a binary patch
153
153
154 class patchmeta(object):
154 class patchmeta(object):
155 """Patched file metadata
155 """Patched file metadata
156
156
157 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
157 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
158 or COPY. 'path' is patched file path. 'oldpath' is set to the
158 or COPY. 'path' is patched file path. 'oldpath' is set to the
159 origin file when 'op' is either COPY or RENAME, None otherwise. If
159 origin file when 'op' is either COPY or RENAME, None otherwise. If
160 file mode is changed, 'mode' is a tuple (islink, isexec) where
160 file mode is changed, 'mode' is a tuple (islink, isexec) where
161 'islink' is True if the file is a symlink and 'isexec' is True if
161 'islink' is True if the file is a symlink and 'isexec' is True if
162 the file is executable. Otherwise, 'mode' is None.
162 the file is executable. Otherwise, 'mode' is None.
163 """
163 """
164 def __init__(self, path):
164 def __init__(self, path):
165 self.path = path
165 self.path = path
166 self.oldpath = None
166 self.oldpath = None
167 self.mode = None
167 self.mode = None
168 self.op = 'MODIFY'
168 self.op = 'MODIFY'
169 self.lineno = 0
169 self.lineno = 0
170 self.binary = False
170 self.binary = False
171
171
172 def setmode(self, mode):
172 def setmode(self, mode):
173 islink = mode & 020000
173 islink = mode & 020000
174 isexec = mode & 0100
174 isexec = mode & 0100
175 self.mode = (islink, isexec)
175 self.mode = (islink, isexec)
176
176
177 def readgitpatch(lr):
177 def readgitpatch(lr):
178 """extract git-style metadata about patches from <patchname>"""
178 """extract git-style metadata about patches from <patchname>"""
179
179
180 # Filter patch for git information
180 # Filter patch for git information
181 gp = None
181 gp = None
182 gitpatches = []
182 gitpatches = []
183 # Can have a git patch with only metadata, causing patch to complain
183 # Can have a git patch with only metadata, causing patch to complain
184 dopatch = 0
184 dopatch = 0
185
185
186 lineno = 0
186 lineno = 0
187 for line in lr:
187 for line in lr:
188 lineno += 1
188 lineno += 1
189 line = line.rstrip(' \r\n')
189 line = line.rstrip(' \r\n')
190 if line.startswith('diff --git'):
190 if line.startswith('diff --git'):
191 m = gitre.match(line)
191 m = gitre.match(line)
192 if m:
192 if m:
193 if gp:
193 if gp:
194 gitpatches.append(gp)
194 gitpatches.append(gp)
195 dst = m.group(2)
195 dst = m.group(2)
196 gp = patchmeta(dst)
196 gp = patchmeta(dst)
197 gp.lineno = lineno
197 gp.lineno = lineno
198 elif gp:
198 elif gp:
199 if line.startswith('--- '):
199 if line.startswith('--- '):
200 if gp.op in ('COPY', 'RENAME'):
200 if gp.op in ('COPY', 'RENAME'):
201 dopatch |= GP_FILTER
201 dopatch |= GP_FILTER
202 gitpatches.append(gp)
202 gitpatches.append(gp)
203 gp = None
203 gp = None
204 dopatch |= GP_PATCH
204 dopatch |= GP_PATCH
205 continue
205 continue
206 if line.startswith('rename from '):
206 if line.startswith('rename from '):
207 gp.op = 'RENAME'
207 gp.op = 'RENAME'
208 gp.oldpath = line[12:]
208 gp.oldpath = line[12:]
209 elif line.startswith('rename to '):
209 elif line.startswith('rename to '):
210 gp.path = line[10:]
210 gp.path = line[10:]
211 elif line.startswith('copy from '):
211 elif line.startswith('copy from '):
212 gp.op = 'COPY'
212 gp.op = 'COPY'
213 gp.oldpath = line[10:]
213 gp.oldpath = line[10:]
214 elif line.startswith('copy to '):
214 elif line.startswith('copy to '):
215 gp.path = line[8:]
215 gp.path = line[8:]
216 elif line.startswith('deleted file'):
216 elif line.startswith('deleted file'):
217 gp.op = 'DELETE'
217 gp.op = 'DELETE'
218 # is the deleted file a symlink?
218 # is the deleted file a symlink?
219 gp.setmode(int(line[-6:], 8))
219 gp.setmode(int(line[-6:], 8))
220 elif line.startswith('new file mode '):
220 elif line.startswith('new file mode '):
221 gp.op = 'ADD'
221 gp.op = 'ADD'
222 gp.setmode(int(line[-6:], 8))
222 gp.setmode(int(line[-6:], 8))
223 elif line.startswith('new mode '):
223 elif line.startswith('new mode '):
224 gp.setmode(int(line[-6:], 8))
224 gp.setmode(int(line[-6:], 8))
225 elif line.startswith('GIT binary patch'):
225 elif line.startswith('GIT binary patch'):
226 dopatch |= GP_BINARY
226 dopatch |= GP_BINARY
227 gp.binary = True
227 gp.binary = True
228 if gp:
228 if gp:
229 gitpatches.append(gp)
229 gitpatches.append(gp)
230
230
231 if not gitpatches:
231 if not gitpatches:
232 dopatch = GP_PATCH
232 dopatch = GP_PATCH
233
233
234 return (dopatch, gitpatches)
234 return (dopatch, gitpatches)
235
235
236 class linereader(object):
236 class linereader(object):
237 # simple class to allow pushing lines back into the input stream
237 # simple class to allow pushing lines back into the input stream
238 def __init__(self, fp, textmode=False):
238 def __init__(self, fp, textmode=False):
239 self.fp = fp
239 self.fp = fp
240 self.buf = []
240 self.buf = []
241 self.textmode = textmode
241 self.textmode = textmode
242
242
243 def push(self, line):
243 def push(self, line):
244 if line is not None:
244 if line is not None:
245 self.buf.append(line)
245 self.buf.append(line)
246
246
247 def readline(self):
247 def readline(self):
248 if self.buf:
248 if self.buf:
249 l = self.buf[0]
249 l = self.buf[0]
250 del self.buf[0]
250 del self.buf[0]
251 return l
251 return l
252 l = self.fp.readline()
252 l = self.fp.readline()
253 if self.textmode and l.endswith('\r\n'):
253 if self.textmode and l.endswith('\r\n'):
254 l = l[:-2] + '\n'
254 l = l[:-2] + '\n'
255 return l
255 return l
256
256
257 def __iter__(self):
257 def __iter__(self):
258 while 1:
258 while 1:
259 l = self.readline()
259 l = self.readline()
260 if not l:
260 if not l:
261 break
261 break
262 yield l
262 yield l
263
263
264 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
264 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
265 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
265 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
266 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
266 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
267
267
268 class patchfile(object):
268 class patchfile(object):
269 def __init__(self, ui, fname, opener, missing=False, eol=None):
269 def __init__(self, ui, fname, opener, missing=False, eol=None):
270 self.fname = fname
270 self.fname = fname
271 self.eol = eol
271 self.eol = eol
272 self.opener = opener
272 self.opener = opener
273 self.ui = ui
273 self.ui = ui
274 self.lines = []
274 self.lines = []
275 self.exists = False
275 self.exists = False
276 self.missing = missing
276 self.missing = missing
277 if not missing:
277 if not missing:
278 try:
278 try:
279 self.lines = self.readlines(fname)
279 self.lines = self.readlines(fname)
280 self.exists = True
280 self.exists = True
281 except IOError:
281 except IOError:
282 pass
282 pass
283 else:
283 else:
284 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
284 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
285
285
286 self.hash = {}
286 self.hash = {}
287 self.dirty = 0
287 self.dirty = 0
288 self.offset = 0
288 self.offset = 0
289 self.rej = []
289 self.rej = []
290 self.fileprinted = False
290 self.fileprinted = False
291 self.printfile(False)
291 self.printfile(False)
292 self.hunks = 0
292 self.hunks = 0
293
293
294 def readlines(self, fname):
294 def readlines(self, fname):
295 if os.path.islink(fname):
295 if os.path.islink(fname):
296 return [os.readlink(fname)]
296 return [os.readlink(fname)]
297 fp = self.opener(fname, 'r')
297 fp = self.opener(fname, 'r')
298 try:
298 try:
299 return list(linereader(fp, self.eol is not None))
299 return list(linereader(fp, self.eol is not None))
300 finally:
300 finally:
301 fp.close()
301 fp.close()
302
302
303 def writelines(self, fname, lines):
303 def writelines(self, fname, lines):
304 # Ensure supplied data ends in fname, being a regular file or
304 # Ensure supplied data ends in fname, being a regular file or
305 # a symlink. updatedir() will -too magically- take care of
305 # a symlink. updatedir() will -too magically- take care of
306 # setting it to the proper type afterwards.
306 # setting it to the proper type afterwards.
307 islink = os.path.islink(fname)
307 islink = os.path.islink(fname)
308 if islink:
308 if islink:
309 fp = cStringIO.StringIO()
309 fp = cStringIO.StringIO()
310 else:
310 else:
311 fp = self.opener(fname, 'w')
311 fp = self.opener(fname, 'w')
312 try:
312 try:
313 if self.eol and self.eol != '\n':
313 if self.eol and self.eol != '\n':
314 for l in lines:
314 for l in lines:
315 if l and l[-1] == '\n':
315 if l and l[-1] == '\n':
316 l = l[:-1] + self.eol
316 l = l[:-1] + self.eol
317 fp.write(l)
317 fp.write(l)
318 else:
318 else:
319 fp.writelines(lines)
319 fp.writelines(lines)
320 if islink:
320 if islink:
321 self.opener.symlink(fp.getvalue(), fname)
321 self.opener.symlink(fp.getvalue(), fname)
322 finally:
322 finally:
323 fp.close()
323 fp.close()
324
324
325 def unlink(self, fname):
325 def unlink(self, fname):
326 os.unlink(fname)
326 os.unlink(fname)
327
327
328 def printfile(self, warn):
328 def printfile(self, warn):
329 if self.fileprinted:
329 if self.fileprinted:
330 return
330 return
331 if warn or self.ui.verbose:
331 if warn or self.ui.verbose:
332 self.fileprinted = True
332 self.fileprinted = True
333 s = _("patching file %s\n") % self.fname
333 s = _("patching file %s\n") % self.fname
334 if warn:
334 if warn:
335 self.ui.warn(s)
335 self.ui.warn(s)
336 else:
336 else:
337 self.ui.note(s)
337 self.ui.note(s)
338
338
339
339
340 def findlines(self, l, linenum):
340 def findlines(self, l, linenum):
341 # looks through the hash and finds candidate lines. The
341 # looks through the hash and finds candidate lines. The
342 # result is a list of line numbers sorted based on distance
342 # result is a list of line numbers sorted based on distance
343 # from linenum
343 # from linenum
344
344
345 cand = self.hash.get(l, [])
345 cand = self.hash.get(l, [])
346 if len(cand) > 1:
346 if len(cand) > 1:
347 # resort our list of potentials forward then back.
347 # resort our list of potentials forward then back.
348 cand.sort(key=lambda x: abs(x - linenum))
348 cand.sort(key=lambda x: abs(x - linenum))
349 return cand
349 return cand
350
350
351 def hashlines(self):
351 def hashlines(self):
352 self.hash = {}
352 self.hash = {}
353 for x, s in enumerate(self.lines):
353 for x, s in enumerate(self.lines):
354 self.hash.setdefault(s, []).append(x)
354 self.hash.setdefault(s, []).append(x)
355
355
356 def write_rej(self):
356 def write_rej(self):
357 # our rejects are a little different from patch(1). This always
357 # our rejects are a little different from patch(1). This always
358 # creates rejects in the same form as the original patch. A file
358 # creates rejects in the same form as the original patch. A file
359 # header is inserted so that you can run the reject through patch again
359 # header is inserted so that you can run the reject through patch again
360 # without having to type the filename.
360 # without having to type the filename.
361
361
362 if not self.rej:
362 if not self.rej:
363 return
363 return
364
364
365 fname = self.fname + ".rej"
365 fname = self.fname + ".rej"
366 self.ui.warn(
366 self.ui.warn(
367 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
367 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
368 (len(self.rej), self.hunks, fname))
368 (len(self.rej), self.hunks, fname))
369
369
370 def rejlines():
370 def rejlines():
371 base = os.path.basename(self.fname)
371 base = os.path.basename(self.fname)
372 yield "--- %s\n+++ %s\n" % (base, base)
372 yield "--- %s\n+++ %s\n" % (base, base)
373 for x in self.rej:
373 for x in self.rej:
374 for l in x.hunk:
374 for l in x.hunk:
375 yield l
375 yield l
376 if l[-1] != '\n':
376 if l[-1] != '\n':
377 yield "\n\ No newline at end of file\n"
377 yield "\n\ No newline at end of file\n"
378
378
379 self.writelines(fname, rejlines())
379 self.writelines(fname, rejlines())
380
380
381 def write(self, dest=None):
381 def write(self, dest=None):
382 if not self.dirty:
382 if not self.dirty:
383 return
383 return
384 if not dest:
384 if not dest:
385 dest = self.fname
385 dest = self.fname
386 self.writelines(dest, self.lines)
386 self.writelines(dest, self.lines)
387
387
388 def close(self):
388 def close(self):
389 self.write()
389 self.write()
390 self.write_rej()
390 self.write_rej()
391
391
392 def apply(self, h):
392 def apply(self, h):
393 if not h.complete():
393 if not h.complete():
394 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
394 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
395 (h.number, h.desc, len(h.a), h.lena, len(h.b),
395 (h.number, h.desc, len(h.a), h.lena, len(h.b),
396 h.lenb))
396 h.lenb))
397
397
398 self.hunks += 1
398 self.hunks += 1
399
399
400 if self.missing:
400 if self.missing:
401 self.rej.append(h)
401 self.rej.append(h)
402 return -1
402 return -1
403
403
404 if self.exists and h.createfile():
404 if self.exists and h.createfile():
405 self.ui.warn(_("file %s already exists\n") % self.fname)
405 self.ui.warn(_("file %s already exists\n") % self.fname)
406 self.rej.append(h)
406 self.rej.append(h)
407 return -1
407 return -1
408
408
409 if isinstance(h, binhunk):
409 if isinstance(h, binhunk):
410 if h.rmfile():
410 if h.rmfile():
411 self.unlink(self.fname)
411 self.unlink(self.fname)
412 else:
412 else:
413 self.lines[:] = h.new()
413 self.lines[:] = h.new()
414 self.offset += len(h.new())
414 self.offset += len(h.new())
415 self.dirty = 1
415 self.dirty = 1
416 return 0
416 return 0
417
417
418 # fast case first, no offsets, no fuzz
418 # fast case first, no offsets, no fuzz
419 old = h.old()
419 old = h.old()
420 # patch starts counting at 1 unless we are adding the file
420 # patch starts counting at 1 unless we are adding the file
421 if h.starta == 0:
421 if h.starta == 0:
422 start = 0
422 start = 0
423 else:
423 else:
424 start = h.starta + self.offset - 1
424 start = h.starta + self.offset - 1
425 orig_start = start
425 orig_start = start
426 if diffhelpers.testhunk(old, self.lines, start) == 0:
426 if diffhelpers.testhunk(old, self.lines, start) == 0:
427 if h.rmfile():
427 if h.rmfile():
428 self.unlink(self.fname)
428 self.unlink(self.fname)
429 else:
429 else:
430 self.lines[start : start + h.lena] = h.new()
430 self.lines[start : start + h.lena] = h.new()
431 self.offset += h.lenb - h.lena
431 self.offset += h.lenb - h.lena
432 self.dirty = 1
432 self.dirty = 1
433 return 0
433 return 0
434
434
435 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
435 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
436 self.hashlines()
436 self.hashlines()
437 if h.hunk[-1][0] != ' ':
437 if h.hunk[-1][0] != ' ':
438 # if the hunk tried to put something at the bottom of the file
438 # if the hunk tried to put something at the bottom of the file
439 # override the start line and use eof here
439 # override the start line and use eof here
440 search_start = len(self.lines)
440 search_start = len(self.lines)
441 else:
441 else:
442 search_start = orig_start
442 search_start = orig_start
443
443
444 for fuzzlen in xrange(3):
444 for fuzzlen in xrange(3):
445 for toponly in [ True, False ]:
445 for toponly in [ True, False ]:
446 old = h.old(fuzzlen, toponly)
446 old = h.old(fuzzlen, toponly)
447
447
448 cand = self.findlines(old[0][1:], search_start)
448 cand = self.findlines(old[0][1:], search_start)
449 for l in cand:
449 for l in cand:
450 if diffhelpers.testhunk(old, self.lines, l) == 0:
450 if diffhelpers.testhunk(old, self.lines, l) == 0:
451 newlines = h.new(fuzzlen, toponly)
451 newlines = h.new(fuzzlen, toponly)
452 self.lines[l : l + len(old)] = newlines
452 self.lines[l : l + len(old)] = newlines
453 self.offset += len(newlines) - len(old)
453 self.offset += len(newlines) - len(old)
454 self.dirty = 1
454 self.dirty = 1
455 if fuzzlen:
455 if fuzzlen:
456 fuzzstr = "with fuzz %d " % fuzzlen
456 fuzzstr = "with fuzz %d " % fuzzlen
457 f = self.ui.warn
457 f = self.ui.warn
458 self.printfile(True)
458 self.printfile(True)
459 else:
459 else:
460 fuzzstr = ""
460 fuzzstr = ""
461 f = self.ui.note
461 f = self.ui.note
462 offset = l - orig_start - fuzzlen
462 offset = l - orig_start - fuzzlen
463 if offset == 1:
463 if offset == 1:
464 msg = _("Hunk #%d succeeded at %d %s"
464 msg = _("Hunk #%d succeeded at %d %s"
465 "(offset %d line).\n")
465 "(offset %d line).\n")
466 else:
466 else:
467 msg = _("Hunk #%d succeeded at %d %s"
467 msg = _("Hunk #%d succeeded at %d %s"
468 "(offset %d lines).\n")
468 "(offset %d lines).\n")
469 f(msg % (h.number, l+1, fuzzstr, offset))
469 f(msg % (h.number, l+1, fuzzstr, offset))
470 return fuzzlen
470 return fuzzlen
471 self.printfile(True)
471 self.printfile(True)
472 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
472 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
473 self.rej.append(h)
473 self.rej.append(h)
474 return -1
474 return -1
475
475
476 class hunk(object):
476 class hunk(object):
477 def __init__(self, desc, num, lr, context, create=False, remove=False):
477 def __init__(self, desc, num, lr, context, create=False, remove=False):
478 self.number = num
478 self.number = num
479 self.desc = desc
479 self.desc = desc
480 self.hunk = [ desc ]
480 self.hunk = [ desc ]
481 self.a = []
481 self.a = []
482 self.b = []
482 self.b = []
483 self.starta = self.lena = None
483 self.starta = self.lena = None
484 self.startb = self.lenb = None
484 self.startb = self.lenb = None
485 if context:
485 if context:
486 self.read_context_hunk(lr)
486 self.read_context_hunk(lr)
487 else:
487 else:
488 self.read_unified_hunk(lr)
488 self.read_unified_hunk(lr)
489 self.create = create
489 self.create = create
490 self.remove = remove and not create
490 self.remove = remove and not create
491
491
492 def read_unified_hunk(self, lr):
492 def read_unified_hunk(self, lr):
493 m = unidesc.match(self.desc)
493 m = unidesc.match(self.desc)
494 if not m:
494 if not m:
495 raise PatchError(_("bad hunk #%d") % self.number)
495 raise PatchError(_("bad hunk #%d") % self.number)
496 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
496 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
497 if self.lena is None:
497 if self.lena is None:
498 self.lena = 1
498 self.lena = 1
499 else:
499 else:
500 self.lena = int(self.lena)
500 self.lena = int(self.lena)
501 if self.lenb is None:
501 if self.lenb is None:
502 self.lenb = 1
502 self.lenb = 1
503 else:
503 else:
504 self.lenb = int(self.lenb)
504 self.lenb = int(self.lenb)
505 self.starta = int(self.starta)
505 self.starta = int(self.starta)
506 self.startb = int(self.startb)
506 self.startb = int(self.startb)
507 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
507 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
508 # if we hit eof before finishing out the hunk, the last line will
508 # if we hit eof before finishing out the hunk, the last line will
509 # be zero length. Lets try to fix it up.
509 # be zero length. Lets try to fix it up.
510 while len(self.hunk[-1]) == 0:
510 while len(self.hunk[-1]) == 0:
511 del self.hunk[-1]
511 del self.hunk[-1]
512 del self.a[-1]
512 del self.a[-1]
513 del self.b[-1]
513 del self.b[-1]
514 self.lena -= 1
514 self.lena -= 1
515 self.lenb -= 1
515 self.lenb -= 1
516
516
517 def read_context_hunk(self, lr):
517 def read_context_hunk(self, lr):
518 self.desc = lr.readline()
518 self.desc = lr.readline()
519 m = contextdesc.match(self.desc)
519 m = contextdesc.match(self.desc)
520 if not m:
520 if not m:
521 raise PatchError(_("bad hunk #%d") % self.number)
521 raise PatchError(_("bad hunk #%d") % self.number)
522 foo, self.starta, foo2, aend, foo3 = m.groups()
522 foo, self.starta, foo2, aend, foo3 = m.groups()
523 self.starta = int(self.starta)
523 self.starta = int(self.starta)
524 if aend is None:
524 if aend is None:
525 aend = self.starta
525 aend = self.starta
526 self.lena = int(aend) - self.starta
526 self.lena = int(aend) - self.starta
527 if self.starta:
527 if self.starta:
528 self.lena += 1
528 self.lena += 1
529 for x in xrange(self.lena):
529 for x in xrange(self.lena):
530 l = lr.readline()
530 l = lr.readline()
531 if l.startswith('---'):
531 if l.startswith('---'):
532 lr.push(l)
532 lr.push(l)
533 break
533 break
534 s = l[2:]
534 s = l[2:]
535 if l.startswith('- ') or l.startswith('! '):
535 if l.startswith('- ') or l.startswith('! '):
536 u = '-' + s
536 u = '-' + s
537 elif l.startswith(' '):
537 elif l.startswith(' '):
538 u = ' ' + s
538 u = ' ' + s
539 else:
539 else:
540 raise PatchError(_("bad hunk #%d old text line %d") %
540 raise PatchError(_("bad hunk #%d old text line %d") %
541 (self.number, x))
541 (self.number, x))
542 self.a.append(u)
542 self.a.append(u)
543 self.hunk.append(u)
543 self.hunk.append(u)
544
544
545 l = lr.readline()
545 l = lr.readline()
546 if l.startswith('\ '):
546 if l.startswith('\ '):
547 s = self.a[-1][:-1]
547 s = self.a[-1][:-1]
548 self.a[-1] = s
548 self.a[-1] = s
549 self.hunk[-1] = s
549 self.hunk[-1] = s
550 l = lr.readline()
550 l = lr.readline()
551 m = contextdesc.match(l)
551 m = contextdesc.match(l)
552 if not m:
552 if not m:
553 raise PatchError(_("bad hunk #%d") % self.number)
553 raise PatchError(_("bad hunk #%d") % self.number)
554 foo, self.startb, foo2, bend, foo3 = m.groups()
554 foo, self.startb, foo2, bend, foo3 = m.groups()
555 self.startb = int(self.startb)
555 self.startb = int(self.startb)
556 if bend is None:
556 if bend is None:
557 bend = self.startb
557 bend = self.startb
558 self.lenb = int(bend) - self.startb
558 self.lenb = int(bend) - self.startb
559 if self.startb:
559 if self.startb:
560 self.lenb += 1
560 self.lenb += 1
561 hunki = 1
561 hunki = 1
562 for x in xrange(self.lenb):
562 for x in xrange(self.lenb):
563 l = lr.readline()
563 l = lr.readline()
564 if l.startswith('\ '):
564 if l.startswith('\ '):
565 s = self.b[-1][:-1]
565 s = self.b[-1][:-1]
566 self.b[-1] = s
566 self.b[-1] = s
567 self.hunk[hunki-1] = s
567 self.hunk[hunki-1] = s
568 continue
568 continue
569 if not l:
569 if not l:
570 lr.push(l)
570 lr.push(l)
571 break
571 break
572 s = l[2:]
572 s = l[2:]
573 if l.startswith('+ ') or l.startswith('! '):
573 if l.startswith('+ ') or l.startswith('! '):
574 u = '+' + s
574 u = '+' + s
575 elif l.startswith(' '):
575 elif l.startswith(' '):
576 u = ' ' + s
576 u = ' ' + s
577 elif len(self.b) == 0:
577 elif len(self.b) == 0:
578 # this can happen when the hunk does not add any lines
578 # this can happen when the hunk does not add any lines
579 lr.push(l)
579 lr.push(l)
580 break
580 break
581 else:
581 else:
582 raise PatchError(_("bad hunk #%d old text line %d") %
582 raise PatchError(_("bad hunk #%d old text line %d") %
583 (self.number, x))
583 (self.number, x))
584 self.b.append(s)
584 self.b.append(s)
585 while True:
585 while True:
586 if hunki >= len(self.hunk):
586 if hunki >= len(self.hunk):
587 h = ""
587 h = ""
588 else:
588 else:
589 h = self.hunk[hunki]
589 h = self.hunk[hunki]
590 hunki += 1
590 hunki += 1
591 if h == u:
591 if h == u:
592 break
592 break
593 elif h.startswith('-'):
593 elif h.startswith('-'):
594 continue
594 continue
595 else:
595 else:
596 self.hunk.insert(hunki-1, u)
596 self.hunk.insert(hunki-1, u)
597 break
597 break
598
598
599 if not self.a:
599 if not self.a:
600 # this happens when lines were only added to the hunk
600 # this happens when lines were only added to the hunk
601 for x in self.hunk:
601 for x in self.hunk:
602 if x.startswith('-') or x.startswith(' '):
602 if x.startswith('-') or x.startswith(' '):
603 self.a.append(x)
603 self.a.append(x)
604 if not self.b:
604 if not self.b:
605 # this happens when lines were only deleted from the hunk
605 # this happens when lines were only deleted from the hunk
606 for x in self.hunk:
606 for x in self.hunk:
607 if x.startswith('+') or x.startswith(' '):
607 if x.startswith('+') or x.startswith(' '):
608 self.b.append(x[1:])
608 self.b.append(x[1:])
609 # @@ -start,len +start,len @@
609 # @@ -start,len +start,len @@
610 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
610 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
611 self.startb, self.lenb)
611 self.startb, self.lenb)
612 self.hunk[0] = self.desc
612 self.hunk[0] = self.desc
613
613
614 def fix_newline(self):
614 def fix_newline(self):
615 diffhelpers.fix_newline(self.hunk, self.a, self.b)
615 diffhelpers.fix_newline(self.hunk, self.a, self.b)
616
616
617 def complete(self):
617 def complete(self):
618 return len(self.a) == self.lena and len(self.b) == self.lenb
618 return len(self.a) == self.lena and len(self.b) == self.lenb
619
619
620 def createfile(self):
620 def createfile(self):
621 return self.starta == 0 and self.lena == 0 and self.create
621 return self.starta == 0 and self.lena == 0 and self.create
622
622
623 def rmfile(self):
623 def rmfile(self):
624 return self.startb == 0 and self.lenb == 0 and self.remove
624 return self.startb == 0 and self.lenb == 0 and self.remove
625
625
626 def fuzzit(self, l, fuzz, toponly):
626 def fuzzit(self, l, fuzz, toponly):
627 # this removes context lines from the top and bottom of list 'l'. It
627 # this removes context lines from the top and bottom of list 'l'. It
628 # checks the hunk to make sure only context lines are removed, and then
628 # checks the hunk to make sure only context lines are removed, and then
629 # returns a new shortened list of lines.
629 # returns a new shortened list of lines.
630 fuzz = min(fuzz, len(l)-1)
630 fuzz = min(fuzz, len(l)-1)
631 if fuzz:
631 if fuzz:
632 top = 0
632 top = 0
633 bot = 0
633 bot = 0
634 hlen = len(self.hunk)
634 hlen = len(self.hunk)
635 for x in xrange(hlen-1):
635 for x in xrange(hlen-1):
636 # the hunk starts with the @@ line, so use x+1
636 # the hunk starts with the @@ line, so use x+1
637 if self.hunk[x+1][0] == ' ':
637 if self.hunk[x+1][0] == ' ':
638 top += 1
638 top += 1
639 else:
639 else:
640 break
640 break
641 if not toponly:
641 if not toponly:
642 for x in xrange(hlen-1):
642 for x in xrange(hlen-1):
643 if self.hunk[hlen-bot-1][0] == ' ':
643 if self.hunk[hlen-bot-1][0] == ' ':
644 bot += 1
644 bot += 1
645 else:
645 else:
646 break
646 break
647
647
648 # top and bot now count context in the hunk
648 # top and bot now count context in the hunk
649 # adjust them if either one is short
649 # adjust them if either one is short
650 context = max(top, bot, 3)
650 context = max(top, bot, 3)
651 if bot < context:
651 if bot < context:
652 bot = max(0, fuzz - (context - bot))
652 bot = max(0, fuzz - (context - bot))
653 else:
653 else:
654 bot = min(fuzz, bot)
654 bot = min(fuzz, bot)
655 if top < context:
655 if top < context:
656 top = max(0, fuzz - (context - top))
656 top = max(0, fuzz - (context - top))
657 else:
657 else:
658 top = min(fuzz, top)
658 top = min(fuzz, top)
659
659
660 return l[top:len(l)-bot]
660 return l[top:len(l)-bot]
661 return l
661 return l
662
662
663 def old(self, fuzz=0, toponly=False):
663 def old(self, fuzz=0, toponly=False):
664 return self.fuzzit(self.a, fuzz, toponly)
664 return self.fuzzit(self.a, fuzz, toponly)
665
665
666 def newctrl(self):
666 def newctrl(self):
667 res = []
667 res = []
668 for x in self.hunk:
668 for x in self.hunk:
669 c = x[0]
669 c = x[0]
670 if c == ' ' or c == '+':
670 if c == ' ' or c == '+':
671 res.append(x)
671 res.append(x)
672 return res
672 return res
673
673
674 def new(self, fuzz=0, toponly=False):
674 def new(self, fuzz=0, toponly=False):
675 return self.fuzzit(self.b, fuzz, toponly)
675 return self.fuzzit(self.b, fuzz, toponly)
676
676
677 class binhunk:
677 class binhunk:
678 'A binary patch file. Only understands literals so far.'
678 'A binary patch file. Only understands literals so far.'
679 def __init__(self, gitpatch):
679 def __init__(self, gitpatch):
680 self.gitpatch = gitpatch
680 self.gitpatch = gitpatch
681 self.text = None
681 self.text = None
682 self.hunk = ['GIT binary patch\n']
682 self.hunk = ['GIT binary patch\n']
683
683
684 def createfile(self):
684 def createfile(self):
685 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
685 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
686
686
687 def rmfile(self):
687 def rmfile(self):
688 return self.gitpatch.op == 'DELETE'
688 return self.gitpatch.op == 'DELETE'
689
689
690 def complete(self):
690 def complete(self):
691 return self.text is not None
691 return self.text is not None
692
692
693 def new(self):
693 def new(self):
694 return [self.text]
694 return [self.text]
695
695
696 def extract(self, lr):
696 def extract(self, lr):
697 line = lr.readline()
697 line = lr.readline()
698 self.hunk.append(line)
698 self.hunk.append(line)
699 while line and not line.startswith('literal '):
699 while line and not line.startswith('literal '):
700 line = lr.readline()
700 line = lr.readline()
701 self.hunk.append(line)
701 self.hunk.append(line)
702 if not line:
702 if not line:
703 raise PatchError(_('could not extract binary patch'))
703 raise PatchError(_('could not extract binary patch'))
704 size = int(line[8:].rstrip())
704 size = int(line[8:].rstrip())
705 dec = []
705 dec = []
706 line = lr.readline()
706 line = lr.readline()
707 self.hunk.append(line)
707 self.hunk.append(line)
708 while len(line) > 1:
708 while len(line) > 1:
709 l = line[0]
709 l = line[0]
710 if l <= 'Z' and l >= 'A':
710 if l <= 'Z' and l >= 'A':
711 l = ord(l) - ord('A') + 1
711 l = ord(l) - ord('A') + 1
712 else:
712 else:
713 l = ord(l) - ord('a') + 27
713 l = ord(l) - ord('a') + 27
714 dec.append(base85.b85decode(line[1:-1])[:l])
714 dec.append(base85.b85decode(line[1:-1])[:l])
715 line = lr.readline()
715 line = lr.readline()
716 self.hunk.append(line)
716 self.hunk.append(line)
717 text = zlib.decompress(''.join(dec))
717 text = zlib.decompress(''.join(dec))
718 if len(text) != size:
718 if len(text) != size:
719 raise PatchError(_('binary patch is %d bytes, not %d') %
719 raise PatchError(_('binary patch is %d bytes, not %d') %
720 len(text), size)
720 len(text), size)
721 self.text = text
721 self.text = text
722
722
723 def parsefilename(str):
723 def parsefilename(str):
724 # --- filename \t|space stuff
724 # --- filename \t|space stuff
725 s = str[4:].rstrip('\r\n')
725 s = str[4:].rstrip('\r\n')
726 i = s.find('\t')
726 i = s.find('\t')
727 if i < 0:
727 if i < 0:
728 i = s.find(' ')
728 i = s.find(' ')
729 if i < 0:
729 if i < 0:
730 return s
730 return s
731 return s[:i]
731 return s[:i]
732
732
733 def selectfile(afile_orig, bfile_orig, hunk, strip):
733 def selectfile(afile_orig, bfile_orig, hunk, strip):
734 def pathstrip(path, count=1):
734 def pathstrip(path, count=1):
735 pathlen = len(path)
735 pathlen = len(path)
736 i = 0
736 i = 0
737 if count == 0:
737 if count == 0:
738 return '', path.rstrip()
738 return '', path.rstrip()
739 while count > 0:
739 while count > 0:
740 i = path.find('/', i)
740 i = path.find('/', i)
741 if i == -1:
741 if i == -1:
742 raise PatchError(_("unable to strip away %d dirs from %s") %
742 raise PatchError(_("unable to strip away %d dirs from %s") %
743 (count, path))
743 (count, path))
744 i += 1
744 i += 1
745 # consume '//' in the path
745 # consume '//' in the path
746 while i < pathlen - 1 and path[i] == '/':
746 while i < pathlen - 1 and path[i] == '/':
747 i += 1
747 i += 1
748 count -= 1
748 count -= 1
749 return path[:i].lstrip(), path[i:].rstrip()
749 return path[:i].lstrip(), path[i:].rstrip()
750
750
751 nulla = afile_orig == "/dev/null"
751 nulla = afile_orig == "/dev/null"
752 nullb = bfile_orig == "/dev/null"
752 nullb = bfile_orig == "/dev/null"
753 abase, afile = pathstrip(afile_orig, strip)
753 abase, afile = pathstrip(afile_orig, strip)
754 gooda = not nulla and util.lexists(afile)
754 gooda = not nulla and util.lexists(afile)
755 bbase, bfile = pathstrip(bfile_orig, strip)
755 bbase, bfile = pathstrip(bfile_orig, strip)
756 if afile == bfile:
756 if afile == bfile:
757 goodb = gooda
757 goodb = gooda
758 else:
758 else:
759 goodb = not nullb and os.path.exists(bfile)
759 goodb = not nullb and os.path.exists(bfile)
760 createfunc = hunk.createfile
760 createfunc = hunk.createfile
761 missing = not goodb and not gooda and not createfunc()
761 missing = not goodb and not gooda and not createfunc()
762
762
763 # some diff programs apparently produce create patches where the
763 # some diff programs apparently produce create patches where the
764 # afile is not /dev/null, but rather the same name as the bfile
764 # afile is not /dev/null, but rather the same name as the bfile
765 if missing and afile == bfile:
765 if missing and afile == bfile:
766 # this isn't very pretty
766 # this isn't very pretty
767 hunk.create = True
767 hunk.create = True
768 if createfunc():
768 if createfunc():
769 missing = False
769 missing = False
770 else:
770 else:
771 hunk.create = False
771 hunk.create = False
772
772
773 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
773 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
774 # diff is between a file and its backup. In this case, the original
774 # diff is between a file and its backup. In this case, the original
775 # file should be patched (see original mpatch code).
775 # file should be patched (see original mpatch code).
776 isbackup = (abase == bbase and bfile.startswith(afile))
776 isbackup = (abase == bbase and bfile.startswith(afile))
777 fname = None
777 fname = None
778 if not missing:
778 if not missing:
779 if gooda and goodb:
779 if gooda and goodb:
780 fname = isbackup and afile or bfile
780 fname = isbackup and afile or bfile
781 elif gooda:
781 elif gooda:
782 fname = afile
782 fname = afile
783
783
784 if not fname:
784 if not fname:
785 if not nullb:
785 if not nullb:
786 fname = isbackup and afile or bfile
786 fname = isbackup and afile or bfile
787 elif not nulla:
787 elif not nulla:
788 fname = afile
788 fname = afile
789 else:
789 else:
790 raise PatchError(_("undefined source and destination files"))
790 raise PatchError(_("undefined source and destination files"))
791
791
792 return fname, missing
792 return fname, missing
793
793
794 def scangitpatch(lr, firstline):
794 def scangitpatch(lr, firstline):
795 """
795 """
796 Git patches can emit:
796 Git patches can emit:
797 - rename a to b
797 - rename a to b
798 - change b
798 - change b
799 - copy a to c
799 - copy a to c
800 - change c
800 - change c
801
801
802 We cannot apply this sequence as-is, the renamed 'a' could not be
802 We cannot apply this sequence as-is, the renamed 'a' could not be
803 found for it would have been renamed already. And we cannot copy
803 found for it would have been renamed already. And we cannot copy
804 from 'b' instead because 'b' would have been changed already. So
804 from 'b' instead because 'b' would have been changed already. So
805 we scan the git patch for copy and rename commands so we can
805 we scan the git patch for copy and rename commands so we can
806 perform the copies ahead of time.
806 perform the copies ahead of time.
807 """
807 """
808 pos = 0
808 pos = 0
809 try:
809 try:
810 pos = lr.fp.tell()
810 pos = lr.fp.tell()
811 fp = lr.fp
811 fp = lr.fp
812 except IOError:
812 except IOError:
813 fp = cStringIO.StringIO(lr.fp.read())
813 fp = cStringIO.StringIO(lr.fp.read())
814 gitlr = linereader(fp, lr.textmode)
814 gitlr = linereader(fp, lr.textmode)
815 gitlr.push(firstline)
815 gitlr.push(firstline)
816 (dopatch, gitpatches) = readgitpatch(gitlr)
816 (dopatch, gitpatches) = readgitpatch(gitlr)
817 fp.seek(pos)
817 fp.seek(pos)
818 return dopatch, gitpatches
818 return dopatch, gitpatches
819
819
820 def iterhunks(ui, fp, sourcefile=None, textmode=False):
820 def iterhunks(ui, fp, sourcefile=None, textmode=False):
821 """Read a patch and yield the following events:
821 """Read a patch and yield the following events:
822 - ("file", afile, bfile, firsthunk): select a new target file.
822 - ("file", afile, bfile, firsthunk): select a new target file.
823 - ("hunk", hunk): a new hunk is ready to be applied, follows a
823 - ("hunk", hunk): a new hunk is ready to be applied, follows a
824 "file" event.
824 "file" event.
825 - ("git", gitchanges): current diff is in git format, gitchanges
825 - ("git", gitchanges): current diff is in git format, gitchanges
826 maps filenames to gitpatch records. Unique event.
826 maps filenames to gitpatch records. Unique event.
827
827
828 If textmode is True, input line-endings are normalized to LF.
828 If textmode is True, input line-endings are normalized to LF.
829 """
829 """
830 changed = {}
830 changed = {}
831 current_hunk = None
831 current_hunk = None
832 afile = ""
832 afile = ""
833 bfile = ""
833 bfile = ""
834 state = None
834 state = None
835 hunknum = 0
835 hunknum = 0
836 emitfile = False
836 emitfile = False
837 git = False
837 git = False
838
838
839 # our states
839 # our states
840 BFILE = 1
840 BFILE = 1
841 context = None
841 context = None
842 lr = linereader(fp, textmode)
842 lr = linereader(fp, textmode)
843 dopatch = True
843 dopatch = True
844 # gitworkdone is True if a git operation (copy, rename, ...) was
844 # gitworkdone is True if a git operation (copy, rename, ...) was
845 # performed already for the current file. Useful when the file
845 # performed already for the current file. Useful when the file
846 # section may have no hunk.
846 # section may have no hunk.
847 gitworkdone = False
847 gitworkdone = False
848
848
849 while True:
849 while True:
850 newfile = False
850 newfile = False
851 x = lr.readline()
851 x = lr.readline()
852 if not x:
852 if not x:
853 break
853 break
854 if current_hunk:
854 if current_hunk:
855 if x.startswith('\ '):
855 if x.startswith('\ '):
856 current_hunk.fix_newline()
856 current_hunk.fix_newline()
857 yield 'hunk', current_hunk
857 yield 'hunk', current_hunk
858 current_hunk = None
858 current_hunk = None
859 gitworkdone = False
859 gitworkdone = False
860 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
860 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
861 ((context is not False) and x.startswith('***************')))):
861 ((context is not False) and x.startswith('***************')))):
862 try:
862 try:
863 if context is None and x.startswith('***************'):
863 if context is None and x.startswith('***************'):
864 context = True
864 context = True
865 gpatch = changed.get(bfile)
865 gpatch = changed.get(bfile)
866 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
866 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
867 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
867 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
868 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
868 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
869 except PatchError, err:
869 except PatchError, err:
870 ui.debug(err)
870 ui.debug(err)
871 current_hunk = None
871 current_hunk = None
872 continue
872 continue
873 hunknum += 1
873 hunknum += 1
874 if emitfile:
874 if emitfile:
875 emitfile = False
875 emitfile = False
876 yield 'file', (afile, bfile, current_hunk)
876 yield 'file', (afile, bfile, current_hunk)
877 elif state == BFILE and x.startswith('GIT binary patch'):
877 elif state == BFILE and x.startswith('GIT binary patch'):
878 current_hunk = binhunk(changed[bfile])
878 current_hunk = binhunk(changed[bfile])
879 hunknum += 1
879 hunknum += 1
880 if emitfile:
880 if emitfile:
881 emitfile = False
881 emitfile = False
882 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
882 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
883 current_hunk.extract(lr)
883 current_hunk.extract(lr)
884 elif x.startswith('diff --git'):
884 elif x.startswith('diff --git'):
885 # check for git diff, scanning the whole patch file if needed
885 # check for git diff, scanning the whole patch file if needed
886 m = gitre.match(x)
886 m = gitre.match(x)
887 if m:
887 if m:
888 afile, bfile = m.group(1, 2)
888 afile, bfile = m.group(1, 2)
889 if not git:
889 if not git:
890 git = True
890 git = True
891 dopatch, gitpatches = scangitpatch(lr, x)
891 dopatch, gitpatches = scangitpatch(lr, x)
892 yield 'git', gitpatches
892 yield 'git', gitpatches
893 for gp in gitpatches:
893 for gp in gitpatches:
894 changed[gp.path] = gp
894 changed[gp.path] = gp
895 # else error?
895 # else error?
896 # copy/rename + modify should modify target, not source
896 # copy/rename + modify should modify target, not source
897 gp = changed.get(bfile)
897 gp = changed.get(bfile)
898 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
898 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
899 afile = bfile
899 afile = bfile
900 gitworkdone = True
900 gitworkdone = True
901 newfile = True
901 newfile = True
902 elif x.startswith('---'):
902 elif x.startswith('---'):
903 # check for a unified diff
903 # check for a unified diff
904 l2 = lr.readline()
904 l2 = lr.readline()
905 if not l2.startswith('+++'):
905 if not l2.startswith('+++'):
906 lr.push(l2)
906 lr.push(l2)
907 continue
907 continue
908 newfile = True
908 newfile = True
909 context = False
909 context = False
910 afile = parsefilename(x)
910 afile = parsefilename(x)
911 bfile = parsefilename(l2)
911 bfile = parsefilename(l2)
912 elif x.startswith('***'):
912 elif x.startswith('***'):
913 # check for a context diff
913 # check for a context diff
914 l2 = lr.readline()
914 l2 = lr.readline()
915 if not l2.startswith('---'):
915 if not l2.startswith('---'):
916 lr.push(l2)
916 lr.push(l2)
917 continue
917 continue
918 l3 = lr.readline()
918 l3 = lr.readline()
919 lr.push(l3)
919 lr.push(l3)
920 if not l3.startswith("***************"):
920 if not l3.startswith("***************"):
921 lr.push(l2)
921 lr.push(l2)
922 continue
922 continue
923 newfile = True
923 newfile = True
924 context = True
924 context = True
925 afile = parsefilename(x)
925 afile = parsefilename(x)
926 bfile = parsefilename(l2)
926 bfile = parsefilename(l2)
927
927
928 if newfile:
928 if newfile:
929 emitfile = True
929 emitfile = True
930 state = BFILE
930 state = BFILE
931 hunknum = 0
931 hunknum = 0
932 if current_hunk:
932 if current_hunk:
933 if current_hunk.complete():
933 if current_hunk.complete():
934 yield 'hunk', current_hunk
934 yield 'hunk', current_hunk
935 else:
935 else:
936 raise PatchError(_("malformed patch %s %s") % (afile,
936 raise PatchError(_("malformed patch %s %s") % (afile,
937 current_hunk.desc))
937 current_hunk.desc))
938
938
939 if hunknum == 0 and dopatch and not gitworkdone:
939 if hunknum == 0 and dopatch and not gitworkdone:
940 raise NoHunks
940 raise NoHunks
941
941
942 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eol=None):
942 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eol=None):
943 """
943 """
944 Reads a patch from fp and tries to apply it.
944 Reads a patch from fp and tries to apply it.
945
945
946 The dict 'changed' is filled in with all of the filenames changed
946 The dict 'changed' is filled in with all of the filenames changed
947 by the patch. Returns 0 for a clean patch, -1 if any rejects were
947 by the patch. Returns 0 for a clean patch, -1 if any rejects were
948 found and 1 if there was any fuzz.
948 found and 1 if there was any fuzz.
949
949
950 If 'eol' is None, the patch content and patched file are read in
950 If 'eol' is None, the patch content and patched file are read in
951 binary mode. Otherwise, line endings are ignored when patching then
951 binary mode. Otherwise, line endings are ignored when patching then
952 normalized to 'eol' (usually '\n' or \r\n').
952 normalized to 'eol' (usually '\n' or \r\n').
953 """
953 """
954 rejects = 0
954 rejects = 0
955 err = 0
955 err = 0
956 current_file = None
956 current_file = None
957 gitpatches = None
957 gitpatches = None
958 opener = util.opener(os.getcwd())
958 opener = util.opener(os.getcwd())
959 textmode = eol is not None
959 textmode = eol is not None
960
960
961 def closefile():
961 def closefile():
962 if not current_file:
962 if not current_file:
963 return 0
963 return 0
964 current_file.close()
964 current_file.close()
965 return len(current_file.rej)
965 return len(current_file.rej)
966
966
967 for state, values in iterhunks(ui, fp, sourcefile, textmode):
967 for state, values in iterhunks(ui, fp, sourcefile, textmode):
968 if state == 'hunk':
968 if state == 'hunk':
969 if not current_file:
969 if not current_file:
970 continue
970 continue
971 current_hunk = values
971 current_hunk = values
972 ret = current_file.apply(current_hunk)
972 ret = current_file.apply(current_hunk)
973 if ret >= 0:
973 if ret >= 0:
974 changed.setdefault(current_file.fname, None)
974 changed.setdefault(current_file.fname, None)
975 if ret > 0:
975 if ret > 0:
976 err = 1
976 err = 1
977 elif state == 'file':
977 elif state == 'file':
978 rejects += closefile()
978 rejects += closefile()
979 afile, bfile, first_hunk = values
979 afile, bfile, first_hunk = values
980 try:
980 try:
981 if sourcefile:
981 if sourcefile:
982 current_file = patchfile(ui, sourcefile, opener, eol=eol)
982 current_file = patchfile(ui, sourcefile, opener, eol=eol)
983 else:
983 else:
984 current_file, missing = selectfile(afile, bfile, first_hunk,
984 current_file, missing = selectfile(afile, bfile, first_hunk,
985 strip)
985 strip)
986 current_file = patchfile(ui, current_file, opener, missing, eol)
986 current_file = patchfile(ui, current_file, opener, missing, eol)
987 except PatchError, err:
987 except PatchError, err:
988 ui.warn(str(err) + '\n')
988 ui.warn(str(err) + '\n')
989 current_file, current_hunk = None, None
989 current_file, current_hunk = None, None
990 rejects += 1
990 rejects += 1
991 continue
991 continue
992 elif state == 'git':
992 elif state == 'git':
993 gitpatches = values
993 gitpatches = values
994 cwd = os.getcwd()
994 cwd = os.getcwd()
995 for gp in gitpatches:
995 for gp in gitpatches:
996 if gp.op in ('COPY', 'RENAME'):
996 if gp.op in ('COPY', 'RENAME'):
997 copyfile(gp.oldpath, gp.path, cwd)
997 copyfile(gp.oldpath, gp.path, cwd)
998 changed[gp.path] = gp
998 changed[gp.path] = gp
999 else:
999 else:
1000 raise util.Abort(_('unsupported parser state: %s') % state)
1000 raise util.Abort(_('unsupported parser state: %s') % state)
1001
1001
1002 rejects += closefile()
1002 rejects += closefile()
1003
1003
1004 if rejects:
1004 if rejects:
1005 return -1
1005 return -1
1006 return err
1006 return err
1007
1007
1008 def diffopts(ui, opts=None, untrusted=False):
1008 def diffopts(ui, opts=None, untrusted=False):
1009 def get(key, name=None, getter=ui.configbool):
1009 def get(key, name=None, getter=ui.configbool):
1010 return ((opts and opts.get(key)) or
1010 return ((opts and opts.get(key)) or
1011 getter('diff', name or key, None, untrusted=untrusted))
1011 getter('diff', name or key, None, untrusted=untrusted))
1012 return mdiff.diffopts(
1012 return mdiff.diffopts(
1013 text=opts and opts.get('text'),
1013 text=opts and opts.get('text'),
1014 git=get('git'),
1014 git=get('git'),
1015 nodates=get('nodates'),
1015 nodates=get('nodates'),
1016 showfunc=get('show_function', 'showfunc'),
1016 showfunc=get('show_function', 'showfunc'),
1017 ignorews=get('ignore_all_space', 'ignorews'),
1017 ignorews=get('ignore_all_space', 'ignorews'),
1018 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1018 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1019 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1019 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1020 context=get('unified', getter=ui.config))
1020 context=get('unified', getter=ui.config))
1021
1021
1022 def updatedir(ui, repo, patches, similarity=0):
1022 def updatedir(ui, repo, patches, similarity=0):
1023 '''Update dirstate after patch application according to metadata'''
1023 '''Update dirstate after patch application according to metadata'''
1024 if not patches:
1024 if not patches:
1025 return
1025 return
1026 copies = []
1026 copies = []
1027 removes = set()
1027 removes = set()
1028 cfiles = patches.keys()
1028 cfiles = patches.keys()
1029 cwd = repo.getcwd()
1029 cwd = repo.getcwd()
1030 if cwd:
1030 if cwd:
1031 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1031 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1032 for f in patches:
1032 for f in patches:
1033 gp = patches[f]
1033 gp = patches[f]
1034 if not gp:
1034 if not gp:
1035 continue
1035 continue
1036 if gp.op == 'RENAME':
1036 if gp.op == 'RENAME':
1037 copies.append((gp.oldpath, gp.path))
1037 copies.append((gp.oldpath, gp.path))
1038 removes.add(gp.oldpath)
1038 removes.add(gp.oldpath)
1039 elif gp.op == 'COPY':
1039 elif gp.op == 'COPY':
1040 copies.append((gp.oldpath, gp.path))
1040 copies.append((gp.oldpath, gp.path))
1041 elif gp.op == 'DELETE':
1041 elif gp.op == 'DELETE':
1042 removes.add(gp.path)
1042 removes.add(gp.path)
1043 for src, dst in copies:
1043 for src, dst in copies:
1044 repo.copy(src, dst)
1044 repo.copy(src, dst)
1045 if (not similarity) and removes:
1045 if (not similarity) and removes:
1046 repo.remove(sorted(removes), True)
1046 repo.remove(sorted(removes), True)
1047 for f in patches:
1047 for f in patches:
1048 gp = patches[f]
1048 gp = patches[f]
1049 if gp and gp.mode:
1049 if gp and gp.mode:
1050 islink, isexec = gp.mode
1050 islink, isexec = gp.mode
1051 dst = repo.wjoin(gp.path)
1051 dst = repo.wjoin(gp.path)
1052 # patch won't create empty files
1052 # patch won't create empty files
1053 if gp.op == 'ADD' and not os.path.exists(dst):
1053 if gp.op == 'ADD' and not os.path.exists(dst):
1054 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1054 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1055 repo.wwrite(gp.path, '', flags)
1055 repo.wwrite(gp.path, '', flags)
1056 elif gp.op != 'DELETE':
1056 elif gp.op != 'DELETE':
1057 util.set_flags(dst, islink, isexec)
1057 util.set_flags(dst, islink, isexec)
1058 cmdutil.addremove(repo, cfiles, similarity=similarity)
1058 cmdutil.addremove(repo, cfiles, similarity=similarity)
1059 files = patches.keys()
1059 files = patches.keys()
1060 files.extend([r for r in removes if r not in files])
1060 files.extend([r for r in removes if r not in files])
1061 return sorted(files)
1061 return sorted(files)
1062
1062
1063 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1063 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1064 """use <patcher> to apply <patchname> to the working directory.
1064 """use <patcher> to apply <patchname> to the working directory.
1065 returns whether patch was applied with fuzz factor."""
1065 returns whether patch was applied with fuzz factor."""
1066
1066
1067 fuzz = False
1067 fuzz = False
1068 if cwd:
1068 if cwd:
1069 args.append('-d %s' % util.shellquote(cwd))
1069 args.append('-d %s' % util.shellquote(cwd))
1070 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1070 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1071 util.shellquote(patchname)))
1071 util.shellquote(patchname)))
1072
1072
1073 for line in fp:
1073 for line in fp:
1074 line = line.rstrip()
1074 line = line.rstrip()
1075 ui.note(line + '\n')
1075 ui.note(line + '\n')
1076 if line.startswith('patching file '):
1076 if line.startswith('patching file '):
1077 pf = util.parse_patch_output(line)
1077 pf = util.parse_patch_output(line)
1078 printed_file = False
1078 printed_file = False
1079 files.setdefault(pf, None)
1079 files.setdefault(pf, None)
1080 elif line.find('with fuzz') >= 0:
1080 elif line.find('with fuzz') >= 0:
1081 fuzz = True
1081 fuzz = True
1082 if not printed_file:
1082 if not printed_file:
1083 ui.warn(pf + '\n')
1083 ui.warn(pf + '\n')
1084 printed_file = True
1084 printed_file = True
1085 ui.warn(line + '\n')
1085 ui.warn(line + '\n')
1086 elif line.find('saving rejects to file') >= 0:
1086 elif line.find('saving rejects to file') >= 0:
1087 ui.warn(line + '\n')
1087 ui.warn(line + '\n')
1088 elif line.find('FAILED') >= 0:
1088 elif line.find('FAILED') >= 0:
1089 if not printed_file:
1089 if not printed_file:
1090 ui.warn(pf + '\n')
1090 ui.warn(pf + '\n')
1091 printed_file = True
1091 printed_file = True
1092 ui.warn(line + '\n')
1092 ui.warn(line + '\n')
1093 code = fp.close()
1093 code = fp.close()
1094 if code:
1094 if code:
1095 raise PatchError(_("patch command failed: %s") %
1095 raise PatchError(_("patch command failed: %s") %
1096 util.explain_exit(code)[0])
1096 util.explain_exit(code)[0])
1097 return fuzz
1097 return fuzz
1098
1098
1099 def internalpatch(patchobj, ui, strip, cwd, files=None, eolmode='strict'):
1099 def internalpatch(patchobj, ui, strip, cwd, files=None, eolmode='strict'):
1100 """use builtin patch to apply <patchobj> to the working directory.
1100 """use builtin patch to apply <patchobj> to the working directory.
1101 returns whether patch was applied with fuzz factor."""
1101 returns whether patch was applied with fuzz factor."""
1102
1102
1103 if files is None:
1103 if files is None:
1104 files = {}
1104 files = {}
1105 if eolmode is None:
1105 if eolmode is None:
1106 eolmode = ui.config('patch', 'eol', 'strict')
1106 eolmode = ui.config('patch', 'eol', 'strict')
1107 try:
1107 try:
1108 eol = {'strict': None, 'crlf': '\r\n', 'lf': '\n'}[eolmode.lower()]
1108 eol = {'strict': None, 'crlf': '\r\n', 'lf': '\n'}[eolmode.lower()]
1109 except KeyError:
1109 except KeyError:
1110 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1110 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1111
1111
1112 try:
1112 try:
1113 fp = open(patchobj, 'rb')
1113 fp = open(patchobj, 'rb')
1114 except TypeError:
1114 except TypeError:
1115 fp = patchobj
1115 fp = patchobj
1116 if cwd:
1116 if cwd:
1117 curdir = os.getcwd()
1117 curdir = os.getcwd()
1118 os.chdir(cwd)
1118 os.chdir(cwd)
1119 try:
1119 try:
1120 ret = applydiff(ui, fp, files, strip=strip, eol=eol)
1120 ret = applydiff(ui, fp, files, strip=strip, eol=eol)
1121 finally:
1121 finally:
1122 if cwd:
1122 if cwd:
1123 os.chdir(curdir)
1123 os.chdir(curdir)
1124 if ret < 0:
1124 if ret < 0:
1125 raise PatchError
1125 raise PatchError
1126 return ret > 0
1126 return ret > 0
1127
1127
1128 def patch(patchname, ui, strip=1, cwd=None, files=None, eolmode='strict'):
1128 def patch(patchname, ui, strip=1, cwd=None, files=None, eolmode='strict'):
1129 """Apply <patchname> to the working directory.
1129 """Apply <patchname> to the working directory.
1130
1130
1131 'eolmode' specifies how end of lines should be handled. It can be:
1131 'eolmode' specifies how end of lines should be handled. It can be:
1132 - 'strict': inputs are read in binary mode, EOLs are preserved
1132 - 'strict': inputs are read in binary mode, EOLs are preserved
1133 - 'crlf': EOLs are ignored when patching and reset to CRLF
1133 - 'crlf': EOLs are ignored when patching and reset to CRLF
1134 - 'lf': EOLs are ignored when patching and reset to LF
1134 - 'lf': EOLs are ignored when patching and reset to LF
1135 - None: get it from user settings, default to 'strict'
1135 - None: get it from user settings, default to 'strict'
1136 'eolmode' is ignored when using an external patcher program.
1136 'eolmode' is ignored when using an external patcher program.
1137
1137
1138 Returns whether patch was applied with fuzz factor.
1138 Returns whether patch was applied with fuzz factor.
1139 """
1139 """
1140 patcher = ui.config('ui', 'patch')
1140 patcher = ui.config('ui', 'patch')
1141 args = []
1141 args = []
1142 if files is None:
1142 if files is None:
1143 files = {}
1143 files = {}
1144 try:
1144 try:
1145 if patcher:
1145 if patcher:
1146 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1146 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1147 files)
1147 files)
1148 else:
1148 else:
1149 try:
1149 try:
1150 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1150 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1151 except NoHunks:
1151 except NoHunks:
1152 patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch'
1152 patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch'
1153 ui.debug('no valid hunks found; trying with %r instead\n' %
1153 ui.debug('no valid hunks found; trying with %r instead\n' %
1154 patcher)
1154 patcher)
1155 if util.needbinarypatch():
1155 if util.needbinarypatch():
1156 args.append('--binary')
1156 args.append('--binary')
1157 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1157 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1158 files)
1158 files)
1159 except PatchError, err:
1159 except PatchError, err:
1160 s = str(err)
1160 s = str(err)
1161 if s:
1161 if s:
1162 raise util.Abort(s)
1162 raise util.Abort(s)
1163 else:
1163 else:
1164 raise util.Abort(_('patch failed to apply'))
1164 raise util.Abort(_('patch failed to apply'))
1165
1165
1166 def b85diff(to, tn):
1166 def b85diff(to, tn):
1167 '''print base85-encoded binary diff'''
1167 '''print base85-encoded binary diff'''
1168 def gitindex(text):
1168 def gitindex(text):
1169 if not text:
1169 if not text:
1170 return '0' * 40
1170 return '0' * 40
1171 l = len(text)
1171 l = len(text)
1172 s = util.sha1('blob %d\0' % l)
1172 s = util.sha1('blob %d\0' % l)
1173 s.update(text)
1173 s.update(text)
1174 return s.hexdigest()
1174 return s.hexdigest()
1175
1175
1176 def fmtline(line):
1176 def fmtline(line):
1177 l = len(line)
1177 l = len(line)
1178 if l <= 26:
1178 if l <= 26:
1179 l = chr(ord('A') + l - 1)
1179 l = chr(ord('A') + l - 1)
1180 else:
1180 else:
1181 l = chr(l - 26 + ord('a') - 1)
1181 l = chr(l - 26 + ord('a') - 1)
1182 return '%c%s\n' % (l, base85.b85encode(line, True))
1182 return '%c%s\n' % (l, base85.b85encode(line, True))
1183
1183
1184 def chunk(text, csize=52):
1184 def chunk(text, csize=52):
1185 l = len(text)
1185 l = len(text)
1186 i = 0
1186 i = 0
1187 while i < l:
1187 while i < l:
1188 yield text[i:i+csize]
1188 yield text[i:i+csize]
1189 i += csize
1189 i += csize
1190
1190
1191 tohash = gitindex(to)
1191 tohash = gitindex(to)
1192 tnhash = gitindex(tn)
1192 tnhash = gitindex(tn)
1193 if tohash == tnhash:
1193 if tohash == tnhash:
1194 return ""
1194 return ""
1195
1195
1196 # TODO: deltas
1196 # TODO: deltas
1197 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1197 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1198 (tohash, tnhash, len(tn))]
1198 (tohash, tnhash, len(tn))]
1199 for l in chunk(zlib.compress(tn)):
1199 for l in chunk(zlib.compress(tn)):
1200 ret.append(fmtline(l))
1200 ret.append(fmtline(l))
1201 ret.append('\n')
1201 ret.append('\n')
1202 return ''.join(ret)
1202 return ''.join(ret)
1203
1203
1204 def _addmodehdr(header, omode, nmode):
1204 def _addmodehdr(header, omode, nmode):
1205 if omode != nmode:
1205 if omode != nmode:
1206 header.append('old mode %s\n' % omode)
1206 header.append('old mode %s\n' % omode)
1207 header.append('new mode %s\n' % nmode)
1207 header.append('new mode %s\n' % nmode)
1208
1208
1209 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None):
1209 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None):
1210 '''yields diff of changes to files between two nodes, or node and
1210 '''yields diff of changes to files between two nodes, or node and
1211 working directory.
1211 working directory.
1212
1212
1213 if node1 is None, use first dirstate parent instead.
1213 if node1 is None, use first dirstate parent instead.
1214 if node2 is None, compare node1 with working directory.'''
1214 if node2 is None, compare node1 with working directory.'''
1215
1215
1216 if opts is None:
1216 if opts is None:
1217 opts = mdiff.defaultopts
1217 opts = mdiff.defaultopts
1218
1218
1219 if not node1:
1219 if not node1 and not node2:
1220 node1 = repo.dirstate.parents()[0]
1220 node1 = repo.dirstate.parents()[0]
1221
1221
1222 def lrugetfilectx():
1222 def lrugetfilectx():
1223 cache = {}
1223 cache = {}
1224 order = []
1224 order = []
1225 def getfilectx(f, ctx):
1225 def getfilectx(f, ctx):
1226 fctx = ctx.filectx(f, filelog=cache.get(f))
1226 fctx = ctx.filectx(f, filelog=cache.get(f))
1227 if f not in cache:
1227 if f not in cache:
1228 if len(cache) > 20:
1228 if len(cache) > 20:
1229 del cache[order.pop(0)]
1229 del cache[order.pop(0)]
1230 cache[f] = fctx.filelog()
1230 cache[f] = fctx.filelog()
1231 else:
1231 else:
1232 order.remove(f)
1232 order.remove(f)
1233 order.append(f)
1233 order.append(f)
1234 return fctx
1234 return fctx
1235 return getfilectx
1235 return getfilectx
1236 getfilectx = lrugetfilectx()
1236 getfilectx = lrugetfilectx()
1237
1237
1238 ctx1 = repo[node1]
1238 ctx1 = repo[node1]
1239 ctx2 = repo[node2]
1239 ctx2 = repo[node2]
1240
1240
1241 if not changes:
1241 if not changes:
1242 changes = repo.status(ctx1, ctx2, match=match)
1242 changes = repo.status(ctx1, ctx2, match=match)
1243 modified, added, removed = changes[:3]
1243 modified, added, removed = changes[:3]
1244
1244
1245 if not modified and not added and not removed:
1245 if not modified and not added and not removed:
1246 return
1246 return
1247
1247
1248 date1 = util.datestr(ctx1.date())
1248 date1 = util.datestr(ctx1.date())
1249 man1 = ctx1.manifest()
1249 man1 = ctx1.manifest()
1250
1250
1251 if repo.ui.quiet:
1251 if repo.ui.quiet:
1252 r = None
1252 r = None
1253 else:
1253 else:
1254 hexfunc = repo.ui.debugflag and hex or short
1254 hexfunc = repo.ui.debugflag and hex or short
1255 r = [hexfunc(node) for node in [node1, node2] if node]
1255 r = [hexfunc(node) for node in [node1, node2] if node]
1256
1256
1257 if opts.git:
1257 if opts.git:
1258 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
1258 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
1259 copy = copy.copy()
1259 copy = copy.copy()
1260 for k, v in copy.items():
1260 for k, v in copy.items():
1261 copy[v] = k
1261 copy[v] = k
1262
1262
1263 gone = set()
1263 gone = set()
1264 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1264 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1265
1265
1266 for f in sorted(modified + added + removed):
1266 for f in sorted(modified + added + removed):
1267 to = None
1267 to = None
1268 tn = None
1268 tn = None
1269 dodiff = True
1269 dodiff = True
1270 header = []
1270 header = []
1271 if f in man1:
1271 if f in man1:
1272 to = getfilectx(f, ctx1).data()
1272 to = getfilectx(f, ctx1).data()
1273 if f not in removed:
1273 if f not in removed:
1274 tn = getfilectx(f, ctx2).data()
1274 tn = getfilectx(f, ctx2).data()
1275 a, b = f, f
1275 a, b = f, f
1276 if opts.git:
1276 if opts.git:
1277 if f in added:
1277 if f in added:
1278 mode = gitmode[ctx2.flags(f)]
1278 mode = gitmode[ctx2.flags(f)]
1279 if f in copy:
1279 if f in copy:
1280 a = copy[f]
1280 a = copy[f]
1281 omode = gitmode[man1.flags(a)]
1281 omode = gitmode[man1.flags(a)]
1282 _addmodehdr(header, omode, mode)
1282 _addmodehdr(header, omode, mode)
1283 if a in removed and a not in gone:
1283 if a in removed and a not in gone:
1284 op = 'rename'
1284 op = 'rename'
1285 gone.add(a)
1285 gone.add(a)
1286 else:
1286 else:
1287 op = 'copy'
1287 op = 'copy'
1288 header.append('%s from %s\n' % (op, a))
1288 header.append('%s from %s\n' % (op, a))
1289 header.append('%s to %s\n' % (op, f))
1289 header.append('%s to %s\n' % (op, f))
1290 to = getfilectx(a, ctx1).data()
1290 to = getfilectx(a, ctx1).data()
1291 else:
1291 else:
1292 header.append('new file mode %s\n' % mode)
1292 header.append('new file mode %s\n' % mode)
1293 if util.binary(tn):
1293 if util.binary(tn):
1294 dodiff = 'binary'
1294 dodiff = 'binary'
1295 elif f in removed:
1295 elif f in removed:
1296 # have we already reported a copy above?
1296 # have we already reported a copy above?
1297 if f in copy and copy[f] in added and copy[copy[f]] == f:
1297 if f in copy and copy[f] in added and copy[copy[f]] == f:
1298 dodiff = False
1298 dodiff = False
1299 else:
1299 else:
1300 header.append('deleted file mode %s\n' %
1300 header.append('deleted file mode %s\n' %
1301 gitmode[man1.flags(f)])
1301 gitmode[man1.flags(f)])
1302 else:
1302 else:
1303 omode = gitmode[man1.flags(f)]
1303 omode = gitmode[man1.flags(f)]
1304 nmode = gitmode[ctx2.flags(f)]
1304 nmode = gitmode[ctx2.flags(f)]
1305 _addmodehdr(header, omode, nmode)
1305 _addmodehdr(header, omode, nmode)
1306 if util.binary(to) or util.binary(tn):
1306 if util.binary(to) or util.binary(tn):
1307 dodiff = 'binary'
1307 dodiff = 'binary'
1308 r = None
1308 r = None
1309 header.insert(0, mdiff.diffline(r, a, b, opts))
1309 header.insert(0, mdiff.diffline(r, a, b, opts))
1310 if dodiff:
1310 if dodiff:
1311 if dodiff == 'binary':
1311 if dodiff == 'binary':
1312 text = b85diff(to, tn)
1312 text = b85diff(to, tn)
1313 else:
1313 else:
1314 text = mdiff.unidiff(to, date1,
1314 text = mdiff.unidiff(to, date1,
1315 # ctx2 date may be dynamic
1315 # ctx2 date may be dynamic
1316 tn, util.datestr(ctx2.date()),
1316 tn, util.datestr(ctx2.date()),
1317 a, b, r, opts=opts)
1317 a, b, r, opts=opts)
1318 if header and (text or len(header) > 1):
1318 if header and (text or len(header) > 1):
1319 yield ''.join(header)
1319 yield ''.join(header)
1320 if text:
1320 if text:
1321 yield text
1321 yield text
1322
1322
1323 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1323 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1324 opts=None):
1324 opts=None):
1325 '''export changesets as hg patches.'''
1325 '''export changesets as hg patches.'''
1326
1326
1327 total = len(revs)
1327 total = len(revs)
1328 revwidth = max([len(str(rev)) for rev in revs])
1328 revwidth = max([len(str(rev)) for rev in revs])
1329
1329
1330 def single(rev, seqno, fp):
1330 def single(rev, seqno, fp):
1331 ctx = repo[rev]
1331 ctx = repo[rev]
1332 node = ctx.node()
1332 node = ctx.node()
1333 parents = [p.node() for p in ctx.parents() if p]
1333 parents = [p.node() for p in ctx.parents() if p]
1334 branch = ctx.branch()
1334 branch = ctx.branch()
1335 if switch_parent:
1335 if switch_parent:
1336 parents.reverse()
1336 parents.reverse()
1337 prev = (parents and parents[0]) or nullid
1337 prev = (parents and parents[0]) or nullid
1338
1338
1339 if not fp:
1339 if not fp:
1340 fp = cmdutil.make_file(repo, template, node, total=total,
1340 fp = cmdutil.make_file(repo, template, node, total=total,
1341 seqno=seqno, revwidth=revwidth,
1341 seqno=seqno, revwidth=revwidth,
1342 mode='ab')
1342 mode='ab')
1343 if fp != sys.stdout and hasattr(fp, 'name'):
1343 if fp != sys.stdout and hasattr(fp, 'name'):
1344 repo.ui.note("%s\n" % fp.name)
1344 repo.ui.note("%s\n" % fp.name)
1345
1345
1346 fp.write("# HG changeset patch\n")
1346 fp.write("# HG changeset patch\n")
1347 fp.write("# User %s\n" % ctx.user())
1347 fp.write("# User %s\n" % ctx.user())
1348 fp.write("# Date %d %d\n" % ctx.date())
1348 fp.write("# Date %d %d\n" % ctx.date())
1349 if branch and (branch != 'default'):
1349 if branch and (branch != 'default'):
1350 fp.write("# Branch %s\n" % branch)
1350 fp.write("# Branch %s\n" % branch)
1351 fp.write("# Node ID %s\n" % hex(node))
1351 fp.write("# Node ID %s\n" % hex(node))
1352 fp.write("# Parent %s\n" % hex(prev))
1352 fp.write("# Parent %s\n" % hex(prev))
1353 if len(parents) > 1:
1353 if len(parents) > 1:
1354 fp.write("# Parent %s\n" % hex(parents[1]))
1354 fp.write("# Parent %s\n" % hex(parents[1]))
1355 fp.write(ctx.description().rstrip())
1355 fp.write(ctx.description().rstrip())
1356 fp.write("\n\n")
1356 fp.write("\n\n")
1357
1357
1358 for chunk in diff(repo, prev, node, opts=opts):
1358 for chunk in diff(repo, prev, node, opts=opts):
1359 fp.write(chunk)
1359 fp.write(chunk)
1360
1360
1361 for seqno, rev in enumerate(revs):
1361 for seqno, rev in enumerate(revs):
1362 single(rev, seqno+1, fp)
1362 single(rev, seqno+1, fp)
1363
1363
1364 def diffstatdata(lines):
1364 def diffstatdata(lines):
1365 filename, adds, removes = None, 0, 0
1365 filename, adds, removes = None, 0, 0
1366 for line in lines:
1366 for line in lines:
1367 if line.startswith('diff'):
1367 if line.startswith('diff'):
1368 if filename:
1368 if filename:
1369 isbinary = adds == 0 and removes == 0
1369 isbinary = adds == 0 and removes == 0
1370 yield (filename, adds, removes, isbinary)
1370 yield (filename, adds, removes, isbinary)
1371 # set numbers to 0 anyway when starting new file
1371 # set numbers to 0 anyway when starting new file
1372 adds, removes = 0, 0
1372 adds, removes = 0, 0
1373 if line.startswith('diff --git'):
1373 if line.startswith('diff --git'):
1374 filename = gitre.search(line).group(1)
1374 filename = gitre.search(line).group(1)
1375 else:
1375 else:
1376 # format: "diff -r ... -r ... filename"
1376 # format: "diff -r ... -r ... filename"
1377 filename = line.split(None, 5)[-1]
1377 filename = line.split(None, 5)[-1]
1378 elif line.startswith('+') and not line.startswith('+++'):
1378 elif line.startswith('+') and not line.startswith('+++'):
1379 adds += 1
1379 adds += 1
1380 elif line.startswith('-') and not line.startswith('---'):
1380 elif line.startswith('-') and not line.startswith('---'):
1381 removes += 1
1381 removes += 1
1382 if filename:
1382 if filename:
1383 isbinary = adds == 0 and removes == 0
1383 isbinary = adds == 0 and removes == 0
1384 yield (filename, adds, removes, isbinary)
1384 yield (filename, adds, removes, isbinary)
1385
1385
1386 def diffstat(lines, width=80, git=False):
1386 def diffstat(lines, width=80, git=False):
1387 output = []
1387 output = []
1388 stats = list(diffstatdata(lines))
1388 stats = list(diffstatdata(lines))
1389
1389
1390 maxtotal, maxname = 0, 0
1390 maxtotal, maxname = 0, 0
1391 totaladds, totalremoves = 0, 0
1391 totaladds, totalremoves = 0, 0
1392 hasbinary = False
1392 hasbinary = False
1393 for filename, adds, removes, isbinary in stats:
1393 for filename, adds, removes, isbinary in stats:
1394 totaladds += adds
1394 totaladds += adds
1395 totalremoves += removes
1395 totalremoves += removes
1396 maxname = max(maxname, len(filename))
1396 maxname = max(maxname, len(filename))
1397 maxtotal = max(maxtotal, adds+removes)
1397 maxtotal = max(maxtotal, adds+removes)
1398 if isbinary:
1398 if isbinary:
1399 hasbinary = True
1399 hasbinary = True
1400
1400
1401 countwidth = len(str(maxtotal))
1401 countwidth = len(str(maxtotal))
1402 if hasbinary and countwidth < 3:
1402 if hasbinary and countwidth < 3:
1403 countwidth = 3
1403 countwidth = 3
1404 graphwidth = width - countwidth - maxname - 6
1404 graphwidth = width - countwidth - maxname - 6
1405 if graphwidth < 10:
1405 if graphwidth < 10:
1406 graphwidth = 10
1406 graphwidth = 10
1407
1407
1408 def scale(i):
1408 def scale(i):
1409 if maxtotal <= graphwidth:
1409 if maxtotal <= graphwidth:
1410 return i
1410 return i
1411 # If diffstat runs out of room it doesn't print anything,
1411 # If diffstat runs out of room it doesn't print anything,
1412 # which isn't very useful, so always print at least one + or -
1412 # which isn't very useful, so always print at least one + or -
1413 # if there were at least some changes.
1413 # if there were at least some changes.
1414 return max(i * graphwidth // maxtotal, int(bool(i)))
1414 return max(i * graphwidth // maxtotal, int(bool(i)))
1415
1415
1416 for filename, adds, removes, isbinary in stats:
1416 for filename, adds, removes, isbinary in stats:
1417 if git and isbinary:
1417 if git and isbinary:
1418 count = 'Bin'
1418 count = 'Bin'
1419 else:
1419 else:
1420 count = adds + removes
1420 count = adds + removes
1421 pluses = '+' * scale(adds)
1421 pluses = '+' * scale(adds)
1422 minuses = '-' * scale(removes)
1422 minuses = '-' * scale(removes)
1423 output.append(' %-*s | %*s %s%s\n' % (maxname, filename, countwidth,
1423 output.append(' %-*s | %*s %s%s\n' % (maxname, filename, countwidth,
1424 count, pluses, minuses))
1424 count, pluses, minuses))
1425
1425
1426 if stats:
1426 if stats:
1427 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1427 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1428 % (len(stats), totaladds, totalremoves))
1428 % (len(stats), totaladds, totalremoves))
1429
1429
1430 return ''.join(output)
1430 return ''.join(output)
@@ -1,231 +1,231 b''
1 % Show all commands except debug commands
1 % Show all commands except debug commands
2 add
2 add
3 addremove
3 addremove
4 annotate
4 annotate
5 archive
5 archive
6 backout
6 backout
7 bisect
7 bisect
8 branch
8 branch
9 branches
9 branches
10 bundle
10 bundle
11 cat
11 cat
12 clone
12 clone
13 commit
13 commit
14 copy
14 copy
15 diff
15 diff
16 export
16 export
17 forget
17 forget
18 grep
18 grep
19 heads
19 heads
20 help
20 help
21 identify
21 identify
22 import
22 import
23 incoming
23 incoming
24 init
24 init
25 locate
25 locate
26 log
26 log
27 manifest
27 manifest
28 merge
28 merge
29 outgoing
29 outgoing
30 parents
30 parents
31 paths
31 paths
32 pull
32 pull
33 push
33 push
34 recover
34 recover
35 remove
35 remove
36 rename
36 rename
37 resolve
37 resolve
38 revert
38 revert
39 rollback
39 rollback
40 root
40 root
41 serve
41 serve
42 showconfig
42 showconfig
43 status
43 status
44 summary
44 summary
45 tag
45 tag
46 tags
46 tags
47 tip
47 tip
48 unbundle
48 unbundle
49 update
49 update
50 verify
50 verify
51 version
51 version
52
52
53 % Show all commands that start with "a"
53 % Show all commands that start with "a"
54 add
54 add
55 addremove
55 addremove
56 annotate
56 annotate
57 archive
57 archive
58
58
59 % Do not show debug commands if there are other candidates
59 % Do not show debug commands if there are other candidates
60 diff
60 diff
61
61
62 % Show debug commands if there are no other candidates
62 % Show debug commands if there are no other candidates
63 debugancestor
63 debugancestor
64 debugcheckstate
64 debugcheckstate
65 debugcommands
65 debugcommands
66 debugcomplete
66 debugcomplete
67 debugconfig
67 debugconfig
68 debugdata
68 debugdata
69 debugdate
69 debugdate
70 debugfsinfo
70 debugfsinfo
71 debugindex
71 debugindex
72 debugindexdot
72 debugindexdot
73 debuginstall
73 debuginstall
74 debugrebuildstate
74 debugrebuildstate
75 debugrename
75 debugrename
76 debugsetparents
76 debugsetparents
77 debugstate
77 debugstate
78 debugsub
78 debugsub
79 debugwalk
79 debugwalk
80
80
81 % Do not show the alias of a debug command if there are other candidates
81 % Do not show the alias of a debug command if there are other candidates
82 % (this should hide rawcommit)
82 % (this should hide rawcommit)
83 recover
83 recover
84 remove
84 remove
85 rename
85 rename
86 resolve
86 resolve
87 revert
87 revert
88 rollback
88 rollback
89 root
89 root
90
90
91 % Show the alias of a debug command if there are no other candidates
91 % Show the alias of a debug command if there are no other candidates
92
92
93
93
94 % Show the global options
94 % Show the global options
95 --config
95 --config
96 --cwd
96 --cwd
97 --debug
97 --debug
98 --debugger
98 --debugger
99 --encoding
99 --encoding
100 --encodingmode
100 --encodingmode
101 --help
101 --help
102 --noninteractive
102 --noninteractive
103 --profile
103 --profile
104 --quiet
104 --quiet
105 --repository
105 --repository
106 --time
106 --time
107 --traceback
107 --traceback
108 --verbose
108 --verbose
109 --version
109 --version
110 -R
110 -R
111 -h
111 -h
112 -q
112 -q
113 -v
113 -v
114 -y
114 -y
115
115
116 % Show the options for the "serve" command
116 % Show the options for the "serve" command
117 --accesslog
117 --accesslog
118 --address
118 --address
119 --certificate
119 --certificate
120 --config
120 --config
121 --cwd
121 --cwd
122 --daemon
122 --daemon
123 --daemon-pipefds
123 --daemon-pipefds
124 --debug
124 --debug
125 --debugger
125 --debugger
126 --encoding
126 --encoding
127 --encodingmode
127 --encodingmode
128 --errorlog
128 --errorlog
129 --help
129 --help
130 --ipv6
130 --ipv6
131 --name
131 --name
132 --noninteractive
132 --noninteractive
133 --pid-file
133 --pid-file
134 --port
134 --port
135 --prefix
135 --prefix
136 --profile
136 --profile
137 --quiet
137 --quiet
138 --repository
138 --repository
139 --stdio
139 --stdio
140 --style
140 --style
141 --templates
141 --templates
142 --time
142 --time
143 --traceback
143 --traceback
144 --verbose
144 --verbose
145 --version
145 --version
146 --webdir-conf
146 --webdir-conf
147 -6
147 -6
148 -A
148 -A
149 -E
149 -E
150 -R
150 -R
151 -a
151 -a
152 -d
152 -d
153 -h
153 -h
154 -n
154 -n
155 -p
155 -p
156 -q
156 -q
157 -t
157 -t
158 -v
158 -v
159 -y
159 -y
160
160
161 % Show an error if we use --options with an ambiguous abbreviation
161 % Show an error if we use --options with an ambiguous abbreviation
162 hg: command 's' is ambiguous:
162 hg: command 's' is ambiguous:
163 serve showconfig status summary
163 serve showconfig status summary
164
164
165 % Show all commands + options
165 % Show all commands + options
166 add: include, exclude, dry-run
166 add: include, exclude, dry-run
167 annotate: rev, follow, text, user, date, number, changeset, line-number, include, exclude
167 annotate: rev, follow, text, user, date, number, changeset, line-number, include, exclude
168 clone: noupdate, updaterev, rev, pull, uncompressed, ssh, remotecmd
168 clone: noupdate, updaterev, rev, pull, uncompressed, ssh, remotecmd
169 commit: addremove, close-branch, include, exclude, message, logfile, date, user
169 commit: addremove, close-branch, include, exclude, message, logfile, date, user
170 diff: rev, change, text, git, nodates, show-function, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude
170 diff: rev, change, text, git, nodates, show-function, inverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude
171 export: output, switch-parent, text, git, nodates
171 export: output, switch-parent, text, git, nodates
172 forget: include, exclude
172 forget: include, exclude
173 init: ssh, remotecmd
173 init: ssh, remotecmd
174 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, prune, patch, git, limit, no-merges, style, template, include, exclude
174 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, prune, patch, git, limit, no-merges, style, template, include, exclude
175 merge: force, rev, preview
175 merge: force, rev, preview
176 pull: update, force, rev, ssh, remotecmd
176 pull: update, force, rev, ssh, remotecmd
177 push: force, rev, ssh, remotecmd
177 push: force, rev, ssh, remotecmd
178 remove: after, force, include, exclude
178 remove: after, force, include, exclude
179 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
179 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
180 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, include, exclude
180 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, include, exclude
181 summary: remote
181 summary: remote
182 update: clean, check, date, rev
182 update: clean, check, date, rev
183 addremove: similarity, include, exclude, dry-run
183 addremove: similarity, include, exclude, dry-run
184 archive: no-decode, prefix, rev, type, include, exclude
184 archive: no-decode, prefix, rev, type, include, exclude
185 backout: merge, parent, rev, include, exclude, message, logfile, date, user
185 backout: merge, parent, rev, include, exclude, message, logfile, date, user
186 bisect: reset, good, bad, skip, command, noupdate
186 bisect: reset, good, bad, skip, command, noupdate
187 branch: force, clean
187 branch: force, clean
188 branches: active, closed
188 branches: active, closed
189 bundle: force, rev, base, all, type, ssh, remotecmd
189 bundle: force, rev, base, all, type, ssh, remotecmd
190 cat: output, rev, decode, include, exclude
190 cat: output, rev, decode, include, exclude
191 copy: after, force, include, exclude, dry-run
191 copy: after, force, include, exclude, dry-run
192 debugancestor:
192 debugancestor:
193 debugcheckstate:
193 debugcheckstate:
194 debugcommands:
194 debugcommands:
195 debugcomplete: options
195 debugcomplete: options
196 debugdata:
196 debugdata:
197 debugdate: extended
197 debugdate: extended
198 debugfsinfo:
198 debugfsinfo:
199 debugindex:
199 debugindex:
200 debugindexdot:
200 debugindexdot:
201 debuginstall:
201 debuginstall:
202 debugrebuildstate: rev
202 debugrebuildstate: rev
203 debugrename: rev
203 debugrename: rev
204 debugsetparents:
204 debugsetparents:
205 debugstate: nodates
205 debugstate: nodates
206 debugsub: rev
206 debugsub: rev
207 debugwalk: include, exclude
207 debugwalk: include, exclude
208 grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
208 grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
209 heads: rev, active, closed, style, template
209 heads: rev, active, closed, style, template
210 help:
210 help:
211 identify: rev, num, id, branch, tags
211 identify: rev, num, id, branch, tags
212 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
212 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
213 incoming: force, newest-first, bundle, rev, patch, git, limit, no-merges, style, template, ssh, remotecmd
213 incoming: force, newest-first, bundle, rev, patch, git, limit, no-merges, style, template, ssh, remotecmd
214 locate: rev, print0, fullpath, include, exclude
214 locate: rev, print0, fullpath, include, exclude
215 manifest: rev
215 manifest: rev
216 outgoing: force, rev, newest-first, patch, git, limit, no-merges, style, template, ssh, remotecmd
216 outgoing: force, rev, newest-first, patch, git, limit, no-merges, style, template, ssh, remotecmd
217 parents: rev, style, template
217 parents: rev, style, template
218 paths:
218 paths:
219 recover:
219 recover:
220 rename: after, force, include, exclude, dry-run
220 rename: after, force, include, exclude, dry-run
221 resolve: all, list, mark, unmark, no-status, include, exclude
221 resolve: all, list, mark, unmark, no-status, include, exclude
222 revert: all, date, rev, no-backup, include, exclude, dry-run
222 revert: all, date, rev, no-backup, include, exclude, dry-run
223 rollback:
223 rollback:
224 root:
224 root:
225 showconfig: untrusted
225 showconfig: untrusted
226 tag: force, local, rev, remove, message, date, user
226 tag: force, local, rev, remove, message, date, user
227 tags:
227 tags:
228 tip: patch, git, style, template
228 tip: patch, git, style, template
229 unbundle: update
229 unbundle: update
230 verify:
230 verify:
231 version:
231 version:
@@ -1,351 +1,352 b''
1 Mercurial Distributed SCM
1 Mercurial Distributed SCM
2
2
3 basic commands:
3 basic commands:
4
4
5 add add the specified files on the next commit
5 add add the specified files on the next commit
6 annotate show changeset information by line for each file
6 annotate show changeset information by line for each file
7 clone make a copy of an existing repository
7 clone make a copy of an existing repository
8 commit commit the specified files or all outstanding changes
8 commit commit the specified files or all outstanding changes
9 diff diff repository (or selected files)
9 diff diff repository (or selected files)
10 export dump the header and diffs for one or more changesets
10 export dump the header and diffs for one or more changesets
11 forget forget the specified files on the next commit
11 forget forget the specified files on the next commit
12 init create a new repository in the given directory
12 init create a new repository in the given directory
13 log show revision history of entire repository or files
13 log show revision history of entire repository or files
14 merge merge working directory with another revision
14 merge merge working directory with another revision
15 pull pull changes from the specified source
15 pull pull changes from the specified source
16 push push changes to the specified destination
16 push push changes to the specified destination
17 remove remove the specified files on the next commit
17 remove remove the specified files on the next commit
18 serve export the repository via HTTP
18 serve export the repository via HTTP
19 status show changed files in the working directory
19 status show changed files in the working directory
20 summary summarize working directory state
20 summary summarize working directory state
21 update update working directory
21 update update working directory
22
22
23 use "hg help" for the full list of commands or "hg -v" for details
23 use "hg help" for the full list of commands or "hg -v" for details
24 add add the specified files on the next commit
24 add add the specified files on the next commit
25 annotate show changeset information by line for each file
25 annotate show changeset information by line for each file
26 clone make a copy of an existing repository
26 clone make a copy of an existing repository
27 commit commit the specified files or all outstanding changes
27 commit commit the specified files or all outstanding changes
28 diff diff repository (or selected files)
28 diff diff repository (or selected files)
29 export dump the header and diffs for one or more changesets
29 export dump the header and diffs for one or more changesets
30 forget forget the specified files on the next commit
30 forget forget the specified files on the next commit
31 init create a new repository in the given directory
31 init create a new repository in the given directory
32 log show revision history of entire repository or files
32 log show revision history of entire repository or files
33 merge merge working directory with another revision
33 merge merge working directory with another revision
34 pull pull changes from the specified source
34 pull pull changes from the specified source
35 push push changes to the specified destination
35 push push changes to the specified destination
36 remove remove the specified files on the next commit
36 remove remove the specified files on the next commit
37 serve export the repository via HTTP
37 serve export the repository via HTTP
38 status show changed files in the working directory
38 status show changed files in the working directory
39 summary summarize working directory state
39 summary summarize working directory state
40 update update working directory
40 update update working directory
41 Mercurial Distributed SCM
41 Mercurial Distributed SCM
42
42
43 list of commands:
43 list of commands:
44
44
45 add add the specified files on the next commit
45 add add the specified files on the next commit
46 addremove add all new files, delete all missing files
46 addremove add all new files, delete all missing files
47 annotate show changeset information by line for each file
47 annotate show changeset information by line for each file
48 archive create an unversioned archive of a repository revision
48 archive create an unversioned archive of a repository revision
49 backout reverse effect of earlier changeset
49 backout reverse effect of earlier changeset
50 bisect subdivision search of changesets
50 bisect subdivision search of changesets
51 branch set or show the current branch name
51 branch set or show the current branch name
52 branches list repository named branches
52 branches list repository named branches
53 bundle create a changegroup file
53 bundle create a changegroup file
54 cat output the current or given revision of files
54 cat output the current or given revision of files
55 clone make a copy of an existing repository
55 clone make a copy of an existing repository
56 commit commit the specified files or all outstanding changes
56 commit commit the specified files or all outstanding changes
57 copy mark files as copied for the next commit
57 copy mark files as copied for the next commit
58 diff diff repository (or selected files)
58 diff diff repository (or selected files)
59 export dump the header and diffs for one or more changesets
59 export dump the header and diffs for one or more changesets
60 forget forget the specified files on the next commit
60 forget forget the specified files on the next commit
61 grep search for a pattern in specified files and revisions
61 grep search for a pattern in specified files and revisions
62 heads show current repository heads or show branch heads
62 heads show current repository heads or show branch heads
63 help show help for a given topic or a help overview
63 help show help for a given topic or a help overview
64 identify identify the working copy or specified revision
64 identify identify the working copy or specified revision
65 import import an ordered set of patches
65 import import an ordered set of patches
66 incoming show new changesets found in source
66 incoming show new changesets found in source
67 init create a new repository in the given directory
67 init create a new repository in the given directory
68 locate locate files matching specific patterns
68 locate locate files matching specific patterns
69 log show revision history of entire repository or files
69 log show revision history of entire repository or files
70 manifest output the current or given revision of the project manifest
70 manifest output the current or given revision of the project manifest
71 merge merge working directory with another revision
71 merge merge working directory with another revision
72 outgoing show changesets not found in destination
72 outgoing show changesets not found in destination
73 parents show the parents of the working directory or revision
73 parents show the parents of the working directory or revision
74 paths show aliases for remote repositories
74 paths show aliases for remote repositories
75 pull pull changes from the specified source
75 pull pull changes from the specified source
76 push push changes to the specified destination
76 push push changes to the specified destination
77 recover roll back an interrupted transaction
77 recover roll back an interrupted transaction
78 remove remove the specified files on the next commit
78 remove remove the specified files on the next commit
79 rename rename files; equivalent of copy + remove
79 rename rename files; equivalent of copy + remove
80 resolve retry file merges from a merge or update
80 resolve retry file merges from a merge or update
81 revert restore individual files or directories to an earlier state
81 revert restore individual files or directories to an earlier state
82 rollback roll back the last transaction
82 rollback roll back the last transaction
83 root print the root (top) of the current working directory
83 root print the root (top) of the current working directory
84 serve export the repository via HTTP
84 serve export the repository via HTTP
85 showconfig show combined config settings from all hgrc files
85 showconfig show combined config settings from all hgrc files
86 status show changed files in the working directory
86 status show changed files in the working directory
87 summary summarize working directory state
87 summary summarize working directory state
88 tag add one or more tags for the current or given revision
88 tag add one or more tags for the current or given revision
89 tags list repository tags
89 tags list repository tags
90 tip show the tip revision
90 tip show the tip revision
91 unbundle apply one or more changegroup files
91 unbundle apply one or more changegroup files
92 update update working directory
92 update update working directory
93 verify verify the integrity of the repository
93 verify verify the integrity of the repository
94 version output version and copyright information
94 version output version and copyright information
95
95
96 additional help topics:
96 additional help topics:
97
97
98 dates Date Formats
98 dates Date Formats
99 patterns File Name Patterns
99 patterns File Name Patterns
100 environment Environment Variables
100 environment Environment Variables
101 revisions Specifying Single Revisions
101 revisions Specifying Single Revisions
102 multirevs Specifying Multiple Revisions
102 multirevs Specifying Multiple Revisions
103 diffs Diff Formats
103 diffs Diff Formats
104 templating Template Usage
104 templating Template Usage
105 urls URL Paths
105 urls URL Paths
106 extensions Using additional features
106 extensions Using additional features
107
107
108 use "hg -v help" to show aliases and global options
108 use "hg -v help" to show aliases and global options
109 add add the specified files on the next commit
109 add add the specified files on the next commit
110 addremove add all new files, delete all missing files
110 addremove add all new files, delete all missing files
111 annotate show changeset information by line for each file
111 annotate show changeset information by line for each file
112 archive create an unversioned archive of a repository revision
112 archive create an unversioned archive of a repository revision
113 backout reverse effect of earlier changeset
113 backout reverse effect of earlier changeset
114 bisect subdivision search of changesets
114 bisect subdivision search of changesets
115 branch set or show the current branch name
115 branch set or show the current branch name
116 branches list repository named branches
116 branches list repository named branches
117 bundle create a changegroup file
117 bundle create a changegroup file
118 cat output the current or given revision of files
118 cat output the current or given revision of files
119 clone make a copy of an existing repository
119 clone make a copy of an existing repository
120 commit commit the specified files or all outstanding changes
120 commit commit the specified files or all outstanding changes
121 copy mark files as copied for the next commit
121 copy mark files as copied for the next commit
122 diff diff repository (or selected files)
122 diff diff repository (or selected files)
123 export dump the header and diffs for one or more changesets
123 export dump the header and diffs for one or more changesets
124 forget forget the specified files on the next commit
124 forget forget the specified files on the next commit
125 grep search for a pattern in specified files and revisions
125 grep search for a pattern in specified files and revisions
126 heads show current repository heads or show branch heads
126 heads show current repository heads or show branch heads
127 help show help for a given topic or a help overview
127 help show help for a given topic or a help overview
128 identify identify the working copy or specified revision
128 identify identify the working copy or specified revision
129 import import an ordered set of patches
129 import import an ordered set of patches
130 incoming show new changesets found in source
130 incoming show new changesets found in source
131 init create a new repository in the given directory
131 init create a new repository in the given directory
132 locate locate files matching specific patterns
132 locate locate files matching specific patterns
133 log show revision history of entire repository or files
133 log show revision history of entire repository or files
134 manifest output the current or given revision of the project manifest
134 manifest output the current or given revision of the project manifest
135 merge merge working directory with another revision
135 merge merge working directory with another revision
136 outgoing show changesets not found in destination
136 outgoing show changesets not found in destination
137 parents show the parents of the working directory or revision
137 parents show the parents of the working directory or revision
138 paths show aliases for remote repositories
138 paths show aliases for remote repositories
139 pull pull changes from the specified source
139 pull pull changes from the specified source
140 push push changes to the specified destination
140 push push changes to the specified destination
141 recover roll back an interrupted transaction
141 recover roll back an interrupted transaction
142 remove remove the specified files on the next commit
142 remove remove the specified files on the next commit
143 rename rename files; equivalent of copy + remove
143 rename rename files; equivalent of copy + remove
144 resolve retry file merges from a merge or update
144 resolve retry file merges from a merge or update
145 revert restore individual files or directories to an earlier state
145 revert restore individual files or directories to an earlier state
146 rollback roll back the last transaction
146 rollback roll back the last transaction
147 root print the root (top) of the current working directory
147 root print the root (top) of the current working directory
148 serve export the repository via HTTP
148 serve export the repository via HTTP
149 showconfig show combined config settings from all hgrc files
149 showconfig show combined config settings from all hgrc files
150 status show changed files in the working directory
150 status show changed files in the working directory
151 summary summarize working directory state
151 summary summarize working directory state
152 tag add one or more tags for the current or given revision
152 tag add one or more tags for the current or given revision
153 tags list repository tags
153 tags list repository tags
154 tip show the tip revision
154 tip show the tip revision
155 unbundle apply one or more changegroup files
155 unbundle apply one or more changegroup files
156 update update working directory
156 update update working directory
157 verify verify the integrity of the repository
157 verify verify the integrity of the repository
158 version output version and copyright information
158 version output version and copyright information
159
159
160 additional help topics:
160 additional help topics:
161
161
162 dates Date Formats
162 dates Date Formats
163 patterns File Name Patterns
163 patterns File Name Patterns
164 environment Environment Variables
164 environment Environment Variables
165 revisions Specifying Single Revisions
165 revisions Specifying Single Revisions
166 multirevs Specifying Multiple Revisions
166 multirevs Specifying Multiple Revisions
167 diffs Diff Formats
167 diffs Diff Formats
168 templating Template Usage
168 templating Template Usage
169 urls URL Paths
169 urls URL Paths
170 extensions Using additional features
170 extensions Using additional features
171 hg add [OPTION]... [FILE]...
171 hg add [OPTION]... [FILE]...
172
172
173 add the specified files on the next commit
173 add the specified files on the next commit
174
174
175 Schedule files to be version controlled and added to the repository.
175 Schedule files to be version controlled and added to the repository.
176
176
177 The files will be added to the repository at the next commit. To undo an
177 The files will be added to the repository at the next commit. To undo an
178 add before that, see hg forget.
178 add before that, see hg forget.
179
179
180 If no names are given, add all files to the repository.
180 If no names are given, add all files to the repository.
181
181
182 options:
182 options:
183
183
184 -I --include include names matching the given patterns
184 -I --include include names matching the given patterns
185 -X --exclude exclude names matching the given patterns
185 -X --exclude exclude names matching the given patterns
186 -n --dry-run do not perform actions, just print output
186 -n --dry-run do not perform actions, just print output
187
187
188 use "hg -v help add" to show global options
188 use "hg -v help add" to show global options
189 hg add: option --skjdfks not recognized
189 hg add: option --skjdfks not recognized
190 hg add [OPTION]... [FILE]...
190 hg add [OPTION]... [FILE]...
191
191
192 add the specified files on the next commit
192 add the specified files on the next commit
193
193
194 Schedule files to be version controlled and added to the repository.
194 Schedule files to be version controlled and added to the repository.
195
195
196 The files will be added to the repository at the next commit. To undo an
196 The files will be added to the repository at the next commit. To undo an
197 add before that, see hg forget.
197 add before that, see hg forget.
198
198
199 If no names are given, add all files to the repository.
199 If no names are given, add all files to the repository.
200
200
201 options:
201 options:
202
202
203 -I --include include names matching the given patterns
203 -I --include include names matching the given patterns
204 -X --exclude exclude names matching the given patterns
204 -X --exclude exclude names matching the given patterns
205 -n --dry-run do not perform actions, just print output
205 -n --dry-run do not perform actions, just print output
206
206
207 use "hg -v help add" to show global options
207 use "hg -v help add" to show global options
208 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
208 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
209
209
210 diff repository (or selected files)
210 diff repository (or selected files)
211
211
212 Show differences between revisions for the specified files.
212 Show differences between revisions for the specified files.
213
213
214 Differences between files are shown using the unified diff format.
214 Differences between files are shown using the unified diff format.
215
215
216 NOTE: diff may generate unexpected results for merges, as it will default
216 NOTE: diff may generate unexpected results for merges, as it will default
217 to comparing against the working directory's first parent changeset if no
217 to comparing against the working directory's first parent changeset if no
218 revisions are specified.
218 revisions are specified.
219
219
220 When two revision arguments are given, then changes are shown between
220 When two revision arguments are given, then changes are shown between
221 those revisions. If only one revision is specified then that revision is
221 those revisions. If only one revision is specified then that revision is
222 compared to the working directory, and, when no revisions are specified,
222 compared to the working directory, and, when no revisions are specified,
223 the working directory files are compared to its parent.
223 the working directory files are compared to its parent.
224
224
225 Without the -a/--text option, diff will avoid generating diffs of files it
225 Without the -a/--text option, diff will avoid generating diffs of files it
226 detects as binary. With -a, diff will generate a diff anyway, probably
226 detects as binary. With -a, diff will generate a diff anyway, probably
227 with undesirable results.
227 with undesirable results.
228
228
229 Use the -g/--git option to generate diffs in the git extended diff format.
229 Use the -g/--git option to generate diffs in the git extended diff format.
230 For more information, read 'hg help diffs'.
230 For more information, read 'hg help diffs'.
231
231
232 options:
232 options:
233
233
234 -r --rev revision
234 -r --rev revision
235 -c --change change made by revision
235 -c --change change made by revision
236 -a --text treat all files as text
236 -a --text treat all files as text
237 -g --git use git extended diff format
237 -g --git use git extended diff format
238 --nodates don't include dates in diff headers
238 --nodates don't include dates in diff headers
239 -p --show-function show which function each change is in
239 -p --show-function show which function each change is in
240 --inverse produce a diff that undoes the changes
240 -w --ignore-all-space ignore white space when comparing lines
241 -w --ignore-all-space ignore white space when comparing lines
241 -b --ignore-space-change ignore changes in the amount of white space
242 -b --ignore-space-change ignore changes in the amount of white space
242 -B --ignore-blank-lines ignore changes whose lines are all blank
243 -B --ignore-blank-lines ignore changes whose lines are all blank
243 -U --unified number of lines of context to show
244 -U --unified number of lines of context to show
244 --stat output diffstat-style summary of changes
245 --stat output diffstat-style summary of changes
245 -I --include include names matching the given patterns
246 -I --include include names matching the given patterns
246 -X --exclude exclude names matching the given patterns
247 -X --exclude exclude names matching the given patterns
247
248
248 use "hg -v help diff" to show global options
249 use "hg -v help diff" to show global options
249 hg status [OPTION]... [FILE]...
250 hg status [OPTION]... [FILE]...
250
251
251 aliases: st
252 aliases: st
252
253
253 show changed files in the working directory
254 show changed files in the working directory
254
255
255 Show status of files in the repository. If names are given, only files
256 Show status of files in the repository. If names are given, only files
256 that match are shown. Files that are clean or ignored or the source of a
257 that match are shown. Files that are clean or ignored or the source of a
257 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
258 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
258 -C/--copies or -A/--all are given. Unless options described with "show
259 -C/--copies or -A/--all are given. Unless options described with "show
259 only ..." are given, the options -mardu are used.
260 only ..." are given, the options -mardu are used.
260
261
261 Option -q/--quiet hides untracked (unknown and ignored) files unless
262 Option -q/--quiet hides untracked (unknown and ignored) files unless
262 explicitly requested with -u/--unknown or -i/--ignored.
263 explicitly requested with -u/--unknown or -i/--ignored.
263
264
264 NOTE: status may appear to disagree with diff if permissions have changed
265 NOTE: status may appear to disagree with diff if permissions have changed
265 or a merge has occurred. The standard diff format does not report
266 or a merge has occurred. The standard diff format does not report
266 permission changes and diff only reports changes relative to one merge
267 permission changes and diff only reports changes relative to one merge
267 parent.
268 parent.
268
269
269 If one revision is given, it is used as the base revision. If two
270 If one revision is given, it is used as the base revision. If two
270 revisions are given, the differences between them are shown.
271 revisions are given, the differences between them are shown.
271
272
272 The codes used to show the status of files are:
273 The codes used to show the status of files are:
273
274
274 M = modified
275 M = modified
275 A = added
276 A = added
276 R = removed
277 R = removed
277 C = clean
278 C = clean
278 ! = missing (deleted by non-hg command, but still tracked)
279 ! = missing (deleted by non-hg command, but still tracked)
279 ? = not tracked
280 ? = not tracked
280 I = ignored
281 I = ignored
281 = origin of the previous file listed as A (added)
282 = origin of the previous file listed as A (added)
282
283
283 options:
284 options:
284
285
285 -A --all show status of all files
286 -A --all show status of all files
286 -m --modified show only modified files
287 -m --modified show only modified files
287 -a --added show only added files
288 -a --added show only added files
288 -r --removed show only removed files
289 -r --removed show only removed files
289 -d --deleted show only deleted (but tracked) files
290 -d --deleted show only deleted (but tracked) files
290 -c --clean show only files without changes
291 -c --clean show only files without changes
291 -u --unknown show only unknown (not tracked) files
292 -u --unknown show only unknown (not tracked) files
292 -i --ignored show only ignored files
293 -i --ignored show only ignored files
293 -n --no-status hide status prefix
294 -n --no-status hide status prefix
294 -C --copies show source of copied files
295 -C --copies show source of copied files
295 -0 --print0 end filenames with NUL, for use with xargs
296 -0 --print0 end filenames with NUL, for use with xargs
296 --rev show difference from revision
297 --rev show difference from revision
297 -I --include include names matching the given patterns
298 -I --include include names matching the given patterns
298 -X --exclude exclude names matching the given patterns
299 -X --exclude exclude names matching the given patterns
299
300
300 use "hg -v help status" to show global options
301 use "hg -v help status" to show global options
301 hg status [OPTION]... [FILE]...
302 hg status [OPTION]... [FILE]...
302
303
303 show changed files in the working directory
304 show changed files in the working directory
304 hg: unknown command 'foo'
305 hg: unknown command 'foo'
305 Mercurial Distributed SCM
306 Mercurial Distributed SCM
306
307
307 basic commands:
308 basic commands:
308
309
309 add add the specified files on the next commit
310 add add the specified files on the next commit
310 annotate show changeset information by line for each file
311 annotate show changeset information by line for each file
311 clone make a copy of an existing repository
312 clone make a copy of an existing repository
312 commit commit the specified files or all outstanding changes
313 commit commit the specified files or all outstanding changes
313 diff diff repository (or selected files)
314 diff diff repository (or selected files)
314 export dump the header and diffs for one or more changesets
315 export dump the header and diffs for one or more changesets
315 forget forget the specified files on the next commit
316 forget forget the specified files on the next commit
316 init create a new repository in the given directory
317 init create a new repository in the given directory
317 log show revision history of entire repository or files
318 log show revision history of entire repository or files
318 merge merge working directory with another revision
319 merge merge working directory with another revision
319 pull pull changes from the specified source
320 pull pull changes from the specified source
320 push push changes to the specified destination
321 push push changes to the specified destination
321 remove remove the specified files on the next commit
322 remove remove the specified files on the next commit
322 serve export the repository via HTTP
323 serve export the repository via HTTP
323 status show changed files in the working directory
324 status show changed files in the working directory
324 summary summarize working directory state
325 summary summarize working directory state
325 update update working directory
326 update update working directory
326
327
327 use "hg help" for the full list of commands or "hg -v" for details
328 use "hg help" for the full list of commands or "hg -v" for details
328 hg: unknown command 'skjdfks'
329 hg: unknown command 'skjdfks'
329 Mercurial Distributed SCM
330 Mercurial Distributed SCM
330
331
331 basic commands:
332 basic commands:
332
333
333 add add the specified files on the next commit
334 add add the specified files on the next commit
334 annotate show changeset information by line for each file
335 annotate show changeset information by line for each file
335 clone make a copy of an existing repository
336 clone make a copy of an existing repository
336 commit commit the specified files or all outstanding changes
337 commit commit the specified files or all outstanding changes
337 diff diff repository (or selected files)
338 diff diff repository (or selected files)
338 export dump the header and diffs for one or more changesets
339 export dump the header and diffs for one or more changesets
339 forget forget the specified files on the next commit
340 forget forget the specified files on the next commit
340 init create a new repository in the given directory
341 init create a new repository in the given directory
341 log show revision history of entire repository or files
342 log show revision history of entire repository or files
342 merge merge working directory with another revision
343 merge merge working directory with another revision
343 pull pull changes from the specified source
344 pull pull changes from the specified source
344 push push changes to the specified destination
345 push push changes to the specified destination
345 remove remove the specified files on the next commit
346 remove remove the specified files on the next commit
346 serve export the repository via HTTP
347 serve export the repository via HTTP
347 status show changed files in the working directory
348 status show changed files in the working directory
348 summary summarize working directory state
349 summary summarize working directory state
349 update update working directory
350 update update working directory
350
351
351 use "hg help" for the full list of commands or "hg -v" for details
352 use "hg help" for the full list of commands or "hg -v" for details
@@ -1,59 +1,62 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 echo "[extensions]" >> $HGRCPATH
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
5
5
6 echo % init
6 echo % init
7 hg init a
7 hg init a
8 cd a
8 cd a
9
9
10 echo % commit
10 echo % commit
11 echo 'base' > base
11 echo 'base' > base
12 hg ci -Ambase -d '1 0'
12 hg ci -Ambase -d '1 0'
13
13
14 echo % qnew mqbase
14 echo % qnew mqbase
15 hg qnew -mmqbase mqbase
15 hg qnew -mmqbase mqbase
16
16
17 echo % qrefresh
17 echo % qrefresh
18 echo 'patched' > base
18 echo 'patched' > base
19 hg qrefresh
19 hg qrefresh
20
20
21 echo % qdiff
21 echo % qdiff
22 hg qdiff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
22 hg qdiff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
23 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
23 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
24
24
25 echo % qdiff dirname
25 echo % qdiff dirname
26 hg qdiff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
26 hg qdiff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
27 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
27 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
28
28
29 echo % qdiff filename
29 echo % qdiff filename
30 hg qdiff --nodates base
30 hg qdiff --nodates base
31
31
32 echo % revert
32 echo % revert
33 hg revert -a
33 hg revert -a
34
34
35 echo % qpop
35 echo % qpop
36 hg qpop
36 hg qpop
37
37
38 echo % qdelete mqbase
38 echo % qdelete mqbase
39 hg qdelete mqbase
39 hg qdelete mqbase
40
40
41 echo % commit 2
41 echo % commit 2
42 printf '1\n2\n3\n4\nhello world\ngoodbye world\n7\n8\n9\n' > lines
42 printf '1\n2\n3\n4\nhello world\ngoodbye world\n7\n8\n9\n' > lines
43 hg ci -Amlines -d '2 0'
43 hg ci -Amlines -d '2 0'
44
44
45 echo % qnew 2
45 echo % qnew 2
46 hg qnew -mmqbase2 mqbase2
46 hg qnew -mmqbase2 mqbase2
47 printf '\n\n1\n2\n3\n4\nhello world\n goodbye world\n7\n8\n9\n' > lines
47 printf '\n\n1\n2\n3\n4\nhello world\n goodbye world\n7\n8\n9\n' > lines
48
48
49 echo % qdiff -U 1
49 echo % qdiff -U 1
50 hg qdiff --nodates -U 1
50 hg qdiff --nodates -U 1
51
51
52 echo % qdiff -b
52 echo % qdiff -b
53 hg qdiff --nodates -b
53 hg qdiff --nodates -b
54
54
55 echo % qdiff -U 1 -B
55 echo % qdiff -U 1 -B
56 hg qdiff --nodates -U 1 -B
56 hg qdiff --nodates -U 1 -B
57
57
58 echo qdiff -w
58 echo % qdiff -w
59 hg qdiff --nodates -w
59 hg qdiff --nodates -w
60
61 echo % qdiff --inverse
62 hg qdiff --nodates --inverse
@@ -1,88 +1,106 b''
1 % init
1 % init
2 % commit
2 % commit
3 adding base
3 adding base
4 % qnew mqbase
4 % qnew mqbase
5 % qrefresh
5 % qrefresh
6 % qdiff
6 % qdiff
7 diff -r 67e992f2c4f3 base
7 diff -r 67e992f2c4f3 base
8 --- a/base
8 --- a/base
9 +++ b/base
9 +++ b/base
10 @@ -1,1 +1,1 @@
10 @@ -1,1 +1,1 @@
11 -base
11 -base
12 +patched
12 +patched
13 % qdiff dirname
13 % qdiff dirname
14 diff -r 67e992f2c4f3 base
14 diff -r 67e992f2c4f3 base
15 --- a/base
15 --- a/base
16 +++ b/base
16 +++ b/base
17 @@ -1,1 +1,1 @@
17 @@ -1,1 +1,1 @@
18 -base
18 -base
19 +patched
19 +patched
20 % qdiff filename
20 % qdiff filename
21 diff -r 67e992f2c4f3 base
21 diff -r 67e992f2c4f3 base
22 --- a/base
22 --- a/base
23 +++ b/base
23 +++ b/base
24 @@ -1,1 +1,1 @@
24 @@ -1,1 +1,1 @@
25 -base
25 -base
26 +patched
26 +patched
27 % revert
27 % revert
28 % qpop
28 % qpop
29 popping mqbase
29 popping mqbase
30 patch queue now empty
30 patch queue now empty
31 % qdelete mqbase
31 % qdelete mqbase
32 % commit 2
32 % commit 2
33 adding lines
33 adding lines
34 % qnew 2
34 % qnew 2
35 % qdiff -U 1
35 % qdiff -U 1
36 diff -r 35fb829491c1 lines
36 diff -r 35fb829491c1 lines
37 --- a/lines
37 --- a/lines
38 +++ b/lines
38 +++ b/lines
39 @@ -1,1 +1,3 @@
39 @@ -1,1 +1,3 @@
40 +
40 +
41 +
41 +
42 1
42 1
43 @@ -4,4 +6,4 @@
43 @@ -4,4 +6,4 @@
44 4
44 4
45 -hello world
45 -hello world
46 -goodbye world
46 -goodbye world
47 +hello world
47 +hello world
48 + goodbye world
48 + goodbye world
49 7
49 7
50 % qdiff -b
50 % qdiff -b
51 diff -r 35fb829491c1 lines
51 diff -r 35fb829491c1 lines
52 --- a/lines
52 --- a/lines
53 +++ b/lines
53 +++ b/lines
54 @@ -1,9 +1,11 @@
54 @@ -1,9 +1,11 @@
55 +
55 +
56 +
56 +
57 1
57 1
58 2
58 2
59 3
59 3
60 4
60 4
61 -hello world
61 -hello world
62 -goodbye world
62 -goodbye world
63 +hello world
63 +hello world
64 + goodbye world
64 + goodbye world
65 7
65 7
66 8
66 8
67 9
67 9
68 % qdiff -U 1 -B
68 % qdiff -U 1 -B
69 diff -r 35fb829491c1 lines
69 diff -r 35fb829491c1 lines
70 --- a/lines
70 --- a/lines
71 +++ b/lines
71 +++ b/lines
72 @@ -4,4 +6,4 @@
72 @@ -4,4 +6,4 @@
73 4
73 4
74 -hello world
74 -hello world
75 -goodbye world
75 -goodbye world
76 +hello world
76 +hello world
77 + goodbye world
77 + goodbye world
78 7
78 7
79 qdiff -w
79 % qdiff -w
80 diff -r 35fb829491c1 lines
80 diff -r 35fb829491c1 lines
81 --- a/lines
81 --- a/lines
82 +++ b/lines
82 +++ b/lines
83 @@ -1,3 +1,5 @@
83 @@ -1,3 +1,5 @@
84 +
84 +
85 +
85 +
86 1
86 1
87 2
87 2
88 3
88 3
89 % qdiff --inverse
90 diff -r 35fb829491c1 lines
91 --- a/lines
92 +++ b/lines
93 @@ -1,11 +1,9 @@
94 -
95 -
96 1
97 2
98 3
99 4
100 -hello world
101 - goodbye world
102 +hello world
103 +goodbye world
104 7
105 8
106 9
General Comments 0
You need to be logged in to leave comments. Login now