##// END OF EJS Templates
Merge with stable
Matt Mackall -
r11078:37d1b201 merge default
parent child Browse files
Show More
@@ -1,2815 +1,2835 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 or any later version.
6 # GNU General Public License version 2 or any later version.
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 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behaviour can be configured with::
31 files creations or deletions. This behaviour can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40 '''
40 '''
41
41
42 from mercurial.i18n import _
42 from mercurial.i18n import _
43 from mercurial.node import bin, hex, short, nullid, nullrev
43 from mercurial.node import bin, hex, short, nullid, nullrev
44 from mercurial.lock import release
44 from mercurial.lock import release
45 from mercurial import commands, cmdutil, hg, patch, util
45 from mercurial import commands, cmdutil, hg, patch, util
46 from mercurial import repair, extensions, url, error
46 from mercurial import repair, extensions, url, error
47 import os, sys, re, errno
47 import os, sys, re, errno
48
48
49 commands.norepo += " qclone"
49 commands.norepo += " qclone"
50
50
51 # Patch names looks like unix-file names.
51 # Patch names looks like unix-file names.
52 # They must be joinable with queue directory and result in the patch path.
52 # They must be joinable with queue directory and result in the patch path.
53 normname = util.normpath
53 normname = util.normpath
54
54
55 class statusentry(object):
55 class statusentry(object):
56 def __init__(self, node, name):
56 def __init__(self, node, name):
57 self.node, self.name = node, name
57 self.node, self.name = node, name
58
58
59 def __str__(self):
59 def __str__(self):
60 return hex(self.node) + ':' + self.name
60 return hex(self.node) + ':' + self.name
61
61
62 class patchheader(object):
62 class patchheader(object):
63 def __init__(self, pf, plainmode=False):
63 def __init__(self, pf, plainmode=False):
64 def eatdiff(lines):
64 def eatdiff(lines):
65 while lines:
65 while lines:
66 l = lines[-1]
66 l = lines[-1]
67 if (l.startswith("diff -") or
67 if (l.startswith("diff -") or
68 l.startswith("Index:") or
68 l.startswith("Index:") or
69 l.startswith("===========")):
69 l.startswith("===========")):
70 del lines[-1]
70 del lines[-1]
71 else:
71 else:
72 break
72 break
73 def eatempty(lines):
73 def eatempty(lines):
74 while lines:
74 while lines:
75 if not lines[-1].strip():
75 if not lines[-1].strip():
76 del lines[-1]
76 del lines[-1]
77 else:
77 else:
78 break
78 break
79
79
80 message = []
80 message = []
81 comments = []
81 comments = []
82 user = None
82 user = None
83 date = None
83 date = None
84 parent = None
84 parent = None
85 format = None
85 format = None
86 subject = None
86 subject = None
87 diffstart = 0
87 diffstart = 0
88
88
89 for line in file(pf):
89 for line in file(pf):
90 line = line.rstrip()
90 line = line.rstrip()
91 if (line.startswith('diff --git')
91 if (line.startswith('diff --git')
92 or (diffstart and line.startswith('+++ '))):
92 or (diffstart and line.startswith('+++ '))):
93 diffstart = 2
93 diffstart = 2
94 break
94 break
95 diffstart = 0 # reset
95 diffstart = 0 # reset
96 if line.startswith("--- "):
96 if line.startswith("--- "):
97 diffstart = 1
97 diffstart = 1
98 continue
98 continue
99 elif format == "hgpatch":
99 elif format == "hgpatch":
100 # parse values when importing the result of an hg export
100 # parse values when importing the result of an hg export
101 if line.startswith("# User "):
101 if line.startswith("# User "):
102 user = line[7:]
102 user = line[7:]
103 elif line.startswith("# Date "):
103 elif line.startswith("# Date "):
104 date = line[7:]
104 date = line[7:]
105 elif line.startswith("# Parent "):
105 elif line.startswith("# Parent "):
106 parent = line[9:]
106 parent = line[9:]
107 elif not line.startswith("# ") and line:
107 elif not line.startswith("# ") and line:
108 message.append(line)
108 message.append(line)
109 format = None
109 format = None
110 elif line == '# HG changeset patch':
110 elif line == '# HG changeset patch':
111 message = []
111 message = []
112 format = "hgpatch"
112 format = "hgpatch"
113 elif (format != "tagdone" and (line.startswith("Subject: ") or
113 elif (format != "tagdone" and (line.startswith("Subject: ") or
114 line.startswith("subject: "))):
114 line.startswith("subject: "))):
115 subject = line[9:]
115 subject = line[9:]
116 format = "tag"
116 format = "tag"
117 elif (format != "tagdone" and (line.startswith("From: ") or
117 elif (format != "tagdone" and (line.startswith("From: ") or
118 line.startswith("from: "))):
118 line.startswith("from: "))):
119 user = line[6:]
119 user = line[6:]
120 format = "tag"
120 format = "tag"
121 elif (format != "tagdone" and (line.startswith("Date: ") or
121 elif (format != "tagdone" and (line.startswith("Date: ") or
122 line.startswith("date: "))):
122 line.startswith("date: "))):
123 date = line[6:]
123 date = line[6:]
124 format = "tag"
124 format = "tag"
125 elif format == "tag" and line == "":
125 elif format == "tag" and line == "":
126 # when looking for tags (subject: from: etc) they
126 # when looking for tags (subject: from: etc) they
127 # end once you find a blank line in the source
127 # end once you find a blank line in the source
128 format = "tagdone"
128 format = "tagdone"
129 elif message or line:
129 elif message or line:
130 message.append(line)
130 message.append(line)
131 comments.append(line)
131 comments.append(line)
132
132
133 eatdiff(message)
133 eatdiff(message)
134 eatdiff(comments)
134 eatdiff(comments)
135 eatempty(message)
135 eatempty(message)
136 eatempty(comments)
136 eatempty(comments)
137
137
138 # make sure message isn't empty
138 # make sure message isn't empty
139 if format and format.startswith("tag") and subject:
139 if format and format.startswith("tag") and subject:
140 message.insert(0, "")
140 message.insert(0, "")
141 message.insert(0, subject)
141 message.insert(0, subject)
142
142
143 self.message = message
143 self.message = message
144 self.comments = comments
144 self.comments = comments
145 self.user = user
145 self.user = user
146 self.date = date
146 self.date = date
147 self.parent = parent
147 self.parent = parent
148 self.haspatch = diffstart > 1
148 self.haspatch = diffstart > 1
149 self.plainmode = plainmode
149 self.plainmode = plainmode
150
150
151 def setuser(self, user):
151 def setuser(self, user):
152 if not self.updateheader(['From: ', '# User '], user):
152 if not self.updateheader(['From: ', '# User '], user):
153 try:
153 try:
154 patchheaderat = self.comments.index('# HG changeset patch')
154 patchheaderat = self.comments.index('# HG changeset patch')
155 self.comments.insert(patchheaderat + 1, '# User ' + user)
155 self.comments.insert(patchheaderat + 1, '# User ' + user)
156 except ValueError:
156 except ValueError:
157 if self.plainmode or self._hasheader(['Date: ']):
157 if self.plainmode or self._hasheader(['Date: ']):
158 self.comments = ['From: ' + user] + self.comments
158 self.comments = ['From: ' + user] + self.comments
159 else:
159 else:
160 tmp = ['# HG changeset patch', '# User ' + user, '']
160 tmp = ['# HG changeset patch', '# User ' + user, '']
161 self.comments = tmp + self.comments
161 self.comments = tmp + self.comments
162 self.user = user
162 self.user = user
163
163
164 def setdate(self, date):
164 def setdate(self, date):
165 if not self.updateheader(['Date: ', '# Date '], date):
165 if not self.updateheader(['Date: ', '# Date '], date):
166 try:
166 try:
167 patchheaderat = self.comments.index('# HG changeset patch')
167 patchheaderat = self.comments.index('# HG changeset patch')
168 self.comments.insert(patchheaderat + 1, '# Date ' + date)
168 self.comments.insert(patchheaderat + 1, '# Date ' + date)
169 except ValueError:
169 except ValueError:
170 if self.plainmode or self._hasheader(['From: ']):
170 if self.plainmode or self._hasheader(['From: ']):
171 self.comments = ['Date: ' + date] + self.comments
171 self.comments = ['Date: ' + date] + self.comments
172 else:
172 else:
173 tmp = ['# HG changeset patch', '# Date ' + date, '']
173 tmp = ['# HG changeset patch', '# Date ' + date, '']
174 self.comments = tmp + self.comments
174 self.comments = tmp + self.comments
175 self.date = date
175 self.date = date
176
176
177 def setparent(self, parent):
177 def setparent(self, parent):
178 if not self.updateheader(['# Parent '], parent):
178 if not self.updateheader(['# Parent '], parent):
179 try:
179 try:
180 patchheaderat = self.comments.index('# HG changeset patch')
180 patchheaderat = self.comments.index('# HG changeset patch')
181 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
181 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
182 except ValueError:
182 except ValueError:
183 pass
183 pass
184 self.parent = parent
184 self.parent = parent
185
185
186 def setmessage(self, message):
186 def setmessage(self, message):
187 if self.comments:
187 if self.comments:
188 self._delmsg()
188 self._delmsg()
189 self.message = [message]
189 self.message = [message]
190 self.comments += self.message
190 self.comments += self.message
191
191
192 def updateheader(self, prefixes, new):
192 def updateheader(self, prefixes, new):
193 '''Update all references to a field in the patch header.
193 '''Update all references to a field in the patch header.
194 Return whether the field is present.'''
194 Return whether the field is present.'''
195 res = False
195 res = False
196 for prefix in prefixes:
196 for prefix in prefixes:
197 for i in xrange(len(self.comments)):
197 for i in xrange(len(self.comments)):
198 if self.comments[i].startswith(prefix):
198 if self.comments[i].startswith(prefix):
199 self.comments[i] = prefix + new
199 self.comments[i] = prefix + new
200 res = True
200 res = True
201 break
201 break
202 return res
202 return res
203
203
204 def _hasheader(self, prefixes):
204 def _hasheader(self, prefixes):
205 '''Check if a header starts with any of the given prefixes.'''
205 '''Check if a header starts with any of the given prefixes.'''
206 for prefix in prefixes:
206 for prefix in prefixes:
207 for comment in self.comments:
207 for comment in self.comments:
208 if comment.startswith(prefix):
208 if comment.startswith(prefix):
209 return True
209 return True
210 return False
210 return False
211
211
212 def __str__(self):
212 def __str__(self):
213 if not self.comments:
213 if not self.comments:
214 return ''
214 return ''
215 return '\n'.join(self.comments) + '\n\n'
215 return '\n'.join(self.comments) + '\n\n'
216
216
217 def _delmsg(self):
217 def _delmsg(self):
218 '''Remove existing message, keeping the rest of the comments fields.
218 '''Remove existing message, keeping the rest of the comments fields.
219 If comments contains 'subject: ', message will prepend
219 If comments contains 'subject: ', message will prepend
220 the field and a blank line.'''
220 the field and a blank line.'''
221 if self.message:
221 if self.message:
222 subj = 'subject: ' + self.message[0].lower()
222 subj = 'subject: ' + self.message[0].lower()
223 for i in xrange(len(self.comments)):
223 for i in xrange(len(self.comments)):
224 if subj == self.comments[i].lower():
224 if subj == self.comments[i].lower():
225 del self.comments[i]
225 del self.comments[i]
226 self.message = self.message[2:]
226 self.message = self.message[2:]
227 break
227 break
228 ci = 0
228 ci = 0
229 for mi in self.message:
229 for mi in self.message:
230 while mi != self.comments[ci]:
230 while mi != self.comments[ci]:
231 ci += 1
231 ci += 1
232 del self.comments[ci]
232 del self.comments[ci]
233
233
234 class queue(object):
234 class queue(object):
235 def __init__(self, ui, path, patchdir=None):
235 def __init__(self, ui, path, patchdir=None):
236 self.basepath = path
236 self.basepath = path
237 self.path = patchdir or os.path.join(path, "patches")
237 self.path = patchdir or os.path.join(path, "patches")
238 self.opener = util.opener(self.path)
238 self.opener = util.opener(self.path)
239 self.ui = ui
239 self.ui = ui
240 self.applied_dirty = 0
240 self.applied_dirty = 0
241 self.series_dirty = 0
241 self.series_dirty = 0
242 self.series_path = "series"
242 self.series_path = "series"
243 self.status_path = "status"
243 self.status_path = "status"
244 self.guards_path = "guards"
244 self.guards_path = "guards"
245 self.active_guards = None
245 self.active_guards = None
246 self.guards_dirty = False
246 self.guards_dirty = False
247 # Handle mq.git as a bool with extended values
247 # Handle mq.git as a bool with extended values
248 try:
248 try:
249 gitmode = ui.configbool('mq', 'git', None)
249 gitmode = ui.configbool('mq', 'git', None)
250 if gitmode is None:
250 if gitmode is None:
251 raise error.ConfigError()
251 raise error.ConfigError()
252 self.gitmode = gitmode and 'yes' or 'no'
252 self.gitmode = gitmode and 'yes' or 'no'
253 except error.ConfigError:
253 except error.ConfigError:
254 self.gitmode = ui.config('mq', 'git', 'auto').lower()
254 self.gitmode = ui.config('mq', 'git', 'auto').lower()
255 self.plainmode = ui.configbool('mq', 'plain', False)
255 self.plainmode = ui.configbool('mq', 'plain', False)
256
256
257 @util.propertycache
257 @util.propertycache
258 def applied(self):
258 def applied(self):
259 if os.path.exists(self.join(self.status_path)):
259 if os.path.exists(self.join(self.status_path)):
260 def parse(l):
260 def parse(l):
261 n, name = l.split(':', 1)
261 n, name = l.split(':', 1)
262 return statusentry(bin(n), name)
262 return statusentry(bin(n), name)
263 lines = self.opener(self.status_path).read().splitlines()
263 lines = self.opener(self.status_path).read().splitlines()
264 return [parse(l) for l in lines]
264 return [parse(l) for l in lines]
265 return []
265 return []
266
266
267 @util.propertycache
267 @util.propertycache
268 def full_series(self):
268 def full_series(self):
269 if os.path.exists(self.join(self.series_path)):
269 if os.path.exists(self.join(self.series_path)):
270 return self.opener(self.series_path).read().splitlines()
270 return self.opener(self.series_path).read().splitlines()
271 return []
271 return []
272
272
273 @util.propertycache
273 @util.propertycache
274 def series(self):
274 def series(self):
275 self.parse_series()
275 self.parse_series()
276 return self.series
276 return self.series
277
277
278 @util.propertycache
278 @util.propertycache
279 def series_guards(self):
279 def series_guards(self):
280 self.parse_series()
280 self.parse_series()
281 return self.series_guards
281 return self.series_guards
282
282
283 def invalidate(self):
283 def invalidate(self):
284 for a in 'applied full_series series series_guards'.split():
284 for a in 'applied full_series series series_guards'.split():
285 if a in self.__dict__:
285 if a in self.__dict__:
286 delattr(self, a)
286 delattr(self, a)
287 self.applied_dirty = 0
287 self.applied_dirty = 0
288 self.series_dirty = 0
288 self.series_dirty = 0
289 self.guards_dirty = False
289 self.guards_dirty = False
290 self.active_guards = None
290 self.active_guards = None
291
291
292 def diffopts(self, opts={}, patchfn=None):
292 def diffopts(self, opts={}, patchfn=None):
293 diffopts = patch.diffopts(self.ui, opts)
293 diffopts = patch.diffopts(self.ui, opts)
294 if self.gitmode == 'auto':
294 if self.gitmode == 'auto':
295 diffopts.upgrade = True
295 diffopts.upgrade = True
296 elif self.gitmode == 'keep':
296 elif self.gitmode == 'keep':
297 pass
297 pass
298 elif self.gitmode in ('yes', 'no'):
298 elif self.gitmode in ('yes', 'no'):
299 diffopts.git = self.gitmode == 'yes'
299 diffopts.git = self.gitmode == 'yes'
300 else:
300 else:
301 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
301 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
302 ' got %s') % self.gitmode)
302 ' got %s') % self.gitmode)
303 if patchfn:
303 if patchfn:
304 diffopts = self.patchopts(diffopts, patchfn)
304 diffopts = self.patchopts(diffopts, patchfn)
305 return diffopts
305 return diffopts
306
306
307 def patchopts(self, diffopts, *patches):
307 def patchopts(self, diffopts, *patches):
308 """Return a copy of input diff options with git set to true if
308 """Return a copy of input diff options with git set to true if
309 referenced patch is a git patch and should be preserved as such.
309 referenced patch is a git patch and should be preserved as such.
310 """
310 """
311 diffopts = diffopts.copy()
311 diffopts = diffopts.copy()
312 if not diffopts.git and self.gitmode == 'keep':
312 if not diffopts.git and self.gitmode == 'keep':
313 for patchfn in patches:
313 for patchfn in patches:
314 patchf = self.opener(patchfn, 'r')
314 patchf = self.opener(patchfn, 'r')
315 # if the patch was a git patch, refresh it as a git patch
315 # if the patch was a git patch, refresh it as a git patch
316 for line in patchf:
316 for line in patchf:
317 if line.startswith('diff --git'):
317 if line.startswith('diff --git'):
318 diffopts.git = True
318 diffopts.git = True
319 break
319 break
320 patchf.close()
320 patchf.close()
321 return diffopts
321 return diffopts
322
322
323 def join(self, *p):
323 def join(self, *p):
324 return os.path.join(self.path, *p)
324 return os.path.join(self.path, *p)
325
325
326 def find_series(self, patch):
326 def find_series(self, patch):
327 def matchpatch(l):
327 def matchpatch(l):
328 l = l.split('#', 1)[0]
328 l = l.split('#', 1)[0]
329 return l.strip() == patch
329 return l.strip() == patch
330 for index, l in enumerate(self.full_series):
330 for index, l in enumerate(self.full_series):
331 if matchpatch(l):
331 if matchpatch(l):
332 return index
332 return index
333 return None
333 return None
334
334
335 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
335 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
336
336
337 def parse_series(self):
337 def parse_series(self):
338 self.series = []
338 self.series = []
339 self.series_guards = []
339 self.series_guards = []
340 for l in self.full_series:
340 for l in self.full_series:
341 h = l.find('#')
341 h = l.find('#')
342 if h == -1:
342 if h == -1:
343 patch = l
343 patch = l
344 comment = ''
344 comment = ''
345 elif h == 0:
345 elif h == 0:
346 continue
346 continue
347 else:
347 else:
348 patch = l[:h]
348 patch = l[:h]
349 comment = l[h:]
349 comment = l[h:]
350 patch = patch.strip()
350 patch = patch.strip()
351 if patch:
351 if patch:
352 if patch in self.series:
352 if patch in self.series:
353 raise util.Abort(_('%s appears more than once in %s') %
353 raise util.Abort(_('%s appears more than once in %s') %
354 (patch, self.join(self.series_path)))
354 (patch, self.join(self.series_path)))
355 self.series.append(patch)
355 self.series.append(patch)
356 self.series_guards.append(self.guard_re.findall(comment))
356 self.series_guards.append(self.guard_re.findall(comment))
357
357
358 def check_guard(self, guard):
358 def check_guard(self, guard):
359 if not guard:
359 if not guard:
360 return _('guard cannot be an empty string')
360 return _('guard cannot be an empty string')
361 bad_chars = '# \t\r\n\f'
361 bad_chars = '# \t\r\n\f'
362 first = guard[0]
362 first = guard[0]
363 if first in '-+':
363 if first in '-+':
364 return (_('guard %r starts with invalid character: %r') %
364 return (_('guard %r starts with invalid character: %r') %
365 (guard, first))
365 (guard, first))
366 for c in bad_chars:
366 for c in bad_chars:
367 if c in guard:
367 if c in guard:
368 return _('invalid character in guard %r: %r') % (guard, c)
368 return _('invalid character in guard %r: %r') % (guard, c)
369
369
370 def set_active(self, guards):
370 def set_active(self, guards):
371 for guard in guards:
371 for guard in guards:
372 bad = self.check_guard(guard)
372 bad = self.check_guard(guard)
373 if bad:
373 if bad:
374 raise util.Abort(bad)
374 raise util.Abort(bad)
375 guards = sorted(set(guards))
375 guards = sorted(set(guards))
376 self.ui.debug('active guards: %s\n' % ' '.join(guards))
376 self.ui.debug('active guards: %s\n' % ' '.join(guards))
377 self.active_guards = guards
377 self.active_guards = guards
378 self.guards_dirty = True
378 self.guards_dirty = True
379
379
380 def active(self):
380 def active(self):
381 if self.active_guards is None:
381 if self.active_guards is None:
382 self.active_guards = []
382 self.active_guards = []
383 try:
383 try:
384 guards = self.opener(self.guards_path).read().split()
384 guards = self.opener(self.guards_path).read().split()
385 except IOError, err:
385 except IOError, err:
386 if err.errno != errno.ENOENT:
386 if err.errno != errno.ENOENT:
387 raise
387 raise
388 guards = []
388 guards = []
389 for i, guard in enumerate(guards):
389 for i, guard in enumerate(guards):
390 bad = self.check_guard(guard)
390 bad = self.check_guard(guard)
391 if bad:
391 if bad:
392 self.ui.warn('%s:%d: %s\n' %
392 self.ui.warn('%s:%d: %s\n' %
393 (self.join(self.guards_path), i + 1, bad))
393 (self.join(self.guards_path), i + 1, bad))
394 else:
394 else:
395 self.active_guards.append(guard)
395 self.active_guards.append(guard)
396 return self.active_guards
396 return self.active_guards
397
397
398 def set_guards(self, idx, guards):
398 def set_guards(self, idx, guards):
399 for g in guards:
399 for g in guards:
400 if len(g) < 2:
400 if len(g) < 2:
401 raise util.Abort(_('guard %r too short') % g)
401 raise util.Abort(_('guard %r too short') % g)
402 if g[0] not in '-+':
402 if g[0] not in '-+':
403 raise util.Abort(_('guard %r starts with invalid char') % g)
403 raise util.Abort(_('guard %r starts with invalid char') % g)
404 bad = self.check_guard(g[1:])
404 bad = self.check_guard(g[1:])
405 if bad:
405 if bad:
406 raise util.Abort(bad)
406 raise util.Abort(bad)
407 drop = self.guard_re.sub('', self.full_series[idx])
407 drop = self.guard_re.sub('', self.full_series[idx])
408 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
408 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
409 self.parse_series()
409 self.parse_series()
410 self.series_dirty = True
410 self.series_dirty = True
411
411
412 def pushable(self, idx):
412 def pushable(self, idx):
413 if isinstance(idx, str):
413 if isinstance(idx, str):
414 idx = self.series.index(idx)
414 idx = self.series.index(idx)
415 patchguards = self.series_guards[idx]
415 patchguards = self.series_guards[idx]
416 if not patchguards:
416 if not patchguards:
417 return True, None
417 return True, None
418 guards = self.active()
418 guards = self.active()
419 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
419 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
420 if exactneg:
420 if exactneg:
421 return False, exactneg[0]
421 return False, exactneg[0]
422 pos = [g for g in patchguards if g[0] == '+']
422 pos = [g for g in patchguards if g[0] == '+']
423 exactpos = [g for g in pos if g[1:] in guards]
423 exactpos = [g for g in pos if g[1:] in guards]
424 if pos:
424 if pos:
425 if exactpos:
425 if exactpos:
426 return True, exactpos[0]
426 return True, exactpos[0]
427 return False, pos
427 return False, pos
428 return True, ''
428 return True, ''
429
429
430 def explain_pushable(self, idx, all_patches=False):
430 def explain_pushable(self, idx, all_patches=False):
431 write = all_patches and self.ui.write or self.ui.warn
431 write = all_patches and self.ui.write or self.ui.warn
432 if all_patches or self.ui.verbose:
432 if all_patches or self.ui.verbose:
433 if isinstance(idx, str):
433 if isinstance(idx, str):
434 idx = self.series.index(idx)
434 idx = self.series.index(idx)
435 pushable, why = self.pushable(idx)
435 pushable, why = self.pushable(idx)
436 if all_patches and pushable:
436 if all_patches and pushable:
437 if why is None:
437 if why is None:
438 write(_('allowing %s - no guards in effect\n') %
438 write(_('allowing %s - no guards in effect\n') %
439 self.series[idx])
439 self.series[idx])
440 else:
440 else:
441 if not why:
441 if not why:
442 write(_('allowing %s - no matching negative guards\n') %
442 write(_('allowing %s - no matching negative guards\n') %
443 self.series[idx])
443 self.series[idx])
444 else:
444 else:
445 write(_('allowing %s - guarded by %r\n') %
445 write(_('allowing %s - guarded by %r\n') %
446 (self.series[idx], why))
446 (self.series[idx], why))
447 if not pushable:
447 if not pushable:
448 if why:
448 if why:
449 write(_('skipping %s - guarded by %r\n') %
449 write(_('skipping %s - guarded by %r\n') %
450 (self.series[idx], why))
450 (self.series[idx], why))
451 else:
451 else:
452 write(_('skipping %s - no matching guards\n') %
452 write(_('skipping %s - no matching guards\n') %
453 self.series[idx])
453 self.series[idx])
454
454
455 def save_dirty(self):
455 def save_dirty(self):
456 def write_list(items, path):
456 def write_list(items, path):
457 fp = self.opener(path, 'w')
457 fp = self.opener(path, 'w')
458 for i in items:
458 for i in items:
459 fp.write("%s\n" % i)
459 fp.write("%s\n" % i)
460 fp.close()
460 fp.close()
461 if self.applied_dirty:
461 if self.applied_dirty:
462 write_list(map(str, self.applied), self.status_path)
462 write_list(map(str, self.applied), self.status_path)
463 if self.series_dirty:
463 if self.series_dirty:
464 write_list(self.full_series, self.series_path)
464 write_list(self.full_series, self.series_path)
465 if self.guards_dirty:
465 if self.guards_dirty:
466 write_list(self.active_guards, self.guards_path)
466 write_list(self.active_guards, self.guards_path)
467
467
468 def removeundo(self, repo):
468 def removeundo(self, repo):
469 undo = repo.sjoin('undo')
469 undo = repo.sjoin('undo')
470 if not os.path.exists(undo):
470 if not os.path.exists(undo):
471 return
471 return
472 try:
472 try:
473 os.unlink(undo)
473 os.unlink(undo)
474 except OSError, inst:
474 except OSError, inst:
475 self.ui.warn(_('error removing undo: %s\n') % str(inst))
475 self.ui.warn(_('error removing undo: %s\n') % str(inst))
476
476
477 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
477 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
478 fp=None, changes=None, opts={}):
478 fp=None, changes=None, opts={}):
479 stat = opts.get('stat')
479 stat = opts.get('stat')
480 m = cmdutil.match(repo, files, opts)
480 m = cmdutil.match(repo, files, opts)
481 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
481 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
482 changes, stat, fp)
482 changes, stat, fp)
483
483
484 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
484 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
485 # first try just applying the patch
485 # first try just applying the patch
486 (err, n) = self.apply(repo, [patch], update_status=False,
486 (err, n) = self.apply(repo, [patch], update_status=False,
487 strict=True, merge=rev)
487 strict=True, merge=rev)
488
488
489 if err == 0:
489 if err == 0:
490 return (err, n)
490 return (err, n)
491
491
492 if n is None:
492 if n is None:
493 raise util.Abort(_("apply failed for patch %s") % patch)
493 raise util.Abort(_("apply failed for patch %s") % patch)
494
494
495 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
495 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
496
496
497 # apply failed, strip away that rev and merge.
497 # apply failed, strip away that rev and merge.
498 hg.clean(repo, head)
498 hg.clean(repo, head)
499 self.strip(repo, n, update=False, backup='strip')
499 self.strip(repo, n, update=False, backup='strip')
500
500
501 ctx = repo[rev]
501 ctx = repo[rev]
502 ret = hg.merge(repo, rev)
502 ret = hg.merge(repo, rev)
503 if ret:
503 if ret:
504 raise util.Abort(_("update returned %d") % ret)
504 raise util.Abort(_("update returned %d") % ret)
505 n = repo.commit(ctx.description(), ctx.user(), force=True)
505 n = repo.commit(ctx.description(), ctx.user(), force=True)
506 if n is None:
506 if n is None:
507 raise util.Abort(_("repo commit failed"))
507 raise util.Abort(_("repo commit failed"))
508 try:
508 try:
509 ph = patchheader(mergeq.join(patch), self.plainmode)
509 ph = patchheader(mergeq.join(patch), self.plainmode)
510 except:
510 except:
511 raise util.Abort(_("unable to read %s") % patch)
511 raise util.Abort(_("unable to read %s") % patch)
512
512
513 diffopts = self.patchopts(diffopts, patch)
513 diffopts = self.patchopts(diffopts, patch)
514 patchf = self.opener(patch, "w")
514 patchf = self.opener(patch, "w")
515 comments = str(ph)
515 comments = str(ph)
516 if comments:
516 if comments:
517 patchf.write(comments)
517 patchf.write(comments)
518 self.printdiff(repo, diffopts, head, n, fp=patchf)
518 self.printdiff(repo, diffopts, head, n, fp=patchf)
519 patchf.close()
519 patchf.close()
520 self.removeundo(repo)
520 self.removeundo(repo)
521 return (0, n)
521 return (0, n)
522
522
523 def qparents(self, repo, rev=None):
523 def qparents(self, repo, rev=None):
524 if rev is None:
524 if rev is None:
525 (p1, p2) = repo.dirstate.parents()
525 (p1, p2) = repo.dirstate.parents()
526 if p2 == nullid:
526 if p2 == nullid:
527 return p1
527 return p1
528 if not self.applied:
528 if not self.applied:
529 return None
529 return None
530 return self.applied[-1].node
530 return self.applied[-1].node
531 p1, p2 = repo.changelog.parents(rev)
531 p1, p2 = repo.changelog.parents(rev)
532 if p2 != nullid and p2 in [x.node for x in self.applied]:
532 if p2 != nullid and p2 in [x.node for x in self.applied]:
533 return p2
533 return p2
534 return p1
534 return p1
535
535
536 def mergepatch(self, repo, mergeq, series, diffopts):
536 def mergepatch(self, repo, mergeq, series, diffopts):
537 if not self.applied:
537 if not self.applied:
538 # each of the patches merged in will have two parents. This
538 # each of the patches merged in will have two parents. This
539 # can confuse the qrefresh, qdiff, and strip code because it
539 # can confuse the qrefresh, qdiff, and strip code because it
540 # needs to know which parent is actually in the patch queue.
540 # needs to know which parent is actually in the patch queue.
541 # so, we insert a merge marker with only one parent. This way
541 # so, we insert a merge marker with only one parent. This way
542 # the first patch in the queue is never a merge patch
542 # the first patch in the queue is never a merge patch
543 #
543 #
544 pname = ".hg.patches.merge.marker"
544 pname = ".hg.patches.merge.marker"
545 n = repo.commit('[mq]: merge marker', force=True)
545 n = repo.commit('[mq]: merge marker', force=True)
546 self.removeundo(repo)
546 self.removeundo(repo)
547 self.applied.append(statusentry(n, pname))
547 self.applied.append(statusentry(n, pname))
548 self.applied_dirty = 1
548 self.applied_dirty = 1
549
549
550 head = self.qparents(repo)
550 head = self.qparents(repo)
551
551
552 for patch in series:
552 for patch in series:
553 patch = mergeq.lookup(patch, strict=True)
553 patch = mergeq.lookup(patch, strict=True)
554 if not patch:
554 if not patch:
555 self.ui.warn(_("patch %s does not exist\n") % patch)
555 self.ui.warn(_("patch %s does not exist\n") % patch)
556 return (1, None)
556 return (1, None)
557 pushable, reason = self.pushable(patch)
557 pushable, reason = self.pushable(patch)
558 if not pushable:
558 if not pushable:
559 self.explain_pushable(patch, all_patches=True)
559 self.explain_pushable(patch, all_patches=True)
560 continue
560 continue
561 info = mergeq.isapplied(patch)
561 info = mergeq.isapplied(patch)
562 if not info:
562 if not info:
563 self.ui.warn(_("patch %s is not applied\n") % patch)
563 self.ui.warn(_("patch %s is not applied\n") % patch)
564 return (1, None)
564 return (1, None)
565 rev = info[1]
565 rev = info[1]
566 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
566 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
567 if head:
567 if head:
568 self.applied.append(statusentry(head, patch))
568 self.applied.append(statusentry(head, patch))
569 self.applied_dirty = 1
569 self.applied_dirty = 1
570 if err:
570 if err:
571 return (err, head)
571 return (err, head)
572 self.save_dirty()
572 self.save_dirty()
573 return (0, head)
573 return (0, head)
574
574
575 def patch(self, repo, patchfile):
575 def patch(self, repo, patchfile):
576 '''Apply patchfile to the working directory.
576 '''Apply patchfile to the working directory.
577 patchfile: name of patch file'''
577 patchfile: name of patch file'''
578 files = {}
578 files = {}
579 try:
579 try:
580 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
580 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
581 files=files, eolmode=None)
581 files=files, eolmode=None)
582 except Exception, inst:
582 except Exception, inst:
583 self.ui.note(str(inst) + '\n')
583 self.ui.note(str(inst) + '\n')
584 if not self.ui.verbose:
584 if not self.ui.verbose:
585 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
585 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
586 return (False, files, False)
586 return (False, files, False)
587
587
588 return (True, files, fuzz)
588 return (True, files, fuzz)
589
589
590 def apply(self, repo, series, list=False, update_status=True,
590 def apply(self, repo, series, list=False, update_status=True,
591 strict=False, patchdir=None, merge=None, all_files=None):
591 strict=False, patchdir=None, merge=None, all_files=None):
592 wlock = lock = tr = None
592 wlock = lock = tr = None
593 try:
593 try:
594 wlock = repo.wlock()
594 wlock = repo.wlock()
595 lock = repo.lock()
595 lock = repo.lock()
596 tr = repo.transaction("qpush")
596 tr = repo.transaction("qpush")
597 try:
597 try:
598 ret = self._apply(repo, series, list, update_status,
598 ret = self._apply(repo, series, list, update_status,
599 strict, patchdir, merge, all_files=all_files)
599 strict, patchdir, merge, all_files=all_files)
600 tr.close()
600 tr.close()
601 self.save_dirty()
601 self.save_dirty()
602 return ret
602 return ret
603 except:
603 except:
604 try:
604 try:
605 tr.abort()
605 tr.abort()
606 finally:
606 finally:
607 repo.invalidate()
607 repo.invalidate()
608 repo.dirstate.invalidate()
608 repo.dirstate.invalidate()
609 raise
609 raise
610 finally:
610 finally:
611 del tr
611 del tr
612 release(lock, wlock)
612 release(lock, wlock)
613 self.removeundo(repo)
613 self.removeundo(repo)
614
614
615 def _apply(self, repo, series, list=False, update_status=True,
615 def _apply(self, repo, series, list=False, update_status=True,
616 strict=False, patchdir=None, merge=None, all_files=None):
616 strict=False, patchdir=None, merge=None, all_files=None):
617 '''returns (error, hash)
617 '''returns (error, hash)
618 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
618 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
619 # TODO unify with commands.py
619 # TODO unify with commands.py
620 if not patchdir:
620 if not patchdir:
621 patchdir = self.path
621 patchdir = self.path
622 err = 0
622 err = 0
623 n = None
623 n = None
624 for patchname in series:
624 for patchname in series:
625 pushable, reason = self.pushable(patchname)
625 pushable, reason = self.pushable(patchname)
626 if not pushable:
626 if not pushable:
627 self.explain_pushable(patchname, all_patches=True)
627 self.explain_pushable(patchname, all_patches=True)
628 continue
628 continue
629 self.ui.status(_("applying %s\n") % patchname)
629 self.ui.status(_("applying %s\n") % patchname)
630 pf = os.path.join(patchdir, patchname)
630 pf = os.path.join(patchdir, patchname)
631
631
632 try:
632 try:
633 ph = patchheader(self.join(patchname), self.plainmode)
633 ph = patchheader(self.join(patchname), self.plainmode)
634 except:
634 except:
635 self.ui.warn(_("unable to read %s\n") % patchname)
635 self.ui.warn(_("unable to read %s\n") % patchname)
636 err = 1
636 err = 1
637 break
637 break
638
638
639 message = ph.message
639 message = ph.message
640 if not message:
640 if not message:
641 message = "imported patch %s\n" % patchname
641 message = "imported patch %s\n" % patchname
642 else:
642 else:
643 if list:
643 if list:
644 message.append("\nimported patch %s" % patchname)
644 message.append("\nimported patch %s" % patchname)
645 message = '\n'.join(message)
645 message = '\n'.join(message)
646
646
647 if ph.haspatch:
647 if ph.haspatch:
648 (patcherr, files, fuzz) = self.patch(repo, pf)
648 (patcherr, files, fuzz) = self.patch(repo, pf)
649 if all_files is not None:
649 if all_files is not None:
650 all_files.update(files)
650 all_files.update(files)
651 patcherr = not patcherr
651 patcherr = not patcherr
652 else:
652 else:
653 self.ui.warn(_("patch %s is empty\n") % patchname)
653 self.ui.warn(_("patch %s is empty\n") % patchname)
654 patcherr, files, fuzz = 0, [], 0
654 patcherr, files, fuzz = 0, [], 0
655
655
656 if merge and files:
656 if merge and files:
657 # Mark as removed/merged and update dirstate parent info
657 # Mark as removed/merged and update dirstate parent info
658 removed = []
658 removed = []
659 merged = []
659 merged = []
660 for f in files:
660 for f in files:
661 if os.path.exists(repo.wjoin(f)):
661 if os.path.exists(repo.wjoin(f)):
662 merged.append(f)
662 merged.append(f)
663 else:
663 else:
664 removed.append(f)
664 removed.append(f)
665 for f in removed:
665 for f in removed:
666 repo.dirstate.remove(f)
666 repo.dirstate.remove(f)
667 for f in merged:
667 for f in merged:
668 repo.dirstate.merge(f)
668 repo.dirstate.merge(f)
669 p1, p2 = repo.dirstate.parents()
669 p1, p2 = repo.dirstate.parents()
670 repo.dirstate.setparents(p1, merge)
670 repo.dirstate.setparents(p1, merge)
671
671
672 files = patch.updatedir(self.ui, repo, files)
672 files = patch.updatedir(self.ui, repo, files)
673 match = cmdutil.matchfiles(repo, files or [])
673 match = cmdutil.matchfiles(repo, files or [])
674 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
674 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
675
675
676 if n is None:
676 if n is None:
677 raise util.Abort(_("repo commit failed"))
677 raise util.Abort(_("repo commit failed"))
678
678
679 if update_status:
679 if update_status:
680 self.applied.append(statusentry(n, patchname))
680 self.applied.append(statusentry(n, patchname))
681
681
682 if patcherr:
682 if patcherr:
683 self.ui.warn(_("patch failed, rejects left in working dir\n"))
683 self.ui.warn(_("patch failed, rejects left in working dir\n"))
684 err = 2
684 err = 2
685 break
685 break
686
686
687 if fuzz and strict:
687 if fuzz and strict:
688 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
688 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
689 err = 3
689 err = 3
690 break
690 break
691 return (err, n)
691 return (err, n)
692
692
693 def _cleanup(self, patches, numrevs, keep=False):
693 def _cleanup(self, patches, numrevs, keep=False):
694 if not keep:
694 if not keep:
695 r = self.qrepo()
695 r = self.qrepo()
696 if r:
696 if r:
697 r.remove(patches, True)
697 r.remove(patches, True)
698 else:
698 else:
699 for p in patches:
699 for p in patches:
700 os.unlink(self.join(p))
700 os.unlink(self.join(p))
701
701
702 if numrevs:
702 if numrevs:
703 del self.applied[:numrevs]
703 del self.applied[:numrevs]
704 self.applied_dirty = 1
704 self.applied_dirty = 1
705
705
706 for i in sorted([self.find_series(p) for p in patches], reverse=True):
706 for i in sorted([self.find_series(p) for p in patches], reverse=True):
707 del self.full_series[i]
707 del self.full_series[i]
708 self.parse_series()
708 self.parse_series()
709 self.series_dirty = 1
709 self.series_dirty = 1
710
710
711 def _revpatches(self, repo, revs):
711 def _revpatches(self, repo, revs):
712 firstrev = repo[self.applied[0].node].rev()
712 firstrev = repo[self.applied[0].node].rev()
713 patches = []
713 patches = []
714 for i, rev in enumerate(revs):
714 for i, rev in enumerate(revs):
715
715
716 if rev < firstrev:
716 if rev < firstrev:
717 raise util.Abort(_('revision %d is not managed') % rev)
717 raise util.Abort(_('revision %d is not managed') % rev)
718
718
719 ctx = repo[rev]
719 ctx = repo[rev]
720 base = self.applied[i].node
720 base = self.applied[i].node
721 if ctx.node() != base:
721 if ctx.node() != base:
722 msg = _('cannot delete revision %d above applied patches')
722 msg = _('cannot delete revision %d above applied patches')
723 raise util.Abort(msg % rev)
723 raise util.Abort(msg % rev)
724
724
725 patch = self.applied[i].name
725 patch = self.applied[i].name
726 for fmt in ('[mq]: %s', 'imported patch %s'):
726 for fmt in ('[mq]: %s', 'imported patch %s'):
727 if ctx.description() == fmt % patch:
727 if ctx.description() == fmt % patch:
728 msg = _('patch %s finalized without changeset message\n')
728 msg = _('patch %s finalized without changeset message\n')
729 repo.ui.status(msg % patch)
729 repo.ui.status(msg % patch)
730 break
730 break
731
731
732 patches.append(patch)
732 patches.append(patch)
733 return patches
733 return patches
734
734
735 def finish(self, repo, revs):
735 def finish(self, repo, revs):
736 patches = self._revpatches(repo, sorted(revs))
736 patches = self._revpatches(repo, sorted(revs))
737 self._cleanup(patches, len(patches))
737 self._cleanup(patches, len(patches))
738
738
739 def delete(self, repo, patches, opts):
739 def delete(self, repo, patches, opts):
740 if not patches and not opts.get('rev'):
740 if not patches and not opts.get('rev'):
741 raise util.Abort(_('qdelete requires at least one revision or '
741 raise util.Abort(_('qdelete requires at least one revision or '
742 'patch name'))
742 'patch name'))
743
743
744 realpatches = []
744 realpatches = []
745 for patch in patches:
745 for patch in patches:
746 patch = self.lookup(patch, strict=True)
746 patch = self.lookup(patch, strict=True)
747 info = self.isapplied(patch)
747 info = self.isapplied(patch)
748 if info:
748 if info:
749 raise util.Abort(_("cannot delete applied patch %s") % patch)
749 raise util.Abort(_("cannot delete applied patch %s") % patch)
750 if patch not in self.series:
750 if patch not in self.series:
751 raise util.Abort(_("patch %s not in series file") % patch)
751 raise util.Abort(_("patch %s not in series file") % patch)
752 realpatches.append(patch)
752 realpatches.append(patch)
753
753
754 numrevs = 0
754 numrevs = 0
755 if opts.get('rev'):
755 if opts.get('rev'):
756 if not self.applied:
756 if not self.applied:
757 raise util.Abort(_('no patches applied'))
757 raise util.Abort(_('no patches applied'))
758 revs = cmdutil.revrange(repo, opts['rev'])
758 revs = cmdutil.revrange(repo, opts['rev'])
759 if len(revs) > 1 and revs[0] > revs[1]:
759 if len(revs) > 1 and revs[0] > revs[1]:
760 revs.reverse()
760 revs.reverse()
761 revpatches = self._revpatches(repo, revs)
761 revpatches = self._revpatches(repo, revs)
762 realpatches += revpatches
762 realpatches += revpatches
763 numrevs = len(revpatches)
763 numrevs = len(revpatches)
764
764
765 self._cleanup(realpatches, numrevs, opts.get('keep'))
765 self._cleanup(realpatches, numrevs, opts.get('keep'))
766
766
767 def check_toppatch(self, repo):
767 def check_toppatch(self, repo):
768 if self.applied:
768 if self.applied:
769 top = self.applied[-1].node
769 top = self.applied[-1].node
770 patch = self.applied[-1].name
770 patch = self.applied[-1].name
771 pp = repo.dirstate.parents()
771 pp = repo.dirstate.parents()
772 if top not in pp:
772 if top not in pp:
773 raise util.Abort(_("working directory revision is not qtip"))
773 raise util.Abort(_("working directory revision is not qtip"))
774 return top, patch
774 return top, patch
775 return None, None
775 return None, None
776
776
777 def check_localchanges(self, repo, force=False, refresh=True):
777 def check_localchanges(self, repo, force=False, refresh=True):
778 m, a, r, d = repo.status()[:4]
778 m, a, r, d = repo.status()[:4]
779 if (m or a or r or d) and not force:
779 if (m or a or r or d) and not force:
780 if refresh:
780 if refresh:
781 raise util.Abort(_("local changes found, refresh first"))
781 raise util.Abort(_("local changes found, refresh first"))
782 else:
782 else:
783 raise util.Abort(_("local changes found"))
783 raise util.Abort(_("local changes found"))
784 return m, a, r, d
784 return m, a, r, d
785
785
786 _reserved = ('series', 'status', 'guards')
786 _reserved = ('series', 'status', 'guards')
787 def check_reserved_name(self, name):
787 def check_reserved_name(self, name):
788 if (name in self._reserved or name.startswith('.hg')
788 if (name in self._reserved or name.startswith('.hg')
789 or name.startswith('.mq') or '#' in name or ':' in name):
789 or name.startswith('.mq') or '#' in name or ':' in name):
790 raise util.Abort(_('"%s" cannot be used as the name of a patch')
790 raise util.Abort(_('"%s" cannot be used as the name of a patch')
791 % name)
791 % name)
792
792
793 def new(self, repo, patchfn, *pats, **opts):
793 def new(self, repo, patchfn, *pats, **opts):
794 """options:
794 """options:
795 msg: a string or a no-argument function returning a string
795 msg: a string or a no-argument function returning a string
796 """
796 """
797 msg = opts.get('msg')
797 msg = opts.get('msg')
798 user = opts.get('user')
798 user = opts.get('user')
799 date = opts.get('date')
799 date = opts.get('date')
800 if date:
800 if date:
801 date = util.parsedate(date)
801 date = util.parsedate(date)
802 diffopts = self.diffopts({'git': opts.get('git')})
802 diffopts = self.diffopts({'git': opts.get('git')})
803 self.check_reserved_name(patchfn)
803 self.check_reserved_name(patchfn)
804 if os.path.exists(self.join(patchfn)):
804 if os.path.exists(self.join(patchfn)):
805 raise util.Abort(_('patch "%s" already exists') % patchfn)
805 raise util.Abort(_('patch "%s" already exists') % patchfn)
806 if opts.get('include') or opts.get('exclude') or pats:
806 if opts.get('include') or opts.get('exclude') or pats:
807 match = cmdutil.match(repo, pats, opts)
807 match = cmdutil.match(repo, pats, opts)
808 # detect missing files in pats
808 # detect missing files in pats
809 def badfn(f, msg):
809 def badfn(f, msg):
810 raise util.Abort('%s: %s' % (f, msg))
810 raise util.Abort('%s: %s' % (f, msg))
811 match.bad = badfn
811 match.bad = badfn
812 m, a, r, d = repo.status(match=match)[:4]
812 m, a, r, d = repo.status(match=match)[:4]
813 else:
813 else:
814 m, a, r, d = self.check_localchanges(repo, force=True)
814 m, a, r, d = self.check_localchanges(repo, force=True)
815 match = cmdutil.matchfiles(repo, m + a + r)
815 match = cmdutil.matchfiles(repo, m + a + r)
816 if len(repo[None].parents()) > 1:
816 if len(repo[None].parents()) > 1:
817 raise util.Abort(_('cannot manage merge changesets'))
817 raise util.Abort(_('cannot manage merge changesets'))
818 commitfiles = m + a + r
818 commitfiles = m + a + r
819 self.check_toppatch(repo)
819 self.check_toppatch(repo)
820 insert = self.full_series_end()
820 insert = self.full_series_end()
821 wlock = repo.wlock()
821 wlock = repo.wlock()
822 try:
822 try:
823 # if patch file write fails, abort early
823 # if patch file write fails, abort early
824 p = self.opener(patchfn, "w")
824 p = self.opener(patchfn, "w")
825 try:
825 try:
826 if self.plainmode:
826 if self.plainmode:
827 if user:
827 if user:
828 p.write("From: " + user + "\n")
828 p.write("From: " + user + "\n")
829 if not date:
829 if not date:
830 p.write("\n")
830 p.write("\n")
831 if date:
831 if date:
832 p.write("Date: %d %d\n\n" % date)
832 p.write("Date: %d %d\n\n" % date)
833 else:
833 else:
834 p.write("# HG changeset patch\n")
834 p.write("# HG changeset patch\n")
835 p.write("# Parent "
835 p.write("# Parent "
836 + hex(repo[None].parents()[0].node()) + "\n")
836 + hex(repo[None].parents()[0].node()) + "\n")
837 if user:
837 if user:
838 p.write("# User " + user + "\n")
838 p.write("# User " + user + "\n")
839 if date:
839 if date:
840 p.write("# Date %s %s\n\n" % date)
840 p.write("# Date %s %s\n\n" % date)
841 if hasattr(msg, '__call__'):
841 if hasattr(msg, '__call__'):
842 msg = msg()
842 msg = msg()
843 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
843 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
844 n = repo.commit(commitmsg, user, date, match=match, force=True)
844 n = repo.commit(commitmsg, user, date, match=match, force=True)
845 if n is None:
845 if n is None:
846 raise util.Abort(_("repo commit failed"))
846 raise util.Abort(_("repo commit failed"))
847 try:
847 try:
848 self.full_series[insert:insert] = [patchfn]
848 self.full_series[insert:insert] = [patchfn]
849 self.applied.append(statusentry(n, patchfn))
849 self.applied.append(statusentry(n, patchfn))
850 self.parse_series()
850 self.parse_series()
851 self.series_dirty = 1
851 self.series_dirty = 1
852 self.applied_dirty = 1
852 self.applied_dirty = 1
853 if msg:
853 if msg:
854 msg = msg + "\n\n"
854 msg = msg + "\n\n"
855 p.write(msg)
855 p.write(msg)
856 if commitfiles:
856 if commitfiles:
857 parent = self.qparents(repo, n)
857 parent = self.qparents(repo, n)
858 chunks = patch.diff(repo, node1=parent, node2=n,
858 chunks = patch.diff(repo, node1=parent, node2=n,
859 match=match, opts=diffopts)
859 match=match, opts=diffopts)
860 for chunk in chunks:
860 for chunk in chunks:
861 p.write(chunk)
861 p.write(chunk)
862 p.close()
862 p.close()
863 wlock.release()
863 wlock.release()
864 wlock = None
864 wlock = None
865 r = self.qrepo()
865 r = self.qrepo()
866 if r:
866 if r:
867 r.add([patchfn])
867 r.add([patchfn])
868 except:
868 except:
869 repo.rollback()
869 repo.rollback()
870 raise
870 raise
871 except Exception:
871 except Exception:
872 patchpath = self.join(patchfn)
872 patchpath = self.join(patchfn)
873 try:
873 try:
874 os.unlink(patchpath)
874 os.unlink(patchpath)
875 except:
875 except:
876 self.ui.warn(_('error unlinking %s\n') % patchpath)
876 self.ui.warn(_('error unlinking %s\n') % patchpath)
877 raise
877 raise
878 self.removeundo(repo)
878 self.removeundo(repo)
879 finally:
879 finally:
880 release(wlock)
880 release(wlock)
881
881
882 def strip(self, repo, rev, update=True, backup="all", force=None):
882 def strip(self, repo, rev, update=True, backup="all", force=None):
883 wlock = lock = None
883 wlock = lock = None
884 try:
884 try:
885 wlock = repo.wlock()
885 wlock = repo.wlock()
886 lock = repo.lock()
886 lock = repo.lock()
887
887
888 if update:
888 if update:
889 self.check_localchanges(repo, force=force, refresh=False)
889 self.check_localchanges(repo, force=force, refresh=False)
890 urev = self.qparents(repo, rev)
890 urev = self.qparents(repo, rev)
891 hg.clean(repo, urev)
891 hg.clean(repo, urev)
892 repo.dirstate.write()
892 repo.dirstate.write()
893
893
894 self.removeundo(repo)
894 self.removeundo(repo)
895 repair.strip(self.ui, repo, rev, backup)
895 repair.strip(self.ui, repo, rev, backup)
896 # strip may have unbundled a set of backed up revisions after
896 # strip may have unbundled a set of backed up revisions after
897 # the actual strip
897 # the actual strip
898 self.removeundo(repo)
898 self.removeundo(repo)
899 finally:
899 finally:
900 release(lock, wlock)
900 release(lock, wlock)
901
901
902 def isapplied(self, patch):
902 def isapplied(self, patch):
903 """returns (index, rev, patch)"""
903 """returns (index, rev, patch)"""
904 for i, a in enumerate(self.applied):
904 for i, a in enumerate(self.applied):
905 if a.name == patch:
905 if a.name == patch:
906 return (i, a.node, a.name)
906 return (i, a.node, a.name)
907 return None
907 return None
908
908
909 # if the exact patch name does not exist, we try a few
909 # if the exact patch name does not exist, we try a few
910 # variations. If strict is passed, we try only #1
910 # variations. If strict is passed, we try only #1
911 #
911 #
912 # 1) a number to indicate an offset in the series file
912 # 1) a number to indicate an offset in the series file
913 # 2) a unique substring of the patch name was given
913 # 2) a unique substring of the patch name was given
914 # 3) patchname[-+]num to indicate an offset in the series file
914 # 3) patchname[-+]num to indicate an offset in the series file
915 def lookup(self, patch, strict=False):
915 def lookup(self, patch, strict=False):
916 patch = patch and str(patch)
916 patch = patch and str(patch)
917
917
918 def partial_name(s):
918 def partial_name(s):
919 if s in self.series:
919 if s in self.series:
920 return s
920 return s
921 matches = [x for x in self.series if s in x]
921 matches = [x for x in self.series if s in x]
922 if len(matches) > 1:
922 if len(matches) > 1:
923 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
923 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
924 for m in matches:
924 for m in matches:
925 self.ui.warn(' %s\n' % m)
925 self.ui.warn(' %s\n' % m)
926 return None
926 return None
927 if matches:
927 if matches:
928 return matches[0]
928 return matches[0]
929 if self.series and self.applied:
929 if self.series and self.applied:
930 if s == 'qtip':
930 if s == 'qtip':
931 return self.series[self.series_end(True)-1]
931 return self.series[self.series_end(True)-1]
932 if s == 'qbase':
932 if s == 'qbase':
933 return self.series[0]
933 return self.series[0]
934 return None
934 return None
935
935
936 if patch is None:
936 if patch is None:
937 return None
937 return None
938 if patch in self.series:
938 if patch in self.series:
939 return patch
939 return patch
940
940
941 if not os.path.isfile(self.join(patch)):
941 if not os.path.isfile(self.join(patch)):
942 try:
942 try:
943 sno = int(patch)
943 sno = int(patch)
944 except (ValueError, OverflowError):
944 except (ValueError, OverflowError):
945 pass
945 pass
946 else:
946 else:
947 if -len(self.series) <= sno < len(self.series):
947 if -len(self.series) <= sno < len(self.series):
948 return self.series[sno]
948 return self.series[sno]
949
949
950 if not strict:
950 if not strict:
951 res = partial_name(patch)
951 res = partial_name(patch)
952 if res:
952 if res:
953 return res
953 return res
954 minus = patch.rfind('-')
954 minus = patch.rfind('-')
955 if minus >= 0:
955 if minus >= 0:
956 res = partial_name(patch[:minus])
956 res = partial_name(patch[:minus])
957 if res:
957 if res:
958 i = self.series.index(res)
958 i = self.series.index(res)
959 try:
959 try:
960 off = int(patch[minus + 1:] or 1)
960 off = int(patch[minus + 1:] or 1)
961 except (ValueError, OverflowError):
961 except (ValueError, OverflowError):
962 pass
962 pass
963 else:
963 else:
964 if i - off >= 0:
964 if i - off >= 0:
965 return self.series[i - off]
965 return self.series[i - off]
966 plus = patch.rfind('+')
966 plus = patch.rfind('+')
967 if plus >= 0:
967 if plus >= 0:
968 res = partial_name(patch[:plus])
968 res = partial_name(patch[:plus])
969 if res:
969 if res:
970 i = self.series.index(res)
970 i = self.series.index(res)
971 try:
971 try:
972 off = int(patch[plus + 1:] or 1)
972 off = int(patch[plus + 1:] or 1)
973 except (ValueError, OverflowError):
973 except (ValueError, OverflowError):
974 pass
974 pass
975 else:
975 else:
976 if i + off < len(self.series):
976 if i + off < len(self.series):
977 return self.series[i + off]
977 return self.series[i + off]
978 raise util.Abort(_("patch %s not in series") % patch)
978 raise util.Abort(_("patch %s not in series") % patch)
979
979
980 def push(self, repo, patch=None, force=False, list=False,
980 def push(self, repo, patch=None, force=False, list=False,
981 mergeq=None, all=False, move=False):
981 mergeq=None, all=False, move=False):
982 diffopts = self.diffopts()
982 diffopts = self.diffopts()
983 wlock = repo.wlock()
983 wlock = repo.wlock()
984 try:
984 try:
985 heads = []
985 heads = []
986 for b, ls in repo.branchmap().iteritems():
986 for b, ls in repo.branchmap().iteritems():
987 heads += ls
987 heads += ls
988 if not heads:
988 if not heads:
989 heads = [nullid]
989 heads = [nullid]
990 if repo.dirstate.parents()[0] not in heads:
990 if repo.dirstate.parents()[0] not in heads:
991 self.ui.status(_("(working directory not at a head)\n"))
991 self.ui.status(_("(working directory not at a head)\n"))
992
992
993 if not self.series:
993 if not self.series:
994 self.ui.warn(_('no patches in series\n'))
994 self.ui.warn(_('no patches in series\n'))
995 return 0
995 return 0
996
996
997 patch = self.lookup(patch)
997 patch = self.lookup(patch)
998 # Suppose our series file is: A B C and the current 'top'
998 # Suppose our series file is: A B C and the current 'top'
999 # patch is B. qpush C should be performed (moving forward)
999 # patch is B. qpush C should be performed (moving forward)
1000 # qpush B is a NOP (no change) qpush A is an error (can't
1000 # qpush B is a NOP (no change) qpush A is an error (can't
1001 # go backwards with qpush)
1001 # go backwards with qpush)
1002 if patch:
1002 if patch:
1003 info = self.isapplied(patch)
1003 info = self.isapplied(patch)
1004 if info:
1004 if info:
1005 if info[0] < len(self.applied) - 1:
1005 if info[0] < len(self.applied) - 1:
1006 raise util.Abort(
1006 raise util.Abort(
1007 _("cannot push to a previous patch: %s") % patch)
1007 _("cannot push to a previous patch: %s") % patch)
1008 self.ui.warn(
1008 self.ui.warn(
1009 _('qpush: %s is already at the top\n') % patch)
1009 _('qpush: %s is already at the top\n') % patch)
1010 return
1010 return
1011 pushable, reason = self.pushable(patch)
1011 pushable, reason = self.pushable(patch)
1012 if not pushable:
1012 if not pushable:
1013 if reason:
1013 if reason:
1014 reason = _('guarded by %r') % reason
1014 reason = _('guarded by %r') % reason
1015 else:
1015 else:
1016 reason = _('no matching guards')
1016 reason = _('no matching guards')
1017 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1017 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1018 return 1
1018 return 1
1019 elif all:
1019 elif all:
1020 patch = self.series[-1]
1020 patch = self.series[-1]
1021 if self.isapplied(patch):
1021 if self.isapplied(patch):
1022 self.ui.warn(_('all patches are currently applied\n'))
1022 self.ui.warn(_('all patches are currently applied\n'))
1023 return 0
1023 return 0
1024
1024
1025 # Following the above example, starting at 'top' of B:
1025 # Following the above example, starting at 'top' of B:
1026 # qpush should be performed (pushes C), but a subsequent
1026 # qpush should be performed (pushes C), but a subsequent
1027 # qpush without an argument is an error (nothing to
1027 # qpush without an argument is an error (nothing to
1028 # apply). This allows a loop of "...while hg qpush..." to
1028 # apply). This allows a loop of "...while hg qpush..." to
1029 # work as it detects an error when done
1029 # work as it detects an error when done
1030 start = self.series_end()
1030 start = self.series_end()
1031 if start == len(self.series):
1031 if start == len(self.series):
1032 self.ui.warn(_('patch series already fully applied\n'))
1032 self.ui.warn(_('patch series already fully applied\n'))
1033 return 1
1033 return 1
1034 if not force:
1034 if not force:
1035 self.check_localchanges(repo)
1035 self.check_localchanges(repo)
1036
1036
1037 if move:
1037 if move:
1038 try:
1038 try:
1039 del self.full_series[self.full_series.index(patch, start)]
1039 del self.full_series[self.full_series.index(patch, start)]
1040 except ValueError:
1040 except ValueError:
1041 raise util.Abort(_("patch '%s' not found") % patch)
1041 raise util.Abort(_("patch '%s' not found") % patch)
1042 self.full_series.insert(start, patch)
1042 self.full_series.insert(start, patch)
1043 self.parse_series()
1043 self.parse_series()
1044 self.series_dirty = 1
1044 self.series_dirty = 1
1045
1045
1046 self.applied_dirty = 1
1046 self.applied_dirty = 1
1047 if start > 0:
1047 if start > 0:
1048 self.check_toppatch(repo)
1048 self.check_toppatch(repo)
1049 if not patch:
1049 if not patch:
1050 patch = self.series[start]
1050 patch = self.series[start]
1051 end = start + 1
1051 end = start + 1
1052 else:
1052 else:
1053 end = self.series.index(patch, start) + 1
1053 end = self.series.index(patch, start) + 1
1054
1054
1055 s = self.series[start:end]
1055 s = self.series[start:end]
1056 all_files = set()
1056 all_files = set()
1057 try:
1057 try:
1058 if mergeq:
1058 if mergeq:
1059 ret = self.mergepatch(repo, mergeq, s, diffopts)
1059 ret = self.mergepatch(repo, mergeq, s, diffopts)
1060 else:
1060 else:
1061 ret = self.apply(repo, s, list, all_files=all_files)
1061 ret = self.apply(repo, s, list, all_files=all_files)
1062 except:
1062 except:
1063 self.ui.warn(_('cleaning up working directory...'))
1063 self.ui.warn(_('cleaning up working directory...'))
1064 node = repo.dirstate.parents()[0]
1064 node = repo.dirstate.parents()[0]
1065 hg.revert(repo, node, None)
1065 hg.revert(repo, node, None)
1066 # only remove unknown files that we know we touched or
1066 # only remove unknown files that we know we touched or
1067 # created while patching
1067 # created while patching
1068 for f in all_files:
1068 for f in all_files:
1069 if f not in repo.dirstate:
1069 if f not in repo.dirstate:
1070 try:
1070 try:
1071 util.unlink(repo.wjoin(f))
1071 util.unlink(repo.wjoin(f))
1072 except OSError, inst:
1072 except OSError, inst:
1073 if inst.errno != errno.ENOENT:
1073 if inst.errno != errno.ENOENT:
1074 raise
1074 raise
1075 self.ui.warn(_('done\n'))
1075 self.ui.warn(_('done\n'))
1076 raise
1076 raise
1077
1077
1078 if not self.applied:
1078 if not self.applied:
1079 return ret[0]
1079 return ret[0]
1080 top = self.applied[-1].name
1080 top = self.applied[-1].name
1081 if ret[0] and ret[0] > 1:
1081 if ret[0] and ret[0] > 1:
1082 msg = _("errors during apply, please fix and refresh %s\n")
1082 msg = _("errors during apply, please fix and refresh %s\n")
1083 self.ui.write(msg % top)
1083 self.ui.write(msg % top)
1084 else:
1084 else:
1085 self.ui.write(_("now at: %s\n") % top)
1085 self.ui.write(_("now at: %s\n") % top)
1086 return ret[0]
1086 return ret[0]
1087
1087
1088 finally:
1088 finally:
1089 wlock.release()
1089 wlock.release()
1090
1090
1091 def pop(self, repo, patch=None, force=False, update=True, all=False):
1091 def pop(self, repo, patch=None, force=False, update=True, all=False):
1092 wlock = repo.wlock()
1092 wlock = repo.wlock()
1093 try:
1093 try:
1094 if patch:
1094 if patch:
1095 # index, rev, patch
1095 # index, rev, patch
1096 info = self.isapplied(patch)
1096 info = self.isapplied(patch)
1097 if not info:
1097 if not info:
1098 patch = self.lookup(patch)
1098 patch = self.lookup(patch)
1099 info = self.isapplied(patch)
1099 info = self.isapplied(patch)
1100 if not info:
1100 if not info:
1101 raise util.Abort(_("patch %s is not applied") % patch)
1101 raise util.Abort(_("patch %s is not applied") % patch)
1102
1102
1103 if not self.applied:
1103 if not self.applied:
1104 # Allow qpop -a to work repeatedly,
1104 # Allow qpop -a to work repeatedly,
1105 # but not qpop without an argument
1105 # but not qpop without an argument
1106 self.ui.warn(_("no patches applied\n"))
1106 self.ui.warn(_("no patches applied\n"))
1107 return not all
1107 return not all
1108
1108
1109 if all:
1109 if all:
1110 start = 0
1110 start = 0
1111 elif patch:
1111 elif patch:
1112 start = info[0] + 1
1112 start = info[0] + 1
1113 else:
1113 else:
1114 start = len(self.applied) - 1
1114 start = len(self.applied) - 1
1115
1115
1116 if start >= len(self.applied):
1116 if start >= len(self.applied):
1117 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1117 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1118 return
1118 return
1119
1119
1120 if not update:
1120 if not update:
1121 parents = repo.dirstate.parents()
1121 parents = repo.dirstate.parents()
1122 rr = [x.node for x in self.applied]
1122 rr = [x.node for x in self.applied]
1123 for p in parents:
1123 for p in parents:
1124 if p in rr:
1124 if p in rr:
1125 self.ui.warn(_("qpop: forcing dirstate update\n"))
1125 self.ui.warn(_("qpop: forcing dirstate update\n"))
1126 update = True
1126 update = True
1127 else:
1127 else:
1128 parents = [p.node() for p in repo[None].parents()]
1128 parents = [p.node() for p in repo[None].parents()]
1129 needupdate = False
1129 needupdate = False
1130 for entry in self.applied[start:]:
1130 for entry in self.applied[start:]:
1131 if entry.node in parents:
1131 if entry.node in parents:
1132 needupdate = True
1132 needupdate = True
1133 break
1133 break
1134 update = needupdate
1134 update = needupdate
1135
1135
1136 if not force and update:
1136 if not force and update:
1137 self.check_localchanges(repo)
1137 self.check_localchanges(repo)
1138
1138
1139 self.applied_dirty = 1
1139 self.applied_dirty = 1
1140 end = len(self.applied)
1140 end = len(self.applied)
1141 rev = self.applied[start].node
1141 rev = self.applied[start].node
1142 if update:
1142 if update:
1143 top = self.check_toppatch(repo)[0]
1143 top = self.check_toppatch(repo)[0]
1144
1144
1145 try:
1145 try:
1146 heads = repo.changelog.heads(rev)
1146 heads = repo.changelog.heads(rev)
1147 except error.LookupError:
1147 except error.LookupError:
1148 node = short(rev)
1148 node = short(rev)
1149 raise util.Abort(_('trying to pop unknown node %s') % node)
1149 raise util.Abort(_('trying to pop unknown node %s') % node)
1150
1150
1151 if heads != [self.applied[-1].node]:
1151 if heads != [self.applied[-1].node]:
1152 raise util.Abort(_("popping would remove a revision not "
1152 raise util.Abort(_("popping would remove a revision not "
1153 "managed by this patch queue"))
1153 "managed by this patch queue"))
1154
1154
1155 # we know there are no local changes, so we can make a simplified
1155 # we know there are no local changes, so we can make a simplified
1156 # form of hg.update.
1156 # form of hg.update.
1157 if update:
1157 if update:
1158 qp = self.qparents(repo, rev)
1158 qp = self.qparents(repo, rev)
1159 ctx = repo[qp]
1159 ctx = repo[qp]
1160 m, a, r, d = repo.status(qp, top)[:4]
1160 m, a, r, d = repo.status(qp, top)[:4]
1161 if d:
1161 if d:
1162 raise util.Abort(_("deletions found between repo revs"))
1162 raise util.Abort(_("deletions found between repo revs"))
1163 for f in a:
1163 for f in a:
1164 try:
1164 try:
1165 util.unlink(repo.wjoin(f))
1165 util.unlink(repo.wjoin(f))
1166 except OSError, e:
1166 except OSError, e:
1167 if e.errno != errno.ENOENT:
1167 if e.errno != errno.ENOENT:
1168 raise
1168 raise
1169 repo.dirstate.forget(f)
1169 repo.dirstate.forget(f)
1170 for f in m + r:
1170 for f in m + r:
1171 fctx = ctx[f]
1171 fctx = ctx[f]
1172 repo.wwrite(f, fctx.data(), fctx.flags())
1172 repo.wwrite(f, fctx.data(), fctx.flags())
1173 repo.dirstate.normal(f)
1173 repo.dirstate.normal(f)
1174 repo.dirstate.setparents(qp, nullid)
1174 repo.dirstate.setparents(qp, nullid)
1175 for patch in reversed(self.applied[start:end]):
1175 for patch in reversed(self.applied[start:end]):
1176 self.ui.status(_("popping %s\n") % patch.name)
1176 self.ui.status(_("popping %s\n") % patch.name)
1177 del self.applied[start:end]
1177 del self.applied[start:end]
1178 self.strip(repo, rev, update=False, backup='strip')
1178 self.strip(repo, rev, update=False, backup='strip')
1179 if self.applied:
1179 if self.applied:
1180 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1180 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1181 else:
1181 else:
1182 self.ui.write(_("patch queue now empty\n"))
1182 self.ui.write(_("patch queue now empty\n"))
1183 finally:
1183 finally:
1184 wlock.release()
1184 wlock.release()
1185
1185
1186 def diff(self, repo, pats, opts):
1186 def diff(self, repo, pats, opts):
1187 top, patch = self.check_toppatch(repo)
1187 top, patch = self.check_toppatch(repo)
1188 if not top:
1188 if not top:
1189 self.ui.write(_("no patches applied\n"))
1189 self.ui.write(_("no patches applied\n"))
1190 return
1190 return
1191 qp = self.qparents(repo, top)
1191 qp = self.qparents(repo, top)
1192 if opts.get('reverse'):
1192 if opts.get('reverse'):
1193 node1, node2 = None, qp
1193 node1, node2 = None, qp
1194 else:
1194 else:
1195 node1, node2 = qp, None
1195 node1, node2 = qp, None
1196 diffopts = self.diffopts(opts, patch)
1196 diffopts = self.diffopts(opts, patch)
1197 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1197 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1198
1198
1199 def refresh(self, repo, pats=None, **opts):
1199 def refresh(self, repo, pats=None, **opts):
1200 if not self.applied:
1200 if not self.applied:
1201 self.ui.write(_("no patches applied\n"))
1201 self.ui.write(_("no patches applied\n"))
1202 return 1
1202 return 1
1203 msg = opts.get('msg', '').rstrip()
1203 msg = opts.get('msg', '').rstrip()
1204 newuser = opts.get('user')
1204 newuser = opts.get('user')
1205 newdate = opts.get('date')
1205 newdate = opts.get('date')
1206 if newdate:
1206 if newdate:
1207 newdate = '%d %d' % util.parsedate(newdate)
1207 newdate = '%d %d' % util.parsedate(newdate)
1208 wlock = repo.wlock()
1208 wlock = repo.wlock()
1209
1209
1210 try:
1210 try:
1211 self.check_toppatch(repo)
1211 self.check_toppatch(repo)
1212 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1212 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1213 if repo.changelog.heads(top) != [top]:
1213 if repo.changelog.heads(top) != [top]:
1214 raise util.Abort(_("cannot refresh a revision with children"))
1214 raise util.Abort(_("cannot refresh a revision with children"))
1215
1215
1216 cparents = repo.changelog.parents(top)
1216 cparents = repo.changelog.parents(top)
1217 patchparent = self.qparents(repo, top)
1217 patchparent = self.qparents(repo, top)
1218 ph = patchheader(self.join(patchfn), self.plainmode)
1218 ph = patchheader(self.join(patchfn), self.plainmode)
1219 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1219 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1220 if msg:
1220 if msg:
1221 ph.setmessage(msg)
1221 ph.setmessage(msg)
1222 if newuser:
1222 if newuser:
1223 ph.setuser(newuser)
1223 ph.setuser(newuser)
1224 if newdate:
1224 if newdate:
1225 ph.setdate(newdate)
1225 ph.setdate(newdate)
1226 ph.setparent(hex(patchparent))
1226 ph.setparent(hex(patchparent))
1227
1227
1228 # only commit new patch when write is complete
1228 # only commit new patch when write is complete
1229 patchf = self.opener(patchfn, 'w', atomictemp=True)
1229 patchf = self.opener(patchfn, 'w', atomictemp=True)
1230
1230
1231 comments = str(ph)
1231 comments = str(ph)
1232 if comments:
1232 if comments:
1233 patchf.write(comments)
1233 patchf.write(comments)
1234
1234
1235 # update the dirstate in place, strip off the qtip commit
1235 # update the dirstate in place, strip off the qtip commit
1236 # and then commit.
1236 # and then commit.
1237 #
1237 #
1238 # this should really read:
1238 # this should really read:
1239 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1239 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1240 # but we do it backwards to take advantage of manifest/chlog
1240 # but we do it backwards to take advantage of manifest/chlog
1241 # caching against the next repo.status call
1241 # caching against the next repo.status call
1242 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1242 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1243 changes = repo.changelog.read(top)
1243 changes = repo.changelog.read(top)
1244 man = repo.manifest.read(changes[0])
1244 man = repo.manifest.read(changes[0])
1245 aaa = aa[:]
1245 aaa = aa[:]
1246 matchfn = cmdutil.match(repo, pats, opts)
1246 matchfn = cmdutil.match(repo, pats, opts)
1247 # in short mode, we only diff the files included in the
1247 # in short mode, we only diff the files included in the
1248 # patch already plus specified files
1248 # patch already plus specified files
1249 if opts.get('short'):
1249 if opts.get('short'):
1250 # if amending a patch, we start with existing
1250 # if amending a patch, we start with existing
1251 # files plus specified files - unfiltered
1251 # files plus specified files - unfiltered
1252 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1252 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1253 # filter with inc/exl options
1253 # filter with inc/exl options
1254 matchfn = cmdutil.match(repo, opts=opts)
1254 matchfn = cmdutil.match(repo, opts=opts)
1255 else:
1255 else:
1256 match = cmdutil.matchall(repo)
1256 match = cmdutil.matchall(repo)
1257 m, a, r, d = repo.status(match=match)[:4]
1257 m, a, r, d = repo.status(match=match)[:4]
1258
1258
1259 # we might end up with files that were added between
1259 # we might end up with files that were added between
1260 # qtip and the dirstate parent, but then changed in the
1260 # qtip and the dirstate parent, but then changed in the
1261 # local dirstate. in this case, we want them to only
1261 # local dirstate. in this case, we want them to only
1262 # show up in the added section
1262 # show up in the added section
1263 for x in m:
1263 for x in m:
1264 if x not in aa:
1264 if x not in aa:
1265 mm.append(x)
1265 mm.append(x)
1266 # we might end up with files added by the local dirstate that
1266 # we might end up with files added by the local dirstate that
1267 # were deleted by the patch. In this case, they should only
1267 # were deleted by the patch. In this case, they should only
1268 # show up in the changed section.
1268 # show up in the changed section.
1269 for x in a:
1269 for x in a:
1270 if x in dd:
1270 if x in dd:
1271 del dd[dd.index(x)]
1271 del dd[dd.index(x)]
1272 mm.append(x)
1272 mm.append(x)
1273 else:
1273 else:
1274 aa.append(x)
1274 aa.append(x)
1275 # make sure any files deleted in the local dirstate
1275 # make sure any files deleted in the local dirstate
1276 # are not in the add or change column of the patch
1276 # are not in the add or change column of the patch
1277 forget = []
1277 forget = []
1278 for x in d + r:
1278 for x in d + r:
1279 if x in aa:
1279 if x in aa:
1280 del aa[aa.index(x)]
1280 del aa[aa.index(x)]
1281 forget.append(x)
1281 forget.append(x)
1282 continue
1282 continue
1283 elif x in mm:
1283 elif x in mm:
1284 del mm[mm.index(x)]
1284 del mm[mm.index(x)]
1285 dd.append(x)
1285 dd.append(x)
1286
1286
1287 m = list(set(mm))
1287 m = list(set(mm))
1288 r = list(set(dd))
1288 r = list(set(dd))
1289 a = list(set(aa))
1289 a = list(set(aa))
1290 c = [filter(matchfn, l) for l in (m, a, r)]
1290 c = [filter(matchfn, l) for l in (m, a, r)]
1291 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1291 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1292 chunks = patch.diff(repo, patchparent, match=match,
1292 chunks = patch.diff(repo, patchparent, match=match,
1293 changes=c, opts=diffopts)
1293 changes=c, opts=diffopts)
1294 for chunk in chunks:
1294 for chunk in chunks:
1295 patchf.write(chunk)
1295 patchf.write(chunk)
1296
1296
1297 try:
1297 try:
1298 if diffopts.git or diffopts.upgrade:
1298 if diffopts.git or diffopts.upgrade:
1299 copies = {}
1299 copies = {}
1300 for dst in a:
1300 for dst in a:
1301 src = repo.dirstate.copied(dst)
1301 src = repo.dirstate.copied(dst)
1302 # during qfold, the source file for copies may
1302 # during qfold, the source file for copies may
1303 # be removed. Treat this as a simple add.
1303 # be removed. Treat this as a simple add.
1304 if src is not None and src in repo.dirstate:
1304 if src is not None and src in repo.dirstate:
1305 copies.setdefault(src, []).append(dst)
1305 copies.setdefault(src, []).append(dst)
1306 repo.dirstate.add(dst)
1306 repo.dirstate.add(dst)
1307 # remember the copies between patchparent and qtip
1307 # remember the copies between patchparent and qtip
1308 for dst in aaa:
1308 for dst in aaa:
1309 f = repo.file(dst)
1309 f = repo.file(dst)
1310 src = f.renamed(man[dst])
1310 src = f.renamed(man[dst])
1311 if src:
1311 if src:
1312 copies.setdefault(src[0], []).extend(
1312 copies.setdefault(src[0], []).extend(
1313 copies.get(dst, []))
1313 copies.get(dst, []))
1314 if dst in a:
1314 if dst in a:
1315 copies[src[0]].append(dst)
1315 copies[src[0]].append(dst)
1316 # we can't copy a file created by the patch itself
1316 # we can't copy a file created by the patch itself
1317 if dst in copies:
1317 if dst in copies:
1318 del copies[dst]
1318 del copies[dst]
1319 for src, dsts in copies.iteritems():
1319 for src, dsts in copies.iteritems():
1320 for dst in dsts:
1320 for dst in dsts:
1321 repo.dirstate.copy(src, dst)
1321 repo.dirstate.copy(src, dst)
1322 else:
1322 else:
1323 for dst in a:
1323 for dst in a:
1324 repo.dirstate.add(dst)
1324 repo.dirstate.add(dst)
1325 # Drop useless copy information
1325 # Drop useless copy information
1326 for f in list(repo.dirstate.copies()):
1326 for f in list(repo.dirstate.copies()):
1327 repo.dirstate.copy(None, f)
1327 repo.dirstate.copy(None, f)
1328 for f in r:
1328 for f in r:
1329 repo.dirstate.remove(f)
1329 repo.dirstate.remove(f)
1330 # if the patch excludes a modified file, mark that
1330 # if the patch excludes a modified file, mark that
1331 # file with mtime=0 so status can see it.
1331 # file with mtime=0 so status can see it.
1332 mm = []
1332 mm = []
1333 for i in xrange(len(m)-1, -1, -1):
1333 for i in xrange(len(m)-1, -1, -1):
1334 if not matchfn(m[i]):
1334 if not matchfn(m[i]):
1335 mm.append(m[i])
1335 mm.append(m[i])
1336 del m[i]
1336 del m[i]
1337 for f in m:
1337 for f in m:
1338 repo.dirstate.normal(f)
1338 repo.dirstate.normal(f)
1339 for f in mm:
1339 for f in mm:
1340 repo.dirstate.normallookup(f)
1340 repo.dirstate.normallookup(f)
1341 for f in forget:
1341 for f in forget:
1342 repo.dirstate.forget(f)
1342 repo.dirstate.forget(f)
1343
1343
1344 if not msg:
1344 if not msg:
1345 if not ph.message:
1345 if not ph.message:
1346 message = "[mq]: %s\n" % patchfn
1346 message = "[mq]: %s\n" % patchfn
1347 else:
1347 else:
1348 message = "\n".join(ph.message)
1348 message = "\n".join(ph.message)
1349 else:
1349 else:
1350 message = msg
1350 message = msg
1351
1351
1352 user = ph.user or changes[1]
1352 user = ph.user or changes[1]
1353
1353
1354 # assumes strip can roll itself back if interrupted
1354 # assumes strip can roll itself back if interrupted
1355 repo.dirstate.setparents(*cparents)
1355 repo.dirstate.setparents(*cparents)
1356 self.applied.pop()
1356 self.applied.pop()
1357 self.applied_dirty = 1
1357 self.applied_dirty = 1
1358 self.strip(repo, top, update=False,
1358 self.strip(repo, top, update=False,
1359 backup='strip')
1359 backup='strip')
1360 except:
1360 except:
1361 repo.dirstate.invalidate()
1361 repo.dirstate.invalidate()
1362 raise
1362 raise
1363
1363
1364 try:
1364 try:
1365 # might be nice to attempt to roll back strip after this
1365 # might be nice to attempt to roll back strip after this
1366 patchf.rename()
1366 patchf.rename()
1367 n = repo.commit(message, user, ph.date, match=match,
1367 n = repo.commit(message, user, ph.date, match=match,
1368 force=True)
1368 force=True)
1369 self.applied.append(statusentry(n, patchfn))
1369 self.applied.append(statusentry(n, patchfn))
1370 except:
1370 except:
1371 ctx = repo[cparents[0]]
1371 ctx = repo[cparents[0]]
1372 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1372 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1373 self.save_dirty()
1373 self.save_dirty()
1374 self.ui.warn(_('refresh interrupted while patch was popped! '
1374 self.ui.warn(_('refresh interrupted while patch was popped! '
1375 '(revert --all, qpush to recover)\n'))
1375 '(revert --all, qpush to recover)\n'))
1376 raise
1376 raise
1377 finally:
1377 finally:
1378 wlock.release()
1378 wlock.release()
1379 self.removeundo(repo)
1379 self.removeundo(repo)
1380
1380
1381 def init(self, repo, create=False):
1381 def init(self, repo, create=False):
1382 if not create and os.path.isdir(self.path):
1382 if not create and os.path.isdir(self.path):
1383 raise util.Abort(_("patch queue directory already exists"))
1383 raise util.Abort(_("patch queue directory already exists"))
1384 try:
1384 try:
1385 os.mkdir(self.path)
1385 os.mkdir(self.path)
1386 except OSError, inst:
1386 except OSError, inst:
1387 if inst.errno != errno.EEXIST or not create:
1387 if inst.errno != errno.EEXIST or not create:
1388 raise
1388 raise
1389 if create:
1389 if create:
1390 return self.qrepo(create=True)
1390 return self.qrepo(create=True)
1391
1391
1392 def unapplied(self, repo, patch=None):
1392 def unapplied(self, repo, patch=None):
1393 if patch and patch not in self.series:
1393 if patch and patch not in self.series:
1394 raise util.Abort(_("patch %s is not in series file") % patch)
1394 raise util.Abort(_("patch %s is not in series file") % patch)
1395 if not patch:
1395 if not patch:
1396 start = self.series_end()
1396 start = self.series_end()
1397 else:
1397 else:
1398 start = self.series.index(patch) + 1
1398 start = self.series.index(patch) + 1
1399 unapplied = []
1399 unapplied = []
1400 for i in xrange(start, len(self.series)):
1400 for i in xrange(start, len(self.series)):
1401 pushable, reason = self.pushable(i)
1401 pushable, reason = self.pushable(i)
1402 if pushable:
1402 if pushable:
1403 unapplied.append((i, self.series[i]))
1403 unapplied.append((i, self.series[i]))
1404 self.explain_pushable(i)
1404 self.explain_pushable(i)
1405 return unapplied
1405 return unapplied
1406
1406
1407 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1407 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1408 summary=False):
1408 summary=False):
1409 def displayname(pfx, patchname, state):
1409 def displayname(pfx, patchname, state):
1410 if pfx:
1410 if pfx:
1411 self.ui.write(pfx)
1411 self.ui.write(pfx)
1412 if summary:
1412 if summary:
1413 ph = patchheader(self.join(patchname), self.plainmode)
1413 ph = patchheader(self.join(patchname), self.plainmode)
1414 msg = ph.message and ph.message[0] or ''
1414 msg = ph.message and ph.message[0] or ''
1415 if not self.ui.plain():
1415 if not self.ui.plain():
1416 width = util.termwidth() - len(pfx) - len(patchname) - 2
1416 width = util.termwidth() - len(pfx) - len(patchname) - 2
1417 if width > 0:
1417 if width > 0:
1418 msg = util.ellipsis(msg, width)
1418 msg = util.ellipsis(msg, width)
1419 else:
1419 else:
1420 msg = ''
1420 msg = ''
1421 self.ui.write(patchname, label='qseries.' + state)
1421 self.ui.write(patchname, label='qseries.' + state)
1422 self.ui.write(': ')
1422 self.ui.write(': ')
1423 self.ui.write(msg, label='qseries.message.' + state)
1423 self.ui.write(msg, label='qseries.message.' + state)
1424 else:
1424 else:
1425 self.ui.write(patchname, label='qseries.' + state)
1425 self.ui.write(patchname, label='qseries.' + state)
1426 self.ui.write('\n')
1426 self.ui.write('\n')
1427
1427
1428 applied = set([p.name for p in self.applied])
1428 applied = set([p.name for p in self.applied])
1429 if length is None:
1429 if length is None:
1430 length = len(self.series) - start
1430 length = len(self.series) - start
1431 if not missing:
1431 if not missing:
1432 if self.ui.verbose:
1432 if self.ui.verbose:
1433 idxwidth = len(str(start + length - 1))
1433 idxwidth = len(str(start + length - 1))
1434 for i in xrange(start, start + length):
1434 for i in xrange(start, start + length):
1435 patch = self.series[i]
1435 patch = self.series[i]
1436 if patch in applied:
1436 if patch in applied:
1437 char, state = 'A', 'applied'
1437 char, state = 'A', 'applied'
1438 elif self.pushable(i)[0]:
1438 elif self.pushable(i)[0]:
1439 char, state = 'U', 'unapplied'
1439 char, state = 'U', 'unapplied'
1440 else:
1440 else:
1441 char, state = 'G', 'guarded'
1441 char, state = 'G', 'guarded'
1442 pfx = ''
1442 pfx = ''
1443 if self.ui.verbose:
1443 if self.ui.verbose:
1444 pfx = '%*d %s ' % (idxwidth, i, char)
1444 pfx = '%*d %s ' % (idxwidth, i, char)
1445 elif status and status != char:
1445 elif status and status != char:
1446 continue
1446 continue
1447 displayname(pfx, patch, state)
1447 displayname(pfx, patch, state)
1448 else:
1448 else:
1449 msng_list = []
1449 msng_list = []
1450 for root, dirs, files in os.walk(self.path):
1450 for root, dirs, files in os.walk(self.path):
1451 d = root[len(self.path) + 1:]
1451 d = root[len(self.path) + 1:]
1452 for f in files:
1452 for f in files:
1453 fl = os.path.join(d, f)
1453 fl = os.path.join(d, f)
1454 if (fl not in self.series and
1454 if (fl not in self.series and
1455 fl not in (self.status_path, self.series_path,
1455 fl not in (self.status_path, self.series_path,
1456 self.guards_path)
1456 self.guards_path)
1457 and not fl.startswith('.')):
1457 and not fl.startswith('.')):
1458 msng_list.append(fl)
1458 msng_list.append(fl)
1459 for x in sorted(msng_list):
1459 for x in sorted(msng_list):
1460 pfx = self.ui.verbose and ('D ') or ''
1460 pfx = self.ui.verbose and ('D ') or ''
1461 displayname(pfx, x, 'missing')
1461 displayname(pfx, x, 'missing')
1462
1462
1463 def issaveline(self, l):
1463 def issaveline(self, l):
1464 if l.name == '.hg.patches.save.line':
1464 if l.name == '.hg.patches.save.line':
1465 return True
1465 return True
1466
1466
1467 def qrepo(self, create=False):
1467 def qrepo(self, create=False):
1468 if create or os.path.isdir(self.join(".hg")):
1468 if create or os.path.isdir(self.join(".hg")):
1469 return hg.repository(self.ui, path=self.path, create=create)
1469 return hg.repository(self.ui, path=self.path, create=create)
1470
1470
1471 def restore(self, repo, rev, delete=None, qupdate=None):
1471 def restore(self, repo, rev, delete=None, qupdate=None):
1472 desc = repo[rev].description().strip()
1472 desc = repo[rev].description().strip()
1473 lines = desc.splitlines()
1473 lines = desc.splitlines()
1474 i = 0
1474 i = 0
1475 datastart = None
1475 datastart = None
1476 series = []
1476 series = []
1477 applied = []
1477 applied = []
1478 qpp = None
1478 qpp = None
1479 for i, line in enumerate(lines):
1479 for i, line in enumerate(lines):
1480 if line == 'Patch Data:':
1480 if line == 'Patch Data:':
1481 datastart = i + 1
1481 datastart = i + 1
1482 elif line.startswith('Dirstate:'):
1482 elif line.startswith('Dirstate:'):
1483 l = line.rstrip()
1483 l = line.rstrip()
1484 l = l[10:].split(' ')
1484 l = l[10:].split(' ')
1485 qpp = [bin(x) for x in l]
1485 qpp = [bin(x) for x in l]
1486 elif datastart != None:
1486 elif datastart != None:
1487 l = line.rstrip()
1487 l = line.rstrip()
1488 n, name = l.split(':', 1)
1488 n, name = l.split(':', 1)
1489 if n:
1489 if n:
1490 applied.append(statusentry(bin(n), name))
1490 applied.append(statusentry(bin(n), name))
1491 else:
1491 else:
1492 series.append(l)
1492 series.append(l)
1493 if datastart is None:
1493 if datastart is None:
1494 self.ui.warn(_("No saved patch data found\n"))
1494 self.ui.warn(_("No saved patch data found\n"))
1495 return 1
1495 return 1
1496 self.ui.warn(_("restoring status: %s\n") % lines[0])
1496 self.ui.warn(_("restoring status: %s\n") % lines[0])
1497 self.full_series = series
1497 self.full_series = series
1498 self.applied = applied
1498 self.applied = applied
1499 self.parse_series()
1499 self.parse_series()
1500 self.series_dirty = 1
1500 self.series_dirty = 1
1501 self.applied_dirty = 1
1501 self.applied_dirty = 1
1502 heads = repo.changelog.heads()
1502 heads = repo.changelog.heads()
1503 if delete:
1503 if delete:
1504 if rev not in heads:
1504 if rev not in heads:
1505 self.ui.warn(_("save entry has children, leaving it alone\n"))
1505 self.ui.warn(_("save entry has children, leaving it alone\n"))
1506 else:
1506 else:
1507 self.ui.warn(_("removing save entry %s\n") % short(rev))
1507 self.ui.warn(_("removing save entry %s\n") % short(rev))
1508 pp = repo.dirstate.parents()
1508 pp = repo.dirstate.parents()
1509 if rev in pp:
1509 if rev in pp:
1510 update = True
1510 update = True
1511 else:
1511 else:
1512 update = False
1512 update = False
1513 self.strip(repo, rev, update=update, backup='strip')
1513 self.strip(repo, rev, update=update, backup='strip')
1514 if qpp:
1514 if qpp:
1515 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1515 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1516 (short(qpp[0]), short(qpp[1])))
1516 (short(qpp[0]), short(qpp[1])))
1517 if qupdate:
1517 if qupdate:
1518 self.ui.status(_("queue directory updating\n"))
1518 self.ui.status(_("queue directory updating\n"))
1519 r = self.qrepo()
1519 r = self.qrepo()
1520 if not r:
1520 if not r:
1521 self.ui.warn(_("Unable to load queue repository\n"))
1521 self.ui.warn(_("Unable to load queue repository\n"))
1522 return 1
1522 return 1
1523 hg.clean(r, qpp[0])
1523 hg.clean(r, qpp[0])
1524
1524
1525 def save(self, repo, msg=None):
1525 def save(self, repo, msg=None):
1526 if not self.applied:
1526 if not self.applied:
1527 self.ui.warn(_("save: no patches applied, exiting\n"))
1527 self.ui.warn(_("save: no patches applied, exiting\n"))
1528 return 1
1528 return 1
1529 if self.issaveline(self.applied[-1]):
1529 if self.issaveline(self.applied[-1]):
1530 self.ui.warn(_("status is already saved\n"))
1530 self.ui.warn(_("status is already saved\n"))
1531 return 1
1531 return 1
1532
1532
1533 if not msg:
1533 if not msg:
1534 msg = _("hg patches saved state")
1534 msg = _("hg patches saved state")
1535 else:
1535 else:
1536 msg = "hg patches: " + msg.rstrip('\r\n')
1536 msg = "hg patches: " + msg.rstrip('\r\n')
1537 r = self.qrepo()
1537 r = self.qrepo()
1538 if r:
1538 if r:
1539 pp = r.dirstate.parents()
1539 pp = r.dirstate.parents()
1540 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1540 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1541 msg += "\n\nPatch Data:\n"
1541 msg += "\n\nPatch Data:\n"
1542 msg += ''.join('%s\n' % x for x in self.applied)
1542 msg += ''.join('%s\n' % x for x in self.applied)
1543 msg += ''.join(':%s\n' % x for x in self.full_series)
1543 msg += ''.join(':%s\n' % x for x in self.full_series)
1544 n = repo.commit(msg, force=True)
1544 n = repo.commit(msg, force=True)
1545 if not n:
1545 if not n:
1546 self.ui.warn(_("repo commit failed\n"))
1546 self.ui.warn(_("repo commit failed\n"))
1547 return 1
1547 return 1
1548 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1548 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1549 self.applied_dirty = 1
1549 self.applied_dirty = 1
1550 self.removeundo(repo)
1550 self.removeundo(repo)
1551
1551
1552 def full_series_end(self):
1552 def full_series_end(self):
1553 if self.applied:
1553 if self.applied:
1554 p = self.applied[-1].name
1554 p = self.applied[-1].name
1555 end = self.find_series(p)
1555 end = self.find_series(p)
1556 if end is None:
1556 if end is None:
1557 return len(self.full_series)
1557 return len(self.full_series)
1558 return end + 1
1558 return end + 1
1559 return 0
1559 return 0
1560
1560
1561 def series_end(self, all_patches=False):
1561 def series_end(self, all_patches=False):
1562 """If all_patches is False, return the index of the next pushable patch
1562 """If all_patches is False, return the index of the next pushable patch
1563 in the series, or the series length. If all_patches is True, return the
1563 in the series, or the series length. If all_patches is True, return the
1564 index of the first patch past the last applied one.
1564 index of the first patch past the last applied one.
1565 """
1565 """
1566 end = 0
1566 end = 0
1567 def next(start):
1567 def next(start):
1568 if all_patches or start >= len(self.series):
1568 if all_patches or start >= len(self.series):
1569 return start
1569 return start
1570 for i in xrange(start, len(self.series)):
1570 for i in xrange(start, len(self.series)):
1571 p, reason = self.pushable(i)
1571 p, reason = self.pushable(i)
1572 if p:
1572 if p:
1573 break
1573 break
1574 self.explain_pushable(i)
1574 self.explain_pushable(i)
1575 return i
1575 return i
1576 if self.applied:
1576 if self.applied:
1577 p = self.applied[-1].name
1577 p = self.applied[-1].name
1578 try:
1578 try:
1579 end = self.series.index(p)
1579 end = self.series.index(p)
1580 except ValueError:
1580 except ValueError:
1581 return 0
1581 return 0
1582 return next(end + 1)
1582 return next(end + 1)
1583 return next(end)
1583 return next(end)
1584
1584
1585 def appliedname(self, index):
1585 def appliedname(self, index):
1586 pname = self.applied[index].name
1586 pname = self.applied[index].name
1587 if not self.ui.verbose:
1587 if not self.ui.verbose:
1588 p = pname
1588 p = pname
1589 else:
1589 else:
1590 p = str(self.series.index(pname)) + " " + pname
1590 p = str(self.series.index(pname)) + " " + pname
1591 return p
1591 return p
1592
1592
1593 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1593 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1594 force=None, git=False):
1594 force=None, git=False):
1595 def checkseries(patchname):
1595 def checkseries(patchname):
1596 if patchname in self.series:
1596 if patchname in self.series:
1597 raise util.Abort(_('patch %s is already in the series file')
1597 raise util.Abort(_('patch %s is already in the series file')
1598 % patchname)
1598 % patchname)
1599 def checkfile(patchname):
1599 def checkfile(patchname):
1600 if not force and os.path.exists(self.join(patchname)):
1600 if not force and os.path.exists(self.join(patchname)):
1601 raise util.Abort(_('patch "%s" already exists')
1601 raise util.Abort(_('patch "%s" already exists')
1602 % patchname)
1602 % patchname)
1603
1603
1604 if rev:
1604 if rev:
1605 if files:
1605 if files:
1606 raise util.Abort(_('option "-r" not valid when importing '
1606 raise util.Abort(_('option "-r" not valid when importing '
1607 'files'))
1607 'files'))
1608 rev = cmdutil.revrange(repo, rev)
1608 rev = cmdutil.revrange(repo, rev)
1609 rev.sort(reverse=True)
1609 rev.sort(reverse=True)
1610 if (len(files) > 1 or len(rev) > 1) and patchname:
1610 if (len(files) > 1 or len(rev) > 1) and patchname:
1611 raise util.Abort(_('option "-n" not valid when importing multiple '
1611 raise util.Abort(_('option "-n" not valid when importing multiple '
1612 'patches'))
1612 'patches'))
1613 added = []
1613 added = []
1614 if rev:
1614 if rev:
1615 # If mq patches are applied, we can only import revisions
1615 # If mq patches are applied, we can only import revisions
1616 # that form a linear path to qbase.
1616 # that form a linear path to qbase.
1617 # Otherwise, they should form a linear path to a head.
1617 # Otherwise, they should form a linear path to a head.
1618 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1618 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1619 if len(heads) > 1:
1619 if len(heads) > 1:
1620 raise util.Abort(_('revision %d is the root of more than one '
1620 raise util.Abort(_('revision %d is the root of more than one '
1621 'branch') % rev[-1])
1621 'branch') % rev[-1])
1622 if self.applied:
1622 if self.applied:
1623 base = repo.changelog.node(rev[0])
1623 base = repo.changelog.node(rev[0])
1624 if base in [n.node for n in self.applied]:
1624 if base in [n.node for n in self.applied]:
1625 raise util.Abort(_('revision %d is already managed')
1625 raise util.Abort(_('revision %d is already managed')
1626 % rev[0])
1626 % rev[0])
1627 if heads != [self.applied[-1].node]:
1627 if heads != [self.applied[-1].node]:
1628 raise util.Abort(_('revision %d is not the parent of '
1628 raise util.Abort(_('revision %d is not the parent of '
1629 'the queue') % rev[0])
1629 'the queue') % rev[0])
1630 base = repo.changelog.rev(self.applied[0].node)
1630 base = repo.changelog.rev(self.applied[0].node)
1631 lastparent = repo.changelog.parentrevs(base)[0]
1631 lastparent = repo.changelog.parentrevs(base)[0]
1632 else:
1632 else:
1633 if heads != [repo.changelog.node(rev[0])]:
1633 if heads != [repo.changelog.node(rev[0])]:
1634 raise util.Abort(_('revision %d has unmanaged children')
1634 raise util.Abort(_('revision %d has unmanaged children')
1635 % rev[0])
1635 % rev[0])
1636 lastparent = None
1636 lastparent = None
1637
1637
1638 diffopts = self.diffopts({'git': git})
1638 diffopts = self.diffopts({'git': git})
1639 for r in rev:
1639 for r in rev:
1640 p1, p2 = repo.changelog.parentrevs(r)
1640 p1, p2 = repo.changelog.parentrevs(r)
1641 n = repo.changelog.node(r)
1641 n = repo.changelog.node(r)
1642 if p2 != nullrev:
1642 if p2 != nullrev:
1643 raise util.Abort(_('cannot import merge revision %d') % r)
1643 raise util.Abort(_('cannot import merge revision %d') % r)
1644 if lastparent and lastparent != r:
1644 if lastparent and lastparent != r:
1645 raise util.Abort(_('revision %d is not the parent of %d')
1645 raise util.Abort(_('revision %d is not the parent of %d')
1646 % (r, lastparent))
1646 % (r, lastparent))
1647 lastparent = p1
1647 lastparent = p1
1648
1648
1649 if not patchname:
1649 if not patchname:
1650 patchname = normname('%d.diff' % r)
1650 patchname = normname('%d.diff' % r)
1651 self.check_reserved_name(patchname)
1651 self.check_reserved_name(patchname)
1652 checkseries(patchname)
1652 checkseries(patchname)
1653 checkfile(patchname)
1653 checkfile(patchname)
1654 self.full_series.insert(0, patchname)
1654 self.full_series.insert(0, patchname)
1655
1655
1656 patchf = self.opener(patchname, "w")
1656 patchf = self.opener(patchname, "w")
1657 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1657 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1658 patchf.close()
1658 patchf.close()
1659
1659
1660 se = statusentry(n, patchname)
1660 se = statusentry(n, patchname)
1661 self.applied.insert(0, se)
1661 self.applied.insert(0, se)
1662
1662
1663 added.append(patchname)
1663 added.append(patchname)
1664 patchname = None
1664 patchname = None
1665 self.parse_series()
1665 self.parse_series()
1666 self.applied_dirty = 1
1666 self.applied_dirty = 1
1667
1667
1668 for i, filename in enumerate(files):
1668 for i, filename in enumerate(files):
1669 if existing:
1669 if existing:
1670 if filename == '-':
1670 if filename == '-':
1671 raise util.Abort(_('-e is incompatible with import from -'))
1671 raise util.Abort(_('-e is incompatible with import from -'))
1672 if not patchname:
1672 if not patchname:
1673 patchname = normname(filename)
1673 patchname = normname(filename)
1674 self.check_reserved_name(patchname)
1674 self.check_reserved_name(patchname)
1675 if not os.path.isfile(self.join(patchname)):
1675 if not os.path.isfile(self.join(patchname)):
1676 raise util.Abort(_("patch %s does not exist") % patchname)
1676 raise util.Abort(_("patch %s does not exist") % patchname)
1677 else:
1677 else:
1678 try:
1678 try:
1679 if filename == '-':
1679 if filename == '-':
1680 if not patchname:
1680 if not patchname:
1681 raise util.Abort(
1681 raise util.Abort(
1682 _('need --name to import a patch from -'))
1682 _('need --name to import a patch from -'))
1683 text = sys.stdin.read()
1683 text = sys.stdin.read()
1684 else:
1684 else:
1685 text = url.open(self.ui, filename).read()
1685 text = url.open(self.ui, filename).read()
1686 except (OSError, IOError):
1686 except (OSError, IOError):
1687 raise util.Abort(_("unable to read %s") % filename)
1687 raise util.Abort(_("unable to read %s") % filename)
1688 if not patchname:
1688 if not patchname:
1689 patchname = normname(os.path.basename(filename))
1689 patchname = normname(os.path.basename(filename))
1690 self.check_reserved_name(patchname)
1690 self.check_reserved_name(patchname)
1691 checkfile(patchname)
1691 checkfile(patchname)
1692 patchf = self.opener(patchname, "w")
1692 patchf = self.opener(patchname, "w")
1693 patchf.write(text)
1693 patchf.write(text)
1694 if not force:
1694 if not force:
1695 checkseries(patchname)
1695 checkseries(patchname)
1696 if patchname not in self.series:
1696 if patchname not in self.series:
1697 index = self.full_series_end() + i
1697 index = self.full_series_end() + i
1698 self.full_series[index:index] = [patchname]
1698 self.full_series[index:index] = [patchname]
1699 self.parse_series()
1699 self.parse_series()
1700 self.ui.warn(_("adding %s to series file\n") % patchname)
1700 self.ui.warn(_("adding %s to series file\n") % patchname)
1701 added.append(patchname)
1701 added.append(patchname)
1702 patchname = None
1702 patchname = None
1703 self.series_dirty = 1
1703 self.series_dirty = 1
1704 qrepo = self.qrepo()
1704 qrepo = self.qrepo()
1705 if qrepo:
1705 if qrepo:
1706 qrepo.add(added)
1706 qrepo.add(added)
1707
1707
1708 def delete(ui, repo, *patches, **opts):
1708 def delete(ui, repo, *patches, **opts):
1709 """remove patches from queue
1709 """remove patches from queue
1710
1710
1711 The patches must not be applied, and at least one patch is required. With
1711 The patches must not be applied, and at least one patch is required. With
1712 -k/--keep, the patch files are preserved in the patch directory.
1712 -k/--keep, the patch files are preserved in the patch directory.
1713
1713
1714 To stop managing a patch and move it into permanent history,
1714 To stop managing a patch and move it into permanent history,
1715 use the qfinish command."""
1715 use the qfinish command."""
1716 q = repo.mq
1716 q = repo.mq
1717 q.delete(repo, patches, opts)
1717 q.delete(repo, patches, opts)
1718 q.save_dirty()
1718 q.save_dirty()
1719 return 0
1719 return 0
1720
1720
1721 def applied(ui, repo, patch=None, **opts):
1721 def applied(ui, repo, patch=None, **opts):
1722 """print the patches already applied"""
1722 """print the patches already applied"""
1723
1723
1724 q = repo.mq
1724 q = repo.mq
1725 l = len(q.applied)
1725 l = len(q.applied)
1726
1726
1727 if patch:
1727 if patch:
1728 if patch not in q.series:
1728 if patch not in q.series:
1729 raise util.Abort(_("patch %s is not in series file") % patch)
1729 raise util.Abort(_("patch %s is not in series file") % patch)
1730 end = q.series.index(patch) + 1
1730 end = q.series.index(patch) + 1
1731 else:
1731 else:
1732 end = q.series_end(True)
1732 end = q.series_end(True)
1733
1733
1734 if opts.get('last') and not end:
1734 if opts.get('last') and not end:
1735 ui.write(_("no patches applied\n"))
1735 ui.write(_("no patches applied\n"))
1736 return 1
1736 return 1
1737 elif opts.get('last') and end == 1:
1737 elif opts.get('last') and end == 1:
1738 ui.write(_("only one patch applied\n"))
1738 ui.write(_("only one patch applied\n"))
1739 return 1
1739 return 1
1740 elif opts.get('last'):
1740 elif opts.get('last'):
1741 start = end - 2
1741 start = end - 2
1742 end = 1
1742 end = 1
1743 else:
1743 else:
1744 start = 0
1744 start = 0
1745
1745
1746 return q.qseries(repo, length=end, start=start, status='A',
1746 return q.qseries(repo, length=end, start=start, status='A',
1747 summary=opts.get('summary'))
1747 summary=opts.get('summary'))
1748
1748
1749 def unapplied(ui, repo, patch=None, **opts):
1749 def unapplied(ui, repo, patch=None, **opts):
1750 """print the patches not yet applied"""
1750 """print the patches not yet applied"""
1751
1751
1752 q = repo.mq
1752 q = repo.mq
1753 if patch:
1753 if patch:
1754 if patch not in q.series:
1754 if patch not in q.series:
1755 raise util.Abort(_("patch %s is not in series file") % patch)
1755 raise util.Abort(_("patch %s is not in series file") % patch)
1756 start = q.series.index(patch) + 1
1756 start = q.series.index(patch) + 1
1757 else:
1757 else:
1758 start = q.series_end(True)
1758 start = q.series_end(True)
1759
1759
1760 if start == len(q.series) and opts.get('first'):
1760 if start == len(q.series) and opts.get('first'):
1761 ui.write(_("all patches applied\n"))
1761 ui.write(_("all patches applied\n"))
1762 return 1
1762 return 1
1763
1763
1764 length = opts.get('first') and 1 or None
1764 length = opts.get('first') and 1 or None
1765 return q.qseries(repo, start=start, length=length, status='U',
1765 return q.qseries(repo, start=start, length=length, status='U',
1766 summary=opts.get('summary'))
1766 summary=opts.get('summary'))
1767
1767
1768 def qimport(ui, repo, *filename, **opts):
1768 def qimport(ui, repo, *filename, **opts):
1769 """import a patch
1769 """import a patch
1770
1770
1771 The patch is inserted into the series after the last applied
1771 The patch is inserted into the series after the last applied
1772 patch. If no patches have been applied, qimport prepends the patch
1772 patch. If no patches have been applied, qimport prepends the patch
1773 to the series.
1773 to the series.
1774
1774
1775 The patch will have the same name as its source file unless you
1775 The patch will have the same name as its source file unless you
1776 give it a new one with -n/--name.
1776 give it a new one with -n/--name.
1777
1777
1778 You can register an existing patch inside the patch directory with
1778 You can register an existing patch inside the patch directory with
1779 the -e/--existing flag.
1779 the -e/--existing flag.
1780
1780
1781 With -f/--force, an existing patch of the same name will be
1781 With -f/--force, an existing patch of the same name will be
1782 overwritten.
1782 overwritten.
1783
1783
1784 An existing changeset may be placed under mq control with -r/--rev
1784 An existing changeset may be placed under mq control with -r/--rev
1785 (e.g. qimport --rev tip -n patch will place tip under mq control).
1785 (e.g. qimport --rev tip -n patch will place tip under mq control).
1786 With -g/--git, patches imported with --rev will use the git diff
1786 With -g/--git, patches imported with --rev will use the git diff
1787 format. See the diffs help topic for information on why this is
1787 format. See the diffs help topic for information on why this is
1788 important for preserving rename/copy information and permission
1788 important for preserving rename/copy information and permission
1789 changes.
1789 changes.
1790
1790
1791 To import a patch from standard input, pass - as the patch file.
1791 To import a patch from standard input, pass - as the patch file.
1792 When importing from standard input, a patch name must be specified
1792 When importing from standard input, a patch name must be specified
1793 using the --name flag.
1793 using the --name flag.
1794 """
1794 """
1795 q = repo.mq
1795 q = repo.mq
1796 q.qimport(repo, filename, patchname=opts['name'],
1796 q.qimport(repo, filename, patchname=opts['name'],
1797 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1797 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1798 git=opts['git'])
1798 git=opts['git'])
1799 q.save_dirty()
1799 q.save_dirty()
1800
1800
1801 if opts.get('push') and not opts.get('rev'):
1801 if opts.get('push') and not opts.get('rev'):
1802 return q.push(repo, None)
1802 return q.push(repo, None)
1803 return 0
1803 return 0
1804
1804
1805 def qinit(ui, repo, create):
1805 def qinit(ui, repo, create):
1806 """initialize a new queue repository
1806 """initialize a new queue repository
1807
1807
1808 This command also creates a series file for ordering patches, and
1808 This command also creates a series file for ordering patches, and
1809 an mq-specific .hgignore file in the queue repository, to exclude
1809 an mq-specific .hgignore file in the queue repository, to exclude
1810 the status and guards files (these contain mostly transient state)."""
1810 the status and guards files (these contain mostly transient state)."""
1811 q = repo.mq
1811 q = repo.mq
1812 r = q.init(repo, create)
1812 r = q.init(repo, create)
1813 q.save_dirty()
1813 q.save_dirty()
1814 if r:
1814 if r:
1815 if not os.path.exists(r.wjoin('.hgignore')):
1815 if not os.path.exists(r.wjoin('.hgignore')):
1816 fp = r.wopener('.hgignore', 'w')
1816 fp = r.wopener('.hgignore', 'w')
1817 fp.write('^\\.hg\n')
1817 fp.write('^\\.hg\n')
1818 fp.write('^\\.mq\n')
1818 fp.write('^\\.mq\n')
1819 fp.write('syntax: glob\n')
1819 fp.write('syntax: glob\n')
1820 fp.write('status\n')
1820 fp.write('status\n')
1821 fp.write('guards\n')
1821 fp.write('guards\n')
1822 fp.close()
1822 fp.close()
1823 if not os.path.exists(r.wjoin('series')):
1823 if not os.path.exists(r.wjoin('series')):
1824 r.wopener('series', 'w').close()
1824 r.wopener('series', 'w').close()
1825 r.add(['.hgignore', 'series'])
1825 r.add(['.hgignore', 'series'])
1826 commands.add(ui, r)
1826 commands.add(ui, r)
1827 return 0
1827 return 0
1828
1828
1829 def init(ui, repo, **opts):
1829 def init(ui, repo, **opts):
1830 """init a new queue repository (DEPRECATED)
1830 """init a new queue repository (DEPRECATED)
1831
1831
1832 The queue repository is unversioned by default. If
1832 The queue repository is unversioned by default. If
1833 -c/--create-repo is specified, qinit will create a separate nested
1833 -c/--create-repo is specified, qinit will create a separate nested
1834 repository for patches (qinit -c may also be run later to convert
1834 repository for patches (qinit -c may also be run later to convert
1835 an unversioned patch repository into a versioned one). You can use
1835 an unversioned patch repository into a versioned one). You can use
1836 qcommit to commit changes to this queue repository.
1836 qcommit to commit changes to this queue repository.
1837
1837
1838 This command is deprecated. Without -c, it's implied by other relevant
1838 This command is deprecated. Without -c, it's implied by other relevant
1839 commands. With -c, use hg init --mq instead."""
1839 commands. With -c, use hg init --mq instead."""
1840 return qinit(ui, repo, create=opts['create_repo'])
1840 return qinit(ui, repo, create=opts['create_repo'])
1841
1841
1842 def clone(ui, source, dest=None, **opts):
1842 def clone(ui, source, dest=None, **opts):
1843 '''clone main and patch repository at same time
1843 '''clone main and patch repository at same time
1844
1844
1845 If source is local, destination will have no patches applied. If
1845 If source is local, destination will have no patches applied. If
1846 source is remote, this command can not check if patches are
1846 source is remote, this command can not check if patches are
1847 applied in source, so cannot guarantee that patches are not
1847 applied in source, so cannot guarantee that patches are not
1848 applied in destination. If you clone remote repository, be sure
1848 applied in destination. If you clone remote repository, be sure
1849 before that it has no patches applied.
1849 before that it has no patches applied.
1850
1850
1851 Source patch repository is looked for in <src>/.hg/patches by
1851 Source patch repository is looked for in <src>/.hg/patches by
1852 default. Use -p <url> to change.
1852 default. Use -p <url> to change.
1853
1853
1854 The patch directory must be a nested Mercurial repository, as
1854 The patch directory must be a nested Mercurial repository, as
1855 would be created by init --mq.
1855 would be created by init --mq.
1856 '''
1856 '''
1857 def patchdir(repo):
1857 def patchdir(repo):
1858 url = repo.url()
1858 url = repo.url()
1859 if url.endswith('/'):
1859 if url.endswith('/'):
1860 url = url[:-1]
1860 url = url[:-1]
1861 return url + '/.hg/patches'
1861 return url + '/.hg/patches'
1862 if dest is None:
1862 if dest is None:
1863 dest = hg.defaultdest(source)
1863 dest = hg.defaultdest(source)
1864 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1864 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1865 if opts['patches']:
1865 if opts['patches']:
1866 patchespath = ui.expandpath(opts['patches'])
1866 patchespath = ui.expandpath(opts['patches'])
1867 else:
1867 else:
1868 patchespath = patchdir(sr)
1868 patchespath = patchdir(sr)
1869 try:
1869 try:
1870 hg.repository(ui, patchespath)
1870 hg.repository(ui, patchespath)
1871 except error.RepoError:
1871 except error.RepoError:
1872 raise util.Abort(_('versioned patch repository not found'
1872 raise util.Abort(_('versioned patch repository not found'
1873 ' (see init --mq)'))
1873 ' (see init --mq)'))
1874 qbase, destrev = None, None
1874 qbase, destrev = None, None
1875 if sr.local():
1875 if sr.local():
1876 if sr.mq.applied:
1876 if sr.mq.applied:
1877 qbase = sr.mq.applied[0].node
1877 qbase = sr.mq.applied[0].node
1878 if not hg.islocal(dest):
1878 if not hg.islocal(dest):
1879 heads = set(sr.heads())
1879 heads = set(sr.heads())
1880 destrev = list(heads.difference(sr.heads(qbase)))
1880 destrev = list(heads.difference(sr.heads(qbase)))
1881 destrev.append(sr.changelog.parents(qbase)[0])
1881 destrev.append(sr.changelog.parents(qbase)[0])
1882 elif sr.capable('lookup'):
1882 elif sr.capable('lookup'):
1883 try:
1883 try:
1884 qbase = sr.lookup('qbase')
1884 qbase = sr.lookup('qbase')
1885 except error.RepoError:
1885 except error.RepoError:
1886 pass
1886 pass
1887 ui.note(_('cloning main repository\n'))
1887 ui.note(_('cloning main repository\n'))
1888 sr, dr = hg.clone(ui, sr.url(), dest,
1888 sr, dr = hg.clone(ui, sr.url(), dest,
1889 pull=opts['pull'],
1889 pull=opts['pull'],
1890 rev=destrev,
1890 rev=destrev,
1891 update=False,
1891 update=False,
1892 stream=opts['uncompressed'])
1892 stream=opts['uncompressed'])
1893 ui.note(_('cloning patch repository\n'))
1893 ui.note(_('cloning patch repository\n'))
1894 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1894 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1895 pull=opts['pull'], update=not opts['noupdate'],
1895 pull=opts['pull'], update=not opts['noupdate'],
1896 stream=opts['uncompressed'])
1896 stream=opts['uncompressed'])
1897 if dr.local():
1897 if dr.local():
1898 if qbase:
1898 if qbase:
1899 ui.note(_('stripping applied patches from destination '
1899 ui.note(_('stripping applied patches from destination '
1900 'repository\n'))
1900 'repository\n'))
1901 dr.mq.strip(dr, qbase, update=False, backup=None)
1901 dr.mq.strip(dr, qbase, update=False, backup=None)
1902 if not opts['noupdate']:
1902 if not opts['noupdate']:
1903 ui.note(_('updating destination repository\n'))
1903 ui.note(_('updating destination repository\n'))
1904 hg.update(dr, dr.changelog.tip())
1904 hg.update(dr, dr.changelog.tip())
1905
1905
1906 def commit(ui, repo, *pats, **opts):
1906 def commit(ui, repo, *pats, **opts):
1907 """commit changes in the queue repository (DEPRECATED)
1907 """commit changes in the queue repository (DEPRECATED)
1908
1908
1909 This command is deprecated; use hg commit --mq instead."""
1909 This command is deprecated; use hg commit --mq instead."""
1910 q = repo.mq
1910 q = repo.mq
1911 r = q.qrepo()
1911 r = q.qrepo()
1912 if not r:
1912 if not r:
1913 raise util.Abort('no queue repository')
1913 raise util.Abort('no queue repository')
1914 commands.commit(r.ui, r, *pats, **opts)
1914 commands.commit(r.ui, r, *pats, **opts)
1915
1915
1916 def series(ui, repo, **opts):
1916 def series(ui, repo, **opts):
1917 """print the entire series file"""
1917 """print the entire series file"""
1918 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1918 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1919 return 0
1919 return 0
1920
1920
1921 def top(ui, repo, **opts):
1921 def top(ui, repo, **opts):
1922 """print the name of the current patch"""
1922 """print the name of the current patch"""
1923 q = repo.mq
1923 q = repo.mq
1924 t = q.applied and q.series_end(True) or 0
1924 t = q.applied and q.series_end(True) or 0
1925 if t:
1925 if t:
1926 return q.qseries(repo, start=t - 1, length=1, status='A',
1926 return q.qseries(repo, start=t - 1, length=1, status='A',
1927 summary=opts.get('summary'))
1927 summary=opts.get('summary'))
1928 else:
1928 else:
1929 ui.write(_("no patches applied\n"))
1929 ui.write(_("no patches applied\n"))
1930 return 1
1930 return 1
1931
1931
1932 def next(ui, repo, **opts):
1932 def next(ui, repo, **opts):
1933 """print the name of the next patch"""
1933 """print the name of the next patch"""
1934 q = repo.mq
1934 q = repo.mq
1935 end = q.series_end()
1935 end = q.series_end()
1936 if end == len(q.series):
1936 if end == len(q.series):
1937 ui.write(_("all patches applied\n"))
1937 ui.write(_("all patches applied\n"))
1938 return 1
1938 return 1
1939 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1939 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1940
1940
1941 def prev(ui, repo, **opts):
1941 def prev(ui, repo, **opts):
1942 """print the name of the previous patch"""
1942 """print the name of the previous patch"""
1943 q = repo.mq
1943 q = repo.mq
1944 l = len(q.applied)
1944 l = len(q.applied)
1945 if l == 1:
1945 if l == 1:
1946 ui.write(_("only one patch applied\n"))
1946 ui.write(_("only one patch applied\n"))
1947 return 1
1947 return 1
1948 if not l:
1948 if not l:
1949 ui.write(_("no patches applied\n"))
1949 ui.write(_("no patches applied\n"))
1950 return 1
1950 return 1
1951 return q.qseries(repo, start=l - 2, length=1, status='A',
1951 return q.qseries(repo, start=l - 2, length=1, status='A',
1952 summary=opts.get('summary'))
1952 summary=opts.get('summary'))
1953
1953
1954 def setupheaderopts(ui, opts):
1954 def setupheaderopts(ui, opts):
1955 if not opts.get('user') and opts.get('currentuser'):
1955 if not opts.get('user') and opts.get('currentuser'):
1956 opts['user'] = ui.username()
1956 opts['user'] = ui.username()
1957 if not opts.get('date') and opts.get('currentdate'):
1957 if not opts.get('date') and opts.get('currentdate'):
1958 opts['date'] = "%d %d" % util.makedate()
1958 opts['date'] = "%d %d" % util.makedate()
1959
1959
1960 def new(ui, repo, patch, *args, **opts):
1960 def new(ui, repo, patch, *args, **opts):
1961 """create a new patch
1961 """create a new patch
1962
1962
1963 qnew creates a new patch on top of the currently-applied patch (if
1963 qnew creates a new patch on top of the currently-applied patch (if
1964 any). The patch will be initialized with any outstanding changes
1964 any). The patch will be initialized with any outstanding changes
1965 in the working directory. You may also use -I/--include,
1965 in the working directory. You may also use -I/--include,
1966 -X/--exclude, and/or a list of files after the patch name to add
1966 -X/--exclude, and/or a list of files after the patch name to add
1967 only changes to matching files to the new patch, leaving the rest
1967 only changes to matching files to the new patch, leaving the rest
1968 as uncommitted modifications.
1968 as uncommitted modifications.
1969
1969
1970 -u/--user and -d/--date can be used to set the (given) user and
1970 -u/--user and -d/--date can be used to set the (given) user and
1971 date, respectively. -U/--currentuser and -D/--currentdate set user
1971 date, respectively. -U/--currentuser and -D/--currentdate set user
1972 to current user and date to current date.
1972 to current user and date to current date.
1973
1973
1974 -e/--edit, -m/--message or -l/--logfile set the patch header as
1974 -e/--edit, -m/--message or -l/--logfile set the patch header as
1975 well as the commit message. If none is specified, the header is
1975 well as the commit message. If none is specified, the header is
1976 empty and the commit message is '[mq]: PATCH'.
1976 empty and the commit message is '[mq]: PATCH'.
1977
1977
1978 Use the -g/--git option to keep the patch in the git extended diff
1978 Use the -g/--git option to keep the patch in the git extended diff
1979 format. Read the diffs help topic for more information on why this
1979 format. Read the diffs help topic for more information on why this
1980 is important for preserving permission changes and copy/rename
1980 is important for preserving permission changes and copy/rename
1981 information.
1981 information.
1982 """
1982 """
1983 msg = cmdutil.logmessage(opts)
1983 msg = cmdutil.logmessage(opts)
1984 def getmsg():
1984 def getmsg():
1985 return ui.edit(msg, ui.username())
1985 return ui.edit(msg, ui.username())
1986 q = repo.mq
1986 q = repo.mq
1987 opts['msg'] = msg
1987 opts['msg'] = msg
1988 if opts.get('edit'):
1988 if opts.get('edit'):
1989 opts['msg'] = getmsg
1989 opts['msg'] = getmsg
1990 else:
1990 else:
1991 opts['msg'] = msg
1991 opts['msg'] = msg
1992 setupheaderopts(ui, opts)
1992 setupheaderopts(ui, opts)
1993 q.new(repo, patch, *args, **opts)
1993 q.new(repo, patch, *args, **opts)
1994 q.save_dirty()
1994 q.save_dirty()
1995 return 0
1995 return 0
1996
1996
1997 def refresh(ui, repo, *pats, **opts):
1997 def refresh(ui, repo, *pats, **opts):
1998 """update the current patch
1998 """update the current patch
1999
1999
2000 If any file patterns are provided, the refreshed patch will
2000 If any file patterns are provided, the refreshed patch will
2001 contain only the modifications that match those patterns; the
2001 contain only the modifications that match those patterns; the
2002 remaining modifications will remain in the working directory.
2002 remaining modifications will remain in the working directory.
2003
2003
2004 If -s/--short is specified, files currently included in the patch
2004 If -s/--short is specified, files currently included in the patch
2005 will be refreshed just like matched files and remain in the patch.
2005 will be refreshed just like matched files and remain in the patch.
2006
2006
2007 hg add/remove/copy/rename work as usual, though you might want to
2007 hg add/remove/copy/rename work as usual, though you might want to
2008 use git-style patches (-g/--git or [diff] git=1) to track copies
2008 use git-style patches (-g/--git or [diff] git=1) to track copies
2009 and renames. See the diffs help topic for more information on the
2009 and renames. See the diffs help topic for more information on the
2010 git diff format.
2010 git diff format.
2011 """
2011 """
2012 q = repo.mq
2012 q = repo.mq
2013 message = cmdutil.logmessage(opts)
2013 message = cmdutil.logmessage(opts)
2014 if opts['edit']:
2014 if opts['edit']:
2015 if not q.applied:
2015 if not q.applied:
2016 ui.write(_("no patches applied\n"))
2016 ui.write(_("no patches applied\n"))
2017 return 1
2017 return 1
2018 if message:
2018 if message:
2019 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2019 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2020 patch = q.applied[-1].name
2020 patch = q.applied[-1].name
2021 ph = patchheader(q.join(patch), q.plainmode)
2021 ph = patchheader(q.join(patch), q.plainmode)
2022 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2022 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2023 setupheaderopts(ui, opts)
2023 setupheaderopts(ui, opts)
2024 ret = q.refresh(repo, pats, msg=message, **opts)
2024 ret = q.refresh(repo, pats, msg=message, **opts)
2025 q.save_dirty()
2025 q.save_dirty()
2026 return ret
2026 return ret
2027
2027
2028 def diff(ui, repo, *pats, **opts):
2028 def diff(ui, repo, *pats, **opts):
2029 """diff of the current patch and subsequent modifications
2029 """diff of the current patch and subsequent modifications
2030
2030
2031 Shows a diff which includes the current patch as well as any
2031 Shows a diff which includes the current patch as well as any
2032 changes which have been made in the working directory since the
2032 changes which have been made in the working directory since the
2033 last refresh (thus showing what the current patch would become
2033 last refresh (thus showing what the current patch would become
2034 after a qrefresh).
2034 after a qrefresh).
2035
2035
2036 Use :hg:`diff` if you only want to see the changes made since the
2036 Use :hg:`diff` if you only want to see the changes made since the
2037 last qrefresh, or :hg:`export qtip` if you want to see changes
2037 last qrefresh, or :hg:`export qtip` if you want to see changes
2038 made by the current patch without including changes made since the
2038 made by the current patch without including changes made since the
2039 qrefresh.
2039 qrefresh.
2040 """
2040 """
2041 repo.mq.diff(repo, pats, opts)
2041 repo.mq.diff(repo, pats, opts)
2042 return 0
2042 return 0
2043
2043
2044 def fold(ui, repo, *files, **opts):
2044 def fold(ui, repo, *files, **opts):
2045 """fold the named patches into the current patch
2045 """fold the named patches into the current patch
2046
2046
2047 Patches must not yet be applied. Each patch will be successively
2047 Patches must not yet be applied. Each patch will be successively
2048 applied to the current patch in the order given. If all the
2048 applied to the current patch in the order given. If all the
2049 patches apply successfully, the current patch will be refreshed
2049 patches apply successfully, the current patch will be refreshed
2050 with the new cumulative patch, and the folded patches will be
2050 with the new cumulative patch, and the folded patches will be
2051 deleted. With -k/--keep, the folded patch files will not be
2051 deleted. With -k/--keep, the folded patch files will not be
2052 removed afterwards.
2052 removed afterwards.
2053
2053
2054 The header for each folded patch will be concatenated with the
2054 The header for each folded patch will be concatenated with the
2055 current patch header, separated by a line of '* * *'."""
2055 current patch header, separated by a line of '* * *'."""
2056
2056
2057 q = repo.mq
2057 q = repo.mq
2058
2058
2059 if not files:
2059 if not files:
2060 raise util.Abort(_('qfold requires at least one patch name'))
2060 raise util.Abort(_('qfold requires at least one patch name'))
2061 if not q.check_toppatch(repo)[0]:
2061 if not q.check_toppatch(repo)[0]:
2062 raise util.Abort(_('No patches applied'))
2062 raise util.Abort(_('No patches applied'))
2063 q.check_localchanges(repo)
2063 q.check_localchanges(repo)
2064
2064
2065 message = cmdutil.logmessage(opts)
2065 message = cmdutil.logmessage(opts)
2066 if opts['edit']:
2066 if opts['edit']:
2067 if message:
2067 if message:
2068 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2068 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2069
2069
2070 parent = q.lookup('qtip')
2070 parent = q.lookup('qtip')
2071 patches = []
2071 patches = []
2072 messages = []
2072 messages = []
2073 for f in files:
2073 for f in files:
2074 p = q.lookup(f)
2074 p = q.lookup(f)
2075 if p in patches or p == parent:
2075 if p in patches or p == parent:
2076 ui.warn(_('Skipping already folded patch %s') % p)
2076 ui.warn(_('Skipping already folded patch %s') % p)
2077 if q.isapplied(p):
2077 if q.isapplied(p):
2078 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2078 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2079 patches.append(p)
2079 patches.append(p)
2080
2080
2081 for p in patches:
2081 for p in patches:
2082 if not message:
2082 if not message:
2083 ph = patchheader(q.join(p), q.plainmode)
2083 ph = patchheader(q.join(p), q.plainmode)
2084 if ph.message:
2084 if ph.message:
2085 messages.append(ph.message)
2085 messages.append(ph.message)
2086 pf = q.join(p)
2086 pf = q.join(p)
2087 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2087 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2088 if not patchsuccess:
2088 if not patchsuccess:
2089 raise util.Abort(_('Error folding patch %s') % p)
2089 raise util.Abort(_('Error folding patch %s') % p)
2090 patch.updatedir(ui, repo, files)
2090 patch.updatedir(ui, repo, files)
2091
2091
2092 if not message:
2092 if not message:
2093 ph = patchheader(q.join(parent), q.plainmode)
2093 ph = patchheader(q.join(parent), q.plainmode)
2094 message, user = ph.message, ph.user
2094 message, user = ph.message, ph.user
2095 for msg in messages:
2095 for msg in messages:
2096 message.append('* * *')
2096 message.append('* * *')
2097 message.extend(msg)
2097 message.extend(msg)
2098 message = '\n'.join(message)
2098 message = '\n'.join(message)
2099
2099
2100 if opts['edit']:
2100 if opts['edit']:
2101 message = ui.edit(message, user or ui.username())
2101 message = ui.edit(message, user or ui.username())
2102
2102
2103 diffopts = q.patchopts(q.diffopts(), *patches)
2103 diffopts = q.patchopts(q.diffopts(), *patches)
2104 q.refresh(repo, msg=message, git=diffopts.git)
2104 q.refresh(repo, msg=message, git=diffopts.git)
2105 q.delete(repo, patches, opts)
2105 q.delete(repo, patches, opts)
2106 q.save_dirty()
2106 q.save_dirty()
2107
2107
2108 def goto(ui, repo, patch, **opts):
2108 def goto(ui, repo, patch, **opts):
2109 '''push or pop patches until named patch is at top of stack'''
2109 '''push or pop patches until named patch is at top of stack'''
2110 q = repo.mq
2110 q = repo.mq
2111 patch = q.lookup(patch)
2111 patch = q.lookup(patch)
2112 if q.isapplied(patch):
2112 if q.isapplied(patch):
2113 ret = q.pop(repo, patch, force=opts['force'])
2113 ret = q.pop(repo, patch, force=opts['force'])
2114 else:
2114 else:
2115 ret = q.push(repo, patch, force=opts['force'])
2115 ret = q.push(repo, patch, force=opts['force'])
2116 q.save_dirty()
2116 q.save_dirty()
2117 return ret
2117 return ret
2118
2118
2119 def guard(ui, repo, *args, **opts):
2119 def guard(ui, repo, *args, **opts):
2120 '''set or print guards for a patch
2120 '''set or print guards for a patch
2121
2121
2122 Guards control whether a patch can be pushed. A patch with no
2122 Guards control whether a patch can be pushed. A patch with no
2123 guards is always pushed. A patch with a positive guard ("+foo") is
2123 guards is always pushed. A patch with a positive guard ("+foo") is
2124 pushed only if the qselect command has activated it. A patch with
2124 pushed only if the qselect command has activated it. A patch with
2125 a negative guard ("-foo") is never pushed if the qselect command
2125 a negative guard ("-foo") is never pushed if the qselect command
2126 has activated it.
2126 has activated it.
2127
2127
2128 With no arguments, print the currently active guards.
2128 With no arguments, print the currently active guards.
2129 With arguments, set guards for the named patch.
2129 With arguments, set guards for the named patch.
2130 NOTE: Specifying negative guards now requires '--'.
2130 NOTE: Specifying negative guards now requires '--'.
2131
2131
2132 To set guards on another patch::
2132 To set guards on another patch::
2133
2133
2134 hg qguard other.patch -- +2.6.17 -stable
2134 hg qguard other.patch -- +2.6.17 -stable
2135 '''
2135 '''
2136 def status(idx):
2136 def status(idx):
2137 guards = q.series_guards[idx] or ['unguarded']
2137 guards = q.series_guards[idx] or ['unguarded']
2138 ui.write('%s: ' % ui.label(q.series[idx], 'qguard.patch'))
2138 ui.write('%s: ' % ui.label(q.series[idx], 'qguard.patch'))
2139 for i, guard in enumerate(guards):
2139 for i, guard in enumerate(guards):
2140 if guard.startswith('+'):
2140 if guard.startswith('+'):
2141 ui.write(guard, label='qguard.positive')
2141 ui.write(guard, label='qguard.positive')
2142 elif guard.startswith('-'):
2142 elif guard.startswith('-'):
2143 ui.write(guard, label='qguard.negative')
2143 ui.write(guard, label='qguard.negative')
2144 else:
2144 else:
2145 ui.write(guard, label='qguard.unguarded')
2145 ui.write(guard, label='qguard.unguarded')
2146 if i != len(guards) - 1:
2146 if i != len(guards) - 1:
2147 ui.write(' ')
2147 ui.write(' ')
2148 ui.write('\n')
2148 ui.write('\n')
2149 q = repo.mq
2149 q = repo.mq
2150 patch = None
2150 patch = None
2151 args = list(args)
2151 args = list(args)
2152 if opts['list']:
2152 if opts['list']:
2153 if args or opts['none']:
2153 if args or opts['none']:
2154 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2154 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2155 for i in xrange(len(q.series)):
2155 for i in xrange(len(q.series)):
2156 status(i)
2156 status(i)
2157 return
2157 return
2158 if not args or args[0][0:1] in '-+':
2158 if not args or args[0][0:1] in '-+':
2159 if not q.applied:
2159 if not q.applied:
2160 raise util.Abort(_('no patches applied'))
2160 raise util.Abort(_('no patches applied'))
2161 patch = q.applied[-1].name
2161 patch = q.applied[-1].name
2162 if patch is None and args[0][0:1] not in '-+':
2162 if patch is None and args[0][0:1] not in '-+':
2163 patch = args.pop(0)
2163 patch = args.pop(0)
2164 if patch is None:
2164 if patch is None:
2165 raise util.Abort(_('no patch to work with'))
2165 raise util.Abort(_('no patch to work with'))
2166 if args or opts['none']:
2166 if args or opts['none']:
2167 idx = q.find_series(patch)
2167 idx = q.find_series(patch)
2168 if idx is None:
2168 if idx is None:
2169 raise util.Abort(_('no patch named %s') % patch)
2169 raise util.Abort(_('no patch named %s') % patch)
2170 q.set_guards(idx, args)
2170 q.set_guards(idx, args)
2171 q.save_dirty()
2171 q.save_dirty()
2172 else:
2172 else:
2173 status(q.series.index(q.lookup(patch)))
2173 status(q.series.index(q.lookup(patch)))
2174
2174
2175 def header(ui, repo, patch=None):
2175 def header(ui, repo, patch=None):
2176 """print the header of the topmost or specified patch"""
2176 """print the header of the topmost or specified patch"""
2177 q = repo.mq
2177 q = repo.mq
2178
2178
2179 if patch:
2179 if patch:
2180 patch = q.lookup(patch)
2180 patch = q.lookup(patch)
2181 else:
2181 else:
2182 if not q.applied:
2182 if not q.applied:
2183 ui.write(_('no patches applied\n'))
2183 ui.write(_('no patches applied\n'))
2184 return 1
2184 return 1
2185 patch = q.lookup('qtip')
2185 patch = q.lookup('qtip')
2186 ph = patchheader(q.join(patch), q.plainmode)
2186 ph = patchheader(q.join(patch), q.plainmode)
2187
2187
2188 ui.write('\n'.join(ph.message) + '\n')
2188 ui.write('\n'.join(ph.message) + '\n')
2189
2189
2190 def lastsavename(path):
2190 def lastsavename(path):
2191 (directory, base) = os.path.split(path)
2191 (directory, base) = os.path.split(path)
2192 names = os.listdir(directory)
2192 names = os.listdir(directory)
2193 namere = re.compile("%s.([0-9]+)" % base)
2193 namere = re.compile("%s.([0-9]+)" % base)
2194 maxindex = None
2194 maxindex = None
2195 maxname = None
2195 maxname = None
2196 for f in names:
2196 for f in names:
2197 m = namere.match(f)
2197 m = namere.match(f)
2198 if m:
2198 if m:
2199 index = int(m.group(1))
2199 index = int(m.group(1))
2200 if maxindex is None or index > maxindex:
2200 if maxindex is None or index > maxindex:
2201 maxindex = index
2201 maxindex = index
2202 maxname = f
2202 maxname = f
2203 if maxname:
2203 if maxname:
2204 return (os.path.join(directory, maxname), maxindex)
2204 return (os.path.join(directory, maxname), maxindex)
2205 return (None, None)
2205 return (None, None)
2206
2206
2207 def savename(path):
2207 def savename(path):
2208 (last, index) = lastsavename(path)
2208 (last, index) = lastsavename(path)
2209 if last is None:
2209 if last is None:
2210 index = 0
2210 index = 0
2211 newpath = path + ".%d" % (index + 1)
2211 newpath = path + ".%d" % (index + 1)
2212 return newpath
2212 return newpath
2213
2213
2214 def push(ui, repo, patch=None, **opts):
2214 def push(ui, repo, patch=None, **opts):
2215 """push the next patch onto the stack
2215 """push the next patch onto the stack
2216
2216
2217 When -f/--force is applied, all local changes in patched files
2217 When -f/--force is applied, all local changes in patched files
2218 will be lost.
2218 will be lost.
2219 """
2219 """
2220 q = repo.mq
2220 q = repo.mq
2221 mergeq = None
2221 mergeq = None
2222
2222
2223 if opts['merge']:
2223 if opts['merge']:
2224 if opts['name']:
2224 if opts['name']:
2225 newpath = repo.join(opts['name'])
2225 newpath = repo.join(opts['name'])
2226 else:
2226 else:
2227 newpath, i = lastsavename(q.path)
2227 newpath, i = lastsavename(q.path)
2228 if not newpath:
2228 if not newpath:
2229 ui.warn(_("no saved queues found, please use -n\n"))
2229 ui.warn(_("no saved queues found, please use -n\n"))
2230 return 1
2230 return 1
2231 mergeq = queue(ui, repo.join(""), newpath)
2231 mergeq = queue(ui, repo.join(""), newpath)
2232 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2232 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2233 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2233 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2234 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
2234 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
2235 return ret
2235 return ret
2236
2236
2237 def pop(ui, repo, patch=None, **opts):
2237 def pop(ui, repo, patch=None, **opts):
2238 """pop the current patch off the stack
2238 """pop the current patch off the stack
2239
2239
2240 By default, pops off the top of the patch stack. If given a patch
2240 By default, pops off the top of the patch stack. If given a patch
2241 name, keeps popping off patches until the named patch is at the
2241 name, keeps popping off patches until the named patch is at the
2242 top of the stack.
2242 top of the stack.
2243 """
2243 """
2244 localupdate = True
2244 localupdate = True
2245 if opts['name']:
2245 if opts['name']:
2246 q = queue(ui, repo.join(""), repo.join(opts['name']))
2246 q = queue(ui, repo.join(""), repo.join(opts['name']))
2247 ui.warn(_('using patch queue: %s\n') % q.path)
2247 ui.warn(_('using patch queue: %s\n') % q.path)
2248 localupdate = False
2248 localupdate = False
2249 else:
2249 else:
2250 q = repo.mq
2250 q = repo.mq
2251 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2251 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2252 all=opts['all'])
2252 all=opts['all'])
2253 q.save_dirty()
2253 q.save_dirty()
2254 return ret
2254 return ret
2255
2255
2256 def rename(ui, repo, patch, name=None, **opts):
2256 def rename(ui, repo, patch, name=None, **opts):
2257 """rename a patch
2257 """rename a patch
2258
2258
2259 With one argument, renames the current patch to PATCH1.
2259 With one argument, renames the current patch to PATCH1.
2260 With two arguments, renames PATCH1 to PATCH2."""
2260 With two arguments, renames PATCH1 to PATCH2."""
2261
2261
2262 q = repo.mq
2262 q = repo.mq
2263
2263
2264 if not name:
2264 if not name:
2265 name = patch
2265 name = patch
2266 patch = None
2266 patch = None
2267
2267
2268 if patch:
2268 if patch:
2269 patch = q.lookup(patch)
2269 patch = q.lookup(patch)
2270 else:
2270 else:
2271 if not q.applied:
2271 if not q.applied:
2272 ui.write(_('no patches applied\n'))
2272 ui.write(_('no patches applied\n'))
2273 return
2273 return
2274 patch = q.lookup('qtip')
2274 patch = q.lookup('qtip')
2275 absdest = q.join(name)
2275 absdest = q.join(name)
2276 if os.path.isdir(absdest):
2276 if os.path.isdir(absdest):
2277 name = normname(os.path.join(name, os.path.basename(patch)))
2277 name = normname(os.path.join(name, os.path.basename(patch)))
2278 absdest = q.join(name)
2278 absdest = q.join(name)
2279 if os.path.exists(absdest):
2279 if os.path.exists(absdest):
2280 raise util.Abort(_('%s already exists') % absdest)
2280 raise util.Abort(_('%s already exists') % absdest)
2281
2281
2282 if name in q.series:
2282 if name in q.series:
2283 raise util.Abort(
2283 raise util.Abort(
2284 _('A patch named %s already exists in the series file') % name)
2284 _('A patch named %s already exists in the series file') % name)
2285
2285
2286 ui.note(_('renaming %s to %s\n') % (patch, name))
2286 ui.note(_('renaming %s to %s\n') % (patch, name))
2287 i = q.find_series(patch)
2287 i = q.find_series(patch)
2288 guards = q.guard_re.findall(q.full_series[i])
2288 guards = q.guard_re.findall(q.full_series[i])
2289 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2289 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2290 q.parse_series()
2290 q.parse_series()
2291 q.series_dirty = 1
2291 q.series_dirty = 1
2292
2292
2293 info = q.isapplied(patch)
2293 info = q.isapplied(patch)
2294 if info:
2294 if info:
2295 q.applied[info[0]] = statusentry(info[1], name)
2295 q.applied[info[0]] = statusentry(info[1], name)
2296 q.applied_dirty = 1
2296 q.applied_dirty = 1
2297
2297
2298 util.rename(q.join(patch), absdest)
2298 util.rename(q.join(patch), absdest)
2299 r = q.qrepo()
2299 r = q.qrepo()
2300 if r:
2300 if r:
2301 wlock = r.wlock()
2301 wlock = r.wlock()
2302 try:
2302 try:
2303 if r.dirstate[patch] == 'a':
2303 if r.dirstate[patch] == 'a':
2304 r.dirstate.forget(patch)
2304 r.dirstate.forget(patch)
2305 r.dirstate.add(name)
2305 r.dirstate.add(name)
2306 else:
2306 else:
2307 if r.dirstate[name] == 'r':
2307 if r.dirstate[name] == 'r':
2308 r.undelete([name])
2308 r.undelete([name])
2309 r.copy(patch, name)
2309 r.copy(patch, name)
2310 r.remove([patch], False)
2310 r.remove([patch], False)
2311 finally:
2311 finally:
2312 wlock.release()
2312 wlock.release()
2313
2313
2314 q.save_dirty()
2314 q.save_dirty()
2315
2315
2316 def restore(ui, repo, rev, **opts):
2316 def restore(ui, repo, rev, **opts):
2317 """restore the queue state saved by a revision (DEPRECATED)
2317 """restore the queue state saved by a revision (DEPRECATED)
2318
2318
2319 This command is deprecated, use rebase --mq instead."""
2319 This command is deprecated, use rebase --mq instead."""
2320 rev = repo.lookup(rev)
2320 rev = repo.lookup(rev)
2321 q = repo.mq
2321 q = repo.mq
2322 q.restore(repo, rev, delete=opts['delete'],
2322 q.restore(repo, rev, delete=opts['delete'],
2323 qupdate=opts['update'])
2323 qupdate=opts['update'])
2324 q.save_dirty()
2324 q.save_dirty()
2325 return 0
2325 return 0
2326
2326
2327 def save(ui, repo, **opts):
2327 def save(ui, repo, **opts):
2328 """save current queue state (DEPRECATED)
2328 """save current queue state (DEPRECATED)
2329
2329
2330 This command is deprecated, use rebase --mq instead."""
2330 This command is deprecated, use rebase --mq instead."""
2331 q = repo.mq
2331 q = repo.mq
2332 message = cmdutil.logmessage(opts)
2332 message = cmdutil.logmessage(opts)
2333 ret = q.save(repo, msg=message)
2333 ret = q.save(repo, msg=message)
2334 if ret:
2334 if ret:
2335 return ret
2335 return ret
2336 q.save_dirty()
2336 q.save_dirty()
2337 if opts['copy']:
2337 if opts['copy']:
2338 path = q.path
2338 path = q.path
2339 if opts['name']:
2339 if opts['name']:
2340 newpath = os.path.join(q.basepath, opts['name'])
2340 newpath = os.path.join(q.basepath, opts['name'])
2341 if os.path.exists(newpath):
2341 if os.path.exists(newpath):
2342 if not os.path.isdir(newpath):
2342 if not os.path.isdir(newpath):
2343 raise util.Abort(_('destination %s exists and is not '
2343 raise util.Abort(_('destination %s exists and is not '
2344 'a directory') % newpath)
2344 'a directory') % newpath)
2345 if not opts['force']:
2345 if not opts['force']:
2346 raise util.Abort(_('destination %s exists, '
2346 raise util.Abort(_('destination %s exists, '
2347 'use -f to force') % newpath)
2347 'use -f to force') % newpath)
2348 else:
2348 else:
2349 newpath = savename(path)
2349 newpath = savename(path)
2350 ui.warn(_("copy %s to %s\n") % (path, newpath))
2350 ui.warn(_("copy %s to %s\n") % (path, newpath))
2351 util.copyfiles(path, newpath)
2351 util.copyfiles(path, newpath)
2352 if opts['empty']:
2352 if opts['empty']:
2353 try:
2353 try:
2354 os.unlink(q.join(q.status_path))
2354 os.unlink(q.join(q.status_path))
2355 except:
2355 except:
2356 pass
2356 pass
2357 return 0
2357 return 0
2358
2358
2359 def strip(ui, repo, rev, **opts):
2359 def strip(ui, repo, rev, **opts):
2360 """strip a revision and all its descendants from the repository
2360 """strip a changeset and all its descendants from the repository
2361
2362 The strip command removes all changesets whose local revision
2363 number is greater than or equal to REV, and then restores any
2364 changesets that are not descendants of REV. If the working
2365 directory has uncommitted changes, the operation is aborted unless
2366 the --force flag is supplied.
2361
2367
2362 If one of the working directory's parent revisions is stripped, the
2368 If a parent of the working directory is stripped, then the working
2363 working directory will be updated to the parent of the stripped
2369 directory will automatically be updated to the most recent
2364 revision.
2370 available ancestor of the stripped parent after the operation
2371 completes.
2372
2373 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2374 bundle (see ``hg help bundle`` and ``hg help unbundle``). They can
2375 be restored by running ``hg unbundle .hg/strip-backup/BUNDLE``,
2376 where BUNDLE is the bundle file created by the strip. Note that
2377 the local revision numbers will in general be different after the
2378 restore.
2379
2380 Use the --nobackup option to discard the backup bundle once the
2381 operation completes.
2365 """
2382 """
2366 backup = 'all'
2383 backup = 'all'
2367 if opts['backup']:
2384 if opts['backup']:
2368 backup = 'strip'
2385 backup = 'strip'
2369 elif opts['nobackup']:
2386 elif opts['nobackup']:
2370 backup = 'none'
2387 backup = 'none'
2371
2388
2372 rev = repo.lookup(rev)
2389 rev = repo.lookup(rev)
2373 p = repo.dirstate.parents()
2390 p = repo.dirstate.parents()
2374 cl = repo.changelog
2391 cl = repo.changelog
2375 update = True
2392 update = True
2376 if p[0] == nullid:
2393 if p[0] == nullid:
2377 update = False
2394 update = False
2378 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2395 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2379 update = False
2396 update = False
2380 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2397 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2381 update = False
2398 update = False
2382
2399
2383 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2400 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2384 return 0
2401 return 0
2385
2402
2386 def select(ui, repo, *args, **opts):
2403 def select(ui, repo, *args, **opts):
2387 '''set or print guarded patches to push
2404 '''set or print guarded patches to push
2388
2405
2389 Use the qguard command to set or print guards on patch, then use
2406 Use the qguard command to set or print guards on patch, then use
2390 qselect to tell mq which guards to use. A patch will be pushed if
2407 qselect to tell mq which guards to use. A patch will be pushed if
2391 it has no guards or any positive guards match the currently
2408 it has no guards or any positive guards match the currently
2392 selected guard, but will not be pushed if any negative guards
2409 selected guard, but will not be pushed if any negative guards
2393 match the current guard. For example::
2410 match the current guard. For example::
2394
2411
2395 qguard foo.patch -stable (negative guard)
2412 qguard foo.patch -stable (negative guard)
2396 qguard bar.patch +stable (positive guard)
2413 qguard bar.patch +stable (positive guard)
2397 qselect stable
2414 qselect stable
2398
2415
2399 This activates the "stable" guard. mq will skip foo.patch (because
2416 This activates the "stable" guard. mq will skip foo.patch (because
2400 it has a negative match) but push bar.patch (because it has a
2417 it has a negative match) but push bar.patch (because it has a
2401 positive match).
2418 positive match).
2402
2419
2403 With no arguments, prints the currently active guards.
2420 With no arguments, prints the currently active guards.
2404 With one argument, sets the active guard.
2421 With one argument, sets the active guard.
2405
2422
2406 Use -n/--none to deactivate guards (no other arguments needed).
2423 Use -n/--none to deactivate guards (no other arguments needed).
2407 When no guards are active, patches with positive guards are
2424 When no guards are active, patches with positive guards are
2408 skipped and patches with negative guards are pushed.
2425 skipped and patches with negative guards are pushed.
2409
2426
2410 qselect can change the guards on applied patches. It does not pop
2427 qselect can change the guards on applied patches. It does not pop
2411 guarded patches by default. Use --pop to pop back to the last
2428 guarded patches by default. Use --pop to pop back to the last
2412 applied patch that is not guarded. Use --reapply (which implies
2429 applied patch that is not guarded. Use --reapply (which implies
2413 --pop) to push back to the current patch afterwards, but skip
2430 --pop) to push back to the current patch afterwards, but skip
2414 guarded patches.
2431 guarded patches.
2415
2432
2416 Use -s/--series to print a list of all guards in the series file
2433 Use -s/--series to print a list of all guards in the series file
2417 (no other arguments needed). Use -v for more information.'''
2434 (no other arguments needed). Use -v for more information.'''
2418
2435
2419 q = repo.mq
2436 q = repo.mq
2420 guards = q.active()
2437 guards = q.active()
2421 if args or opts['none']:
2438 if args or opts['none']:
2422 old_unapplied = q.unapplied(repo)
2439 old_unapplied = q.unapplied(repo)
2423 old_guarded = [i for i in xrange(len(q.applied)) if
2440 old_guarded = [i for i in xrange(len(q.applied)) if
2424 not q.pushable(i)[0]]
2441 not q.pushable(i)[0]]
2425 q.set_active(args)
2442 q.set_active(args)
2426 q.save_dirty()
2443 q.save_dirty()
2427 if not args:
2444 if not args:
2428 ui.status(_('guards deactivated\n'))
2445 ui.status(_('guards deactivated\n'))
2429 if not opts['pop'] and not opts['reapply']:
2446 if not opts['pop'] and not opts['reapply']:
2430 unapplied = q.unapplied(repo)
2447 unapplied = q.unapplied(repo)
2431 guarded = [i for i in xrange(len(q.applied))
2448 guarded = [i for i in xrange(len(q.applied))
2432 if not q.pushable(i)[0]]
2449 if not q.pushable(i)[0]]
2433 if len(unapplied) != len(old_unapplied):
2450 if len(unapplied) != len(old_unapplied):
2434 ui.status(_('number of unguarded, unapplied patches has '
2451 ui.status(_('number of unguarded, unapplied patches has '
2435 'changed from %d to %d\n') %
2452 'changed from %d to %d\n') %
2436 (len(old_unapplied), len(unapplied)))
2453 (len(old_unapplied), len(unapplied)))
2437 if len(guarded) != len(old_guarded):
2454 if len(guarded) != len(old_guarded):
2438 ui.status(_('number of guarded, applied patches has changed '
2455 ui.status(_('number of guarded, applied patches has changed '
2439 'from %d to %d\n') %
2456 'from %d to %d\n') %
2440 (len(old_guarded), len(guarded)))
2457 (len(old_guarded), len(guarded)))
2441 elif opts['series']:
2458 elif opts['series']:
2442 guards = {}
2459 guards = {}
2443 noguards = 0
2460 noguards = 0
2444 for gs in q.series_guards:
2461 for gs in q.series_guards:
2445 if not gs:
2462 if not gs:
2446 noguards += 1
2463 noguards += 1
2447 for g in gs:
2464 for g in gs:
2448 guards.setdefault(g, 0)
2465 guards.setdefault(g, 0)
2449 guards[g] += 1
2466 guards[g] += 1
2450 if ui.verbose:
2467 if ui.verbose:
2451 guards['NONE'] = noguards
2468 guards['NONE'] = noguards
2452 guards = guards.items()
2469 guards = guards.items()
2453 guards.sort(key=lambda x: x[0][1:])
2470 guards.sort(key=lambda x: x[0][1:])
2454 if guards:
2471 if guards:
2455 ui.note(_('guards in series file:\n'))
2472 ui.note(_('guards in series file:\n'))
2456 for guard, count in guards:
2473 for guard, count in guards:
2457 ui.note('%2d ' % count)
2474 ui.note('%2d ' % count)
2458 ui.write(guard, '\n')
2475 ui.write(guard, '\n')
2459 else:
2476 else:
2460 ui.note(_('no guards in series file\n'))
2477 ui.note(_('no guards in series file\n'))
2461 else:
2478 else:
2462 if guards:
2479 if guards:
2463 ui.note(_('active guards:\n'))
2480 ui.note(_('active guards:\n'))
2464 for g in guards:
2481 for g in guards:
2465 ui.write(g, '\n')
2482 ui.write(g, '\n')
2466 else:
2483 else:
2467 ui.write(_('no active guards\n'))
2484 ui.write(_('no active guards\n'))
2468 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2485 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2469 popped = False
2486 popped = False
2470 if opts['pop'] or opts['reapply']:
2487 if opts['pop'] or opts['reapply']:
2471 for i in xrange(len(q.applied)):
2488 for i in xrange(len(q.applied)):
2472 pushable, reason = q.pushable(i)
2489 pushable, reason = q.pushable(i)
2473 if not pushable:
2490 if not pushable:
2474 ui.status(_('popping guarded patches\n'))
2491 ui.status(_('popping guarded patches\n'))
2475 popped = True
2492 popped = True
2476 if i == 0:
2493 if i == 0:
2477 q.pop(repo, all=True)
2494 q.pop(repo, all=True)
2478 else:
2495 else:
2479 q.pop(repo, i - 1)
2496 q.pop(repo, i - 1)
2480 break
2497 break
2481 if popped:
2498 if popped:
2482 try:
2499 try:
2483 if reapply:
2500 if reapply:
2484 ui.status(_('reapplying unguarded patches\n'))
2501 ui.status(_('reapplying unguarded patches\n'))
2485 q.push(repo, reapply)
2502 q.push(repo, reapply)
2486 finally:
2503 finally:
2487 q.save_dirty()
2504 q.save_dirty()
2488
2505
2489 def finish(ui, repo, *revrange, **opts):
2506 def finish(ui, repo, *revrange, **opts):
2490 """move applied patches into repository history
2507 """move applied patches into repository history
2491
2508
2492 Finishes the specified revisions (corresponding to applied
2509 Finishes the specified revisions (corresponding to applied
2493 patches) by moving them out of mq control into regular repository
2510 patches) by moving them out of mq control into regular repository
2494 history.
2511 history.
2495
2512
2496 Accepts a revision range or the -a/--applied option. If --applied
2513 Accepts a revision range or the -a/--applied option. If --applied
2497 is specified, all applied mq revisions are removed from mq
2514 is specified, all applied mq revisions are removed from mq
2498 control. Otherwise, the given revisions must be at the base of the
2515 control. Otherwise, the given revisions must be at the base of the
2499 stack of applied patches.
2516 stack of applied patches.
2500
2517
2501 This can be especially useful if your changes have been applied to
2518 This can be especially useful if your changes have been applied to
2502 an upstream repository, or if you are about to push your changes
2519 an upstream repository, or if you are about to push your changes
2503 to upstream.
2520 to upstream.
2504 """
2521 """
2505 if not opts['applied'] and not revrange:
2522 if not opts['applied'] and not revrange:
2506 raise util.Abort(_('no revisions specified'))
2523 raise util.Abort(_('no revisions specified'))
2507 elif opts['applied']:
2524 elif opts['applied']:
2508 revrange = ('qbase:qtip',) + revrange
2525 revrange = ('qbase:qtip',) + revrange
2509
2526
2510 q = repo.mq
2527 q = repo.mq
2511 if not q.applied:
2528 if not q.applied:
2512 ui.status(_('no patches applied\n'))
2529 ui.status(_('no patches applied\n'))
2513 return 0
2530 return 0
2514
2531
2515 revs = cmdutil.revrange(repo, revrange)
2532 revs = cmdutil.revrange(repo, revrange)
2516 q.finish(repo, revs)
2533 q.finish(repo, revs)
2517 q.save_dirty()
2534 q.save_dirty()
2518 return 0
2535 return 0
2519
2536
2520 def reposetup(ui, repo):
2537 def reposetup(ui, repo):
2521 class mqrepo(repo.__class__):
2538 class mqrepo(repo.__class__):
2522 @util.propertycache
2539 @util.propertycache
2523 def mq(self):
2540 def mq(self):
2524 return queue(self.ui, self.join(""))
2541 return queue(self.ui, self.join(""))
2525
2542
2526 def abort_if_wdir_patched(self, errmsg, force=False):
2543 def abort_if_wdir_patched(self, errmsg, force=False):
2527 if self.mq.applied and not force:
2544 if self.mq.applied and not force:
2528 parent = self.dirstate.parents()[0]
2545 parent = self.dirstate.parents()[0]
2529 if parent in [s.node for s in self.mq.applied]:
2546 if parent in [s.node for s in self.mq.applied]:
2530 raise util.Abort(errmsg)
2547 raise util.Abort(errmsg)
2531
2548
2532 def commit(self, text="", user=None, date=None, match=None,
2549 def commit(self, text="", user=None, date=None, match=None,
2533 force=False, editor=False, extra={}):
2550 force=False, editor=False, extra={}):
2534 self.abort_if_wdir_patched(
2551 self.abort_if_wdir_patched(
2535 _('cannot commit over an applied mq patch'),
2552 _('cannot commit over an applied mq patch'),
2536 force)
2553 force)
2537
2554
2538 return super(mqrepo, self).commit(text, user, date, match, force,
2555 return super(mqrepo, self).commit(text, user, date, match, force,
2539 editor, extra)
2556 editor, extra)
2540
2557
2541 def push(self, remote, force=False, revs=None):
2558 def push(self, remote, force=False, revs=None):
2542 if self.mq.applied and not force and not revs:
2559 if self.mq.applied and not force and not revs:
2543 raise util.Abort(_('source has mq patches applied'))
2560 raise util.Abort(_('source has mq patches applied'))
2544 return super(mqrepo, self).push(remote, force, revs)
2561 return super(mqrepo, self).push(remote, force, revs)
2545
2562
2546 def _findtags(self):
2563 def _findtags(self):
2547 '''augment tags from base class with patch tags'''
2564 '''augment tags from base class with patch tags'''
2548 result = super(mqrepo, self)._findtags()
2565 result = super(mqrepo, self)._findtags()
2549
2566
2550 q = self.mq
2567 q = self.mq
2551 if not q.applied:
2568 if not q.applied:
2552 return result
2569 return result
2553
2570
2554 mqtags = [(patch.node, patch.name) for patch in q.applied]
2571 mqtags = [(patch.node, patch.name) for patch in q.applied]
2555
2572
2556 if mqtags[-1][0] not in self.changelog.nodemap:
2573 if mqtags[-1][0] not in self.changelog.nodemap:
2557 self.ui.warn(_('mq status file refers to unknown node %s\n')
2574 self.ui.warn(_('mq status file refers to unknown node %s\n')
2558 % short(mqtags[-1][0]))
2575 % short(mqtags[-1][0]))
2559 return result
2576 return result
2560
2577
2561 mqtags.append((mqtags[-1][0], 'qtip'))
2578 mqtags.append((mqtags[-1][0], 'qtip'))
2562 mqtags.append((mqtags[0][0], 'qbase'))
2579 mqtags.append((mqtags[0][0], 'qbase'))
2563 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2580 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2564 tags = result[0]
2581 tags = result[0]
2565 for patch in mqtags:
2582 for patch in mqtags:
2566 if patch[1] in tags:
2583 if patch[1] in tags:
2567 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2584 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2568 % patch[1])
2585 % patch[1])
2569 else:
2586 else:
2570 tags[patch[1]] = patch[0]
2587 tags[patch[1]] = patch[0]
2571
2588
2572 return result
2589 return result
2573
2590
2574 def _branchtags(self, partial, lrev):
2591 def _branchtags(self, partial, lrev):
2575 q = self.mq
2592 q = self.mq
2576 if not q.applied:
2593 if not q.applied:
2577 return super(mqrepo, self)._branchtags(partial, lrev)
2594 return super(mqrepo, self)._branchtags(partial, lrev)
2578
2595
2579 cl = self.changelog
2596 cl = self.changelog
2580 qbasenode = q.applied[0].node
2597 qbasenode = q.applied[0].node
2581 if qbasenode not in cl.nodemap:
2598 if qbasenode not in cl.nodemap:
2582 self.ui.warn(_('mq status file refers to unknown node %s\n')
2599 self.ui.warn(_('mq status file refers to unknown node %s\n')
2583 % short(qbasenode))
2600 % short(qbasenode))
2584 return super(mqrepo, self)._branchtags(partial, lrev)
2601 return super(mqrepo, self)._branchtags(partial, lrev)
2585
2602
2586 qbase = cl.rev(qbasenode)
2603 qbase = cl.rev(qbasenode)
2587 start = lrev + 1
2604 start = lrev + 1
2588 if start < qbase:
2605 if start < qbase:
2589 # update the cache (excluding the patches) and save it
2606 # update the cache (excluding the patches) and save it
2590 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2607 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2591 self._updatebranchcache(partial, ctxgen)
2608 self._updatebranchcache(partial, ctxgen)
2592 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2609 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2593 start = qbase
2610 start = qbase
2594 # if start = qbase, the cache is as updated as it should be.
2611 # if start = qbase, the cache is as updated as it should be.
2595 # if start > qbase, the cache includes (part of) the patches.
2612 # if start > qbase, the cache includes (part of) the patches.
2596 # we might as well use it, but we won't save it.
2613 # we might as well use it, but we won't save it.
2597
2614
2598 # update the cache up to the tip
2615 # update the cache up to the tip
2599 ctxgen = (self[r] for r in xrange(start, len(cl)))
2616 ctxgen = (self[r] for r in xrange(start, len(cl)))
2600 self._updatebranchcache(partial, ctxgen)
2617 self._updatebranchcache(partial, ctxgen)
2601
2618
2602 return partial
2619 return partial
2603
2620
2604 if repo.local():
2621 if repo.local():
2605 repo.__class__ = mqrepo
2622 repo.__class__ = mqrepo
2606
2623
2607 def mqimport(orig, ui, repo, *args, **kwargs):
2624 def mqimport(orig, ui, repo, *args, **kwargs):
2608 if (hasattr(repo, 'abort_if_wdir_patched')
2625 if (hasattr(repo, 'abort_if_wdir_patched')
2609 and not kwargs.get('no_commit', False)):
2626 and not kwargs.get('no_commit', False)):
2610 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2627 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2611 kwargs.get('force'))
2628 kwargs.get('force'))
2612 return orig(ui, repo, *args, **kwargs)
2629 return orig(ui, repo, *args, **kwargs)
2613
2630
2614 def mqinit(orig, ui, *args, **kwargs):
2631 def mqinit(orig, ui, *args, **kwargs):
2615 mq = kwargs.pop('mq', None)
2632 mq = kwargs.pop('mq', None)
2616
2633
2617 if not mq:
2634 if not mq:
2618 return orig(ui, *args, **kwargs)
2635 return orig(ui, *args, **kwargs)
2619
2636
2620 if args:
2637 if args:
2621 repopath = args[0]
2638 repopath = args[0]
2622 if not hg.islocal(repopath):
2639 if not hg.islocal(repopath):
2623 raise util.Abort(_('only a local queue repository '
2640 raise util.Abort(_('only a local queue repository '
2624 'may be initialized'))
2641 'may be initialized'))
2625 else:
2642 else:
2626 repopath = cmdutil.findrepo(os.getcwd())
2643 repopath = cmdutil.findrepo(os.getcwd())
2627 if not repopath:
2644 if not repopath:
2628 raise util.Abort(_('There is no Mercurial repository here '
2645 raise util.Abort(_('There is no Mercurial repository here '
2629 '(.hg not found)'))
2646 '(.hg not found)'))
2630 repo = hg.repository(ui, repopath)
2647 repo = hg.repository(ui, repopath)
2631 return qinit(ui, repo, True)
2648 return qinit(ui, repo, True)
2632
2649
2633 def mqcommand(orig, ui, repo, *args, **kwargs):
2650 def mqcommand(orig, ui, repo, *args, **kwargs):
2634 """Add --mq option to operate on patch repository instead of main"""
2651 """Add --mq option to operate on patch repository instead of main"""
2635
2652
2636 # some commands do not like getting unknown options
2653 # some commands do not like getting unknown options
2637 mq = kwargs.pop('mq', None)
2654 mq = kwargs.pop('mq', None)
2638
2655
2639 if not mq:
2656 if not mq:
2640 return orig(ui, repo, *args, **kwargs)
2657 return orig(ui, repo, *args, **kwargs)
2641
2658
2642 q = repo.mq
2659 q = repo.mq
2643 r = q.qrepo()
2660 r = q.qrepo()
2644 if not r:
2661 if not r:
2645 raise util.Abort('no queue repository')
2662 raise util.Abort('no queue repository')
2646 return orig(r.ui, r, *args, **kwargs)
2663 return orig(r.ui, r, *args, **kwargs)
2647
2664
2648 def uisetup(ui):
2665 def uisetup(ui):
2649 mqopt = [('', 'mq', None, _("operate on patch repository"))]
2666 mqopt = [('', 'mq', None, _("operate on patch repository"))]
2650
2667
2651 extensions.wrapcommand(commands.table, 'import', mqimport)
2668 extensions.wrapcommand(commands.table, 'import', mqimport)
2652
2669
2653 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2670 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2654 entry[1].extend(mqopt)
2671 entry[1].extend(mqopt)
2655
2672
2656 norepo = commands.norepo.split(" ")
2673 norepo = commands.norepo.split(" ")
2657 for cmd in commands.table.keys():
2674 for cmd in commands.table.keys():
2658 cmd = cmdutil.parsealiases(cmd)[0]
2675 cmd = cmdutil.parsealiases(cmd)[0]
2659 if cmd in norepo:
2676 if cmd in norepo:
2660 continue
2677 continue
2661 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2678 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2662 entry[1].extend(mqopt)
2679 entry[1].extend(mqopt)
2663
2680
2664 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2681 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2665
2682
2666 cmdtable = {
2683 cmdtable = {
2667 "qapplied":
2684 "qapplied":
2668 (applied,
2685 (applied,
2669 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2686 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2670 _('hg qapplied [-1] [-s] [PATCH]')),
2687 _('hg qapplied [-1] [-s] [PATCH]')),
2671 "qclone":
2688 "qclone":
2672 (clone,
2689 (clone,
2673 [('', 'pull', None, _('use pull protocol to copy metadata')),
2690 [('', 'pull', None, _('use pull protocol to copy metadata')),
2674 ('U', 'noupdate', None, _('do not update the new working directories')),
2691 ('U', 'noupdate', None, _('do not update the new working directories')),
2675 ('', 'uncompressed', None,
2692 ('', 'uncompressed', None,
2676 _('use uncompressed transfer (fast over LAN)')),
2693 _('use uncompressed transfer (fast over LAN)')),
2677 ('p', 'patches', '', _('location of source patch repository')),
2694 ('p', 'patches', '', _('location of source patch repository')),
2678 ] + commands.remoteopts,
2695 ] + commands.remoteopts,
2679 _('hg qclone [OPTION]... SOURCE [DEST]')),
2696 _('hg qclone [OPTION]... SOURCE [DEST]')),
2680 "qcommit|qci":
2697 "qcommit|qci":
2681 (commit,
2698 (commit,
2682 commands.table["^commit|ci"][1],
2699 commands.table["^commit|ci"][1],
2683 _('hg qcommit [OPTION]... [FILE]...')),
2700 _('hg qcommit [OPTION]... [FILE]...')),
2684 "^qdiff":
2701 "^qdiff":
2685 (diff,
2702 (diff,
2686 commands.diffopts + commands.diffopts2 + commands.walkopts,
2703 commands.diffopts + commands.diffopts2 + commands.walkopts,
2687 _('hg qdiff [OPTION]... [FILE]...')),
2704 _('hg qdiff [OPTION]... [FILE]...')),
2688 "qdelete|qremove|qrm":
2705 "qdelete|qremove|qrm":
2689 (delete,
2706 (delete,
2690 [('k', 'keep', None, _('keep patch file')),
2707 [('k', 'keep', None, _('keep patch file')),
2691 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2708 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2692 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2709 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2693 'qfold':
2710 'qfold':
2694 (fold,
2711 (fold,
2695 [('e', 'edit', None, _('edit patch header')),
2712 [('e', 'edit', None, _('edit patch header')),
2696 ('k', 'keep', None, _('keep folded patch files')),
2713 ('k', 'keep', None, _('keep folded patch files')),
2697 ] + commands.commitopts,
2714 ] + commands.commitopts,
2698 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2715 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2699 'qgoto':
2716 'qgoto':
2700 (goto,
2717 (goto,
2701 [('f', 'force', None, _('overwrite any local changes'))],
2718 [('f', 'force', None, _('overwrite any local changes'))],
2702 _('hg qgoto [OPTION]... PATCH')),
2719 _('hg qgoto [OPTION]... PATCH')),
2703 'qguard':
2720 'qguard':
2704 (guard,
2721 (guard,
2705 [('l', 'list', None, _('list all patches and guards')),
2722 [('l', 'list', None, _('list all patches and guards')),
2706 ('n', 'none', None, _('drop all guards'))],
2723 ('n', 'none', None, _('drop all guards'))],
2707 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2724 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2708 'qheader': (header, [], _('hg qheader [PATCH]')),
2725 'qheader': (header, [], _('hg qheader [PATCH]')),
2709 "qimport":
2726 "qimport":
2710 (qimport,
2727 (qimport,
2711 [('e', 'existing', None, _('import file in patch directory')),
2728 [('e', 'existing', None, _('import file in patch directory')),
2712 ('n', 'name', '', _('name of patch file')),
2729 ('n', 'name', '', _('name of patch file')),
2713 ('f', 'force', None, _('overwrite existing files')),
2730 ('f', 'force', None, _('overwrite existing files')),
2714 ('r', 'rev', [], _('place existing revisions under mq control')),
2731 ('r', 'rev', [], _('place existing revisions under mq control')),
2715 ('g', 'git', None, _('use git extended diff format')),
2732 ('g', 'git', None, _('use git extended diff format')),
2716 ('P', 'push', None, _('qpush after importing'))],
2733 ('P', 'push', None, _('qpush after importing'))],
2717 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2734 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2718 "^qinit":
2735 "^qinit":
2719 (init,
2736 (init,
2720 [('c', 'create-repo', None, _('create queue repository'))],
2737 [('c', 'create-repo', None, _('create queue repository'))],
2721 _('hg qinit [-c]')),
2738 _('hg qinit [-c]')),
2722 "^qnew":
2739 "^qnew":
2723 (new,
2740 (new,
2724 [('e', 'edit', None, _('edit commit message')),
2741 [('e', 'edit', None, _('edit commit message')),
2725 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2742 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2726 ('g', 'git', None, _('use git extended diff format')),
2743 ('g', 'git', None, _('use git extended diff format')),
2727 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2744 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2728 ('u', 'user', '', _('add "From: <given user>" to patch')),
2745 ('u', 'user', '', _('add "From: <given user>" to patch')),
2729 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2746 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2730 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2747 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2731 ] + commands.walkopts + commands.commitopts,
2748 ] + commands.walkopts + commands.commitopts,
2732 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
2749 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
2733 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2750 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2734 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2751 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2735 "^qpop":
2752 "^qpop":
2736 (pop,
2753 (pop,
2737 [('a', 'all', None, _('pop all patches')),
2754 [('a', 'all', None, _('pop all patches')),
2738 ('n', 'name', '', _('queue name to pop (DEPRECATED)')),
2755 ('n', 'name', '', _('queue name to pop (DEPRECATED)')),
2739 ('f', 'force', None, _('forget any local changes to patched files'))],
2756 ('f', 'force', None, _('forget any local changes to patched files'))],
2740 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2757 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2741 "^qpush":
2758 "^qpush":
2742 (push,
2759 (push,
2743 [('f', 'force', None, _('apply if the patch has rejects')),
2760 [('f', 'force', None, _('apply if the patch has rejects')),
2744 ('l', 'list', None, _('list patch name in commit text')),
2761 ('l', 'list', None, _('list patch name in commit text')),
2745 ('a', 'all', None, _('apply all patches')),
2762 ('a', 'all', None, _('apply all patches')),
2746 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2763 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2747 ('n', 'name', '', _('merge queue name (DEPRECATED)')),
2764 ('n', 'name', '', _('merge queue name (DEPRECATED)')),
2748 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2765 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2749 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [--move] [PATCH | INDEX]')),
2766 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [--move] [PATCH | INDEX]')),
2750 "^qrefresh":
2767 "^qrefresh":
2751 (refresh,
2768 (refresh,
2752 [('e', 'edit', None, _('edit commit message')),
2769 [('e', 'edit', None, _('edit commit message')),
2753 ('g', 'git', None, _('use git extended diff format')),
2770 ('g', 'git', None, _('use git extended diff format')),
2754 ('s', 'short', None,
2771 ('s', 'short', None,
2755 _('refresh only files already in the patch and specified files')),
2772 _('refresh only files already in the patch and specified files')),
2756 ('U', 'currentuser', None,
2773 ('U', 'currentuser', None,
2757 _('add/update author field in patch with current user')),
2774 _('add/update author field in patch with current user')),
2758 ('u', 'user', '',
2775 ('u', 'user', '',
2759 _('add/update author field in patch with given user')),
2776 _('add/update author field in patch with given user')),
2760 ('D', 'currentdate', None,
2777 ('D', 'currentdate', None,
2761 _('add/update date field in patch with current date')),
2778 _('add/update date field in patch with current date')),
2762 ('d', 'date', '',
2779 ('d', 'date', '',
2763 _('add/update date field in patch with given date'))
2780 _('add/update date field in patch with given date'))
2764 ] + commands.walkopts + commands.commitopts,
2781 ] + commands.walkopts + commands.commitopts,
2765 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2782 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2766 'qrename|qmv':
2783 'qrename|qmv':
2767 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2784 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2768 "qrestore":
2785 "qrestore":
2769 (restore,
2786 (restore,
2770 [('d', 'delete', None, _('delete save entry')),
2787 [('d', 'delete', None, _('delete save entry')),
2771 ('u', 'update', None, _('update queue working directory'))],
2788 ('u', 'update', None, _('update queue working directory'))],
2772 _('hg qrestore [-d] [-u] REV')),
2789 _('hg qrestore [-d] [-u] REV')),
2773 "qsave":
2790 "qsave":
2774 (save,
2791 (save,
2775 [('c', 'copy', None, _('copy patch directory')),
2792 [('c', 'copy', None, _('copy patch directory')),
2776 ('n', 'name', '', _('copy directory name')),
2793 ('n', 'name', '', _('copy directory name')),
2777 ('e', 'empty', None, _('clear queue status file')),
2794 ('e', 'empty', None, _('clear queue status file')),
2778 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2795 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2779 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2796 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2780 "qselect":
2797 "qselect":
2781 (select,
2798 (select,
2782 [('n', 'none', None, _('disable all guards')),
2799 [('n', 'none', None, _('disable all guards')),
2783 ('s', 'series', None, _('list all guards in series file')),
2800 ('s', 'series', None, _('list all guards in series file')),
2784 ('', 'pop', None, _('pop to before first guarded applied patch')),
2801 ('', 'pop', None, _('pop to before first guarded applied patch')),
2785 ('', 'reapply', None, _('pop, then reapply patches'))],
2802 ('', 'reapply', None, _('pop, then reapply patches'))],
2786 _('hg qselect [OPTION]... [GUARD]...')),
2803 _('hg qselect [OPTION]... [GUARD]...')),
2787 "qseries":
2804 "qseries":
2788 (series,
2805 (series,
2789 [('m', 'missing', None, _('print patches not in series')),
2806 [('m', 'missing', None, _('print patches not in series')),
2790 ] + seriesopts,
2807 ] + seriesopts,
2791 _('hg qseries [-ms]')),
2808 _('hg qseries [-ms]')),
2792 "strip":
2809 "strip":
2793 (strip,
2810 (strip,
2794 [('f', 'force', None, _('force removal with local changes')),
2811 [('f', 'force', None, _('force removal of changesets even if the '
2795 ('b', 'backup', None, _('bundle unrelated changesets')),
2812 'working directory has uncommitted changes')),
2796 ('n', 'nobackup', None, _('no backups'))],
2813 ('b', 'backup', None, _('bundle only changesets with local revision'
2797 _('hg strip [-f] [-b] [-n] REV')),
2814 ' number greater than REV which are not'
2798 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2815 ' descendants of REV (DEPRECATED)')),
2816 ('n', 'nobackup', None, _('no backups'))],
2817 _('hg strip [-f] [-b] [-n] REV')),
2818 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2799 "qunapplied":
2819 "qunapplied":
2800 (unapplied,
2820 (unapplied,
2801 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2821 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2802 _('hg qunapplied [-1] [-s] [PATCH]')),
2822 _('hg qunapplied [-1] [-s] [PATCH]')),
2803 "qfinish":
2823 "qfinish":
2804 (finish,
2824 (finish,
2805 [('a', 'applied', None, _('finish all applied changesets'))],
2825 [('a', 'applied', None, _('finish all applied changesets'))],
2806 _('hg qfinish [-a] [REV]...')),
2826 _('hg qfinish [-a] [REV]...')),
2807 }
2827 }
2808
2828
2809 colortable = {'qguard.negative': 'red',
2829 colortable = {'qguard.negative': 'red',
2810 'qguard.positive': 'yellow',
2830 'qguard.positive': 'yellow',
2811 'qguard.unguarded': 'green',
2831 'qguard.unguarded': 'green',
2812 'qseries.applied': 'blue bold underline',
2832 'qseries.applied': 'blue bold underline',
2813 'qseries.guarded': 'black bold',
2833 'qseries.guarded': 'black bold',
2814 'qseries.missing': 'red bold',
2834 'qseries.missing': 'red bold',
2815 'qseries.unapplied': 'black bold'}
2835 'qseries.unapplied': 'black bold'}
@@ -1,3924 +1,3925 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 or any later version.
6 # GNU General Public License version 2 or any later version.
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, templatekw
13 import patch, help, mdiff, url, encoding, templatekw
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst
16 import minirst
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the
23 Schedule files to be version controlled and added to the
24 repository.
24 repository.
25
25
26 The files will be added to the repository at the next commit. To
26 The files will be added to the repository at the next commit. To
27 undo an add before that, see hg forget.
27 undo an add before that, see hg forget.
28
28
29 If no names are given, add all files to the repository.
29 If no names are given, add all files to the repository.
30
30
31 .. container:: verbose
31 .. container:: verbose
32
32
33 An example showing how new (unknown) files are added
33 An example showing how new (unknown) files are added
34 automatically by :hg:`add`::
34 automatically by :hg:`add`::
35
35
36 $ ls
36 $ ls
37 foo.c
37 foo.c
38 $ hg status
38 $ hg status
39 ? foo.c
39 ? foo.c
40 $ hg add
40 $ hg add
41 adding foo.c
41 adding foo.c
42 $ hg status
42 $ hg status
43 A foo.c
43 A foo.c
44 """
44 """
45
45
46 bad = []
46 bad = []
47 names = []
47 names = []
48 m = cmdutil.match(repo, pats, opts)
48 m = cmdutil.match(repo, pats, opts)
49 oldbad = m.bad
49 oldbad = m.bad
50 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
50 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
51
51
52 for f in repo.walk(m):
52 for f in repo.walk(m):
53 exact = m.exact(f)
53 exact = m.exact(f)
54 if exact or f not in repo.dirstate:
54 if exact or f not in repo.dirstate:
55 names.append(f)
55 names.append(f)
56 if ui.verbose or not exact:
56 if ui.verbose or not exact:
57 ui.status(_('adding %s\n') % m.rel(f))
57 ui.status(_('adding %s\n') % m.rel(f))
58 if not opts.get('dry_run'):
58 if not opts.get('dry_run'):
59 bad += [f for f in repo.add(names) if f in m.files()]
59 bad += [f for f in repo.add(names) if f in m.files()]
60 return bad and 1 or 0
60 return bad and 1 or 0
61
61
62 def addremove(ui, repo, *pats, **opts):
62 def addremove(ui, repo, *pats, **opts):
63 """add all new files, delete all missing files
63 """add all new files, delete all missing files
64
64
65 Add all new files and remove all missing files from the
65 Add all new files and remove all missing files from the
66 repository.
66 repository.
67
67
68 New files are ignored if they match any of the patterns in
68 New files are ignored if they match any of the patterns in
69 .hgignore. As with add, these changes take effect at the next
69 .hgignore. As with add, these changes take effect at the next
70 commit.
70 commit.
71
71
72 Use the -s/--similarity option to detect renamed files. With a
72 Use the -s/--similarity option to detect renamed files. With a
73 parameter greater than 0, this compares every removed file with
73 parameter greater than 0, this compares every removed file with
74 every added file and records those similar enough as renames. This
74 every added file and records those similar enough as renames. This
75 option takes a percentage between 0 (disabled) and 100 (files must
75 option takes a percentage between 0 (disabled) and 100 (files must
76 be identical) as its parameter. Detecting renamed files this way
76 be identical) as its parameter. Detecting renamed files this way
77 can be expensive.
77 can be expensive.
78 """
78 """
79 try:
79 try:
80 sim = float(opts.get('similarity') or 0)
80 sim = float(opts.get('similarity') or 0)
81 except ValueError:
81 except ValueError:
82 raise util.Abort(_('similarity must be a number'))
82 raise util.Abort(_('similarity must be a number'))
83 if sim < 0 or sim > 100:
83 if sim < 0 or sim > 100:
84 raise util.Abort(_('similarity must be between 0 and 100'))
84 raise util.Abort(_('similarity must be between 0 and 100'))
85 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
85 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
86
86
87 def annotate(ui, repo, *pats, **opts):
87 def annotate(ui, repo, *pats, **opts):
88 """show changeset information by line for each file
88 """show changeset information by line for each file
89
89
90 List changes in files, showing the revision id responsible for
90 List changes in files, showing the revision id responsible for
91 each line
91 each line
92
92
93 This command is useful for discovering when a change was made and
93 This command is useful for discovering when a change was made and
94 by whom.
94 by whom.
95
95
96 Without the -a/--text option, annotate will avoid processing files
96 Without the -a/--text option, annotate will avoid processing files
97 it detects as binary. With -a, annotate will annotate the file
97 it detects as binary. With -a, annotate will annotate the file
98 anyway, although the results will probably be neither useful
98 anyway, although the results will probably be neither useful
99 nor desirable.
99 nor desirable.
100 """
100 """
101 if opts.get('follow'):
101 if opts.get('follow'):
102 # --follow is deprecated and now just an alias for -f/--file
102 # --follow is deprecated and now just an alias for -f/--file
103 # to mimic the behavior of Mercurial before version 1.5
103 # to mimic the behavior of Mercurial before version 1.5
104 opts['file'] = 1
104 opts['file'] = 1
105
105
106 datefunc = ui.quiet and util.shortdate or util.datestr
106 datefunc = ui.quiet and util.shortdate or util.datestr
107 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
107 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
108
108
109 if not pats:
109 if not pats:
110 raise util.Abort(_('at least one filename or pattern is required'))
110 raise util.Abort(_('at least one filename or pattern is required'))
111
111
112 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
112 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
113 ('number', lambda x: str(x[0].rev())),
113 ('number', lambda x: str(x[0].rev())),
114 ('changeset', lambda x: short(x[0].node())),
114 ('changeset', lambda x: short(x[0].node())),
115 ('date', getdate),
115 ('date', getdate),
116 ('file', lambda x: x[0].path()),
116 ('file', lambda x: x[0].path()),
117 ]
117 ]
118
118
119 if (not opts.get('user') and not opts.get('changeset')
119 if (not opts.get('user') and not opts.get('changeset')
120 and not opts.get('date') and not opts.get('file')):
120 and not opts.get('date') and not opts.get('file')):
121 opts['number'] = 1
121 opts['number'] = 1
122
122
123 linenumber = opts.get('line_number') is not None
123 linenumber = opts.get('line_number') is not None
124 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
124 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
125 raise util.Abort(_('at least one of -n/-c is required for -l'))
125 raise util.Abort(_('at least one of -n/-c is required for -l'))
126
126
127 funcmap = [func for op, func in opmap if opts.get(op)]
127 funcmap = [func for op, func in opmap if opts.get(op)]
128 if linenumber:
128 if linenumber:
129 lastfunc = funcmap[-1]
129 lastfunc = funcmap[-1]
130 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
130 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
131
131
132 ctx = repo[opts.get('rev')]
132 ctx = repo[opts.get('rev')]
133 m = cmdutil.match(repo, pats, opts)
133 m = cmdutil.match(repo, pats, opts)
134 follow = not opts.get('no_follow')
134 follow = not opts.get('no_follow')
135 for abs in ctx.walk(m):
135 for abs in ctx.walk(m):
136 fctx = ctx[abs]
136 fctx = ctx[abs]
137 if not opts.get('text') and util.binary(fctx.data()):
137 if not opts.get('text') and util.binary(fctx.data()):
138 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
138 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
139 continue
139 continue
140
140
141 lines = fctx.annotate(follow=follow, linenumber=linenumber)
141 lines = fctx.annotate(follow=follow, linenumber=linenumber)
142 pieces = []
142 pieces = []
143
143
144 for f in funcmap:
144 for f in funcmap:
145 l = [f(n) for n, dummy in lines]
145 l = [f(n) for n, dummy in lines]
146 if l:
146 if l:
147 ml = max(map(len, l))
147 ml = max(map(len, l))
148 pieces.append(["%*s" % (ml, x) for x in l])
148 pieces.append(["%*s" % (ml, x) for x in l])
149
149
150 if pieces:
150 if pieces:
151 for p, l in zip(zip(*pieces), lines):
151 for p, l in zip(zip(*pieces), lines):
152 ui.write("%s: %s" % (" ".join(p), l[1]))
152 ui.write("%s: %s" % (" ".join(p), l[1]))
153
153
154 def archive(ui, repo, dest, **opts):
154 def archive(ui, repo, dest, **opts):
155 '''create an unversioned archive of a repository revision
155 '''create an unversioned archive of a repository revision
156
156
157 By default, the revision used is the parent of the working
157 By default, the revision used is the parent of the working
158 directory; use -r/--rev to specify a different revision.
158 directory; use -r/--rev to specify a different revision.
159
159
160 The archive type is automatically detected based on file
160 The archive type is automatically detected based on file
161 extension (or override using -t/--type).
161 extension (or override using -t/--type).
162
162
163 Valid types are:
163 Valid types are:
164
164
165 :``files``: a directory full of files (default)
165 :``files``: a directory full of files (default)
166 :``tar``: tar archive, uncompressed
166 :``tar``: tar archive, uncompressed
167 :``tbz2``: tar archive, compressed using bzip2
167 :``tbz2``: tar archive, compressed using bzip2
168 :``tgz``: tar archive, compressed using gzip
168 :``tgz``: tar archive, compressed using gzip
169 :``uzip``: zip archive, uncompressed
169 :``uzip``: zip archive, uncompressed
170 :``zip``: zip archive, compressed using deflate
170 :``zip``: zip archive, compressed using deflate
171
171
172 The exact name of the destination archive or directory is given
172 The exact name of the destination archive or directory is given
173 using a format string; see :hg:`help export` for details.
173 using a format string; see :hg:`help export` for details.
174
174
175 Each member added to an archive file has a directory prefix
175 Each member added to an archive file has a directory prefix
176 prepended. Use -p/--prefix to specify a format string for the
176 prepended. Use -p/--prefix to specify a format string for the
177 prefix. The default is the basename of the archive, with suffixes
177 prefix. The default is the basename of the archive, with suffixes
178 removed.
178 removed.
179 '''
179 '''
180
180
181 ctx = repo[opts.get('rev')]
181 ctx = repo[opts.get('rev')]
182 if not ctx:
182 if not ctx:
183 raise util.Abort(_('no working directory: please specify a revision'))
183 raise util.Abort(_('no working directory: please specify a revision'))
184 node = ctx.node()
184 node = ctx.node()
185 dest = cmdutil.make_filename(repo, dest, node)
185 dest = cmdutil.make_filename(repo, dest, node)
186 if os.path.realpath(dest) == repo.root:
186 if os.path.realpath(dest) == repo.root:
187 raise util.Abort(_('repository root cannot be destination'))
187 raise util.Abort(_('repository root cannot be destination'))
188
188
189 def guess_type():
189 def guess_type():
190 exttypes = {
190 exttypes = {
191 'tar': ['.tar'],
191 'tar': ['.tar'],
192 'tbz2': ['.tbz2', '.tar.bz2'],
192 'tbz2': ['.tbz2', '.tar.bz2'],
193 'tgz': ['.tgz', '.tar.gz'],
193 'tgz': ['.tgz', '.tar.gz'],
194 'zip': ['.zip'],
194 'zip': ['.zip'],
195 }
195 }
196
196
197 for type, extensions in exttypes.items():
197 for type, extensions in exttypes.items():
198 if util.any(dest.endswith(ext) for ext in extensions):
198 if util.any(dest.endswith(ext) for ext in extensions):
199 return type
199 return type
200 return None
200 return None
201
201
202 kind = opts.get('type') or guess_type() or 'files'
202 kind = opts.get('type') or guess_type() or 'files'
203 prefix = opts.get('prefix')
203 prefix = opts.get('prefix')
204
204
205 if dest == '-':
205 if dest == '-':
206 if kind == 'files':
206 if kind == 'files':
207 raise util.Abort(_('cannot archive plain files to stdout'))
207 raise util.Abort(_('cannot archive plain files to stdout'))
208 dest = sys.stdout
208 dest = sys.stdout
209 if not prefix:
209 if not prefix:
210 prefix = os.path.basename(repo.root) + '-%h'
210 prefix = os.path.basename(repo.root) + '-%h'
211
211
212 prefix = cmdutil.make_filename(repo, prefix, node)
212 prefix = cmdutil.make_filename(repo, prefix, node)
213 matchfn = cmdutil.match(repo, [], opts)
213 matchfn = cmdutil.match(repo, [], opts)
214 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
214 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
215 matchfn, prefix)
215 matchfn, prefix)
216
216
217 def backout(ui, repo, node=None, rev=None, **opts):
217 def backout(ui, repo, node=None, rev=None, **opts):
218 '''reverse effect of earlier changeset
218 '''reverse effect of earlier changeset
219
219
220 Commit the backed out changes as a new changeset. The new
220 Commit the backed out changes as a new changeset. The new
221 changeset is a child of the backed out changeset.
221 changeset is a child of the backed out changeset.
222
222
223 If you backout a changeset other than the tip, a new head is
223 If you backout a changeset other than the tip, a new head is
224 created. This head will be the new tip and you should merge this
224 created. This head will be the new tip and you should merge this
225 backout changeset with another head.
225 backout changeset with another head.
226
226
227 The --merge option remembers the parent of the working directory
227 The --merge option remembers the parent of the working directory
228 before starting the backout, then merges the new head with that
228 before starting the backout, then merges the new head with that
229 changeset afterwards. This saves you from doing the merge by hand.
229 changeset afterwards. This saves you from doing the merge by hand.
230 The result of this merge is not committed, as with a normal merge.
230 The result of this merge is not committed, as with a normal merge.
231
231
232 See :hg:`help dates` for a list of formats valid for -d/--date.
232 See :hg:`help dates` for a list of formats valid for -d/--date.
233 '''
233 '''
234 if rev and node:
234 if rev and node:
235 raise util.Abort(_("please specify just one revision"))
235 raise util.Abort(_("please specify just one revision"))
236
236
237 if not rev:
237 if not rev:
238 rev = node
238 rev = node
239
239
240 if not rev:
240 if not rev:
241 raise util.Abort(_("please specify a revision to backout"))
241 raise util.Abort(_("please specify a revision to backout"))
242
242
243 date = opts.get('date')
243 date = opts.get('date')
244 if date:
244 if date:
245 opts['date'] = util.parsedate(date)
245 opts['date'] = util.parsedate(date)
246
246
247 cmdutil.bail_if_changed(repo)
247 cmdutil.bail_if_changed(repo)
248 node = repo.lookup(rev)
248 node = repo.lookup(rev)
249
249
250 op1, op2 = repo.dirstate.parents()
250 op1, op2 = repo.dirstate.parents()
251 a = repo.changelog.ancestor(op1, node)
251 a = repo.changelog.ancestor(op1, node)
252 if a != node:
252 if a != node:
253 raise util.Abort(_('cannot backout change on a different branch'))
253 raise util.Abort(_('cannot backout change on a different branch'))
254
254
255 p1, p2 = repo.changelog.parents(node)
255 p1, p2 = repo.changelog.parents(node)
256 if p1 == nullid:
256 if p1 == nullid:
257 raise util.Abort(_('cannot backout a change with no parents'))
257 raise util.Abort(_('cannot backout a change with no parents'))
258 if p2 != nullid:
258 if p2 != nullid:
259 if not opts.get('parent'):
259 if not opts.get('parent'):
260 raise util.Abort(_('cannot backout a merge changeset without '
260 raise util.Abort(_('cannot backout a merge changeset without '
261 '--parent'))
261 '--parent'))
262 p = repo.lookup(opts['parent'])
262 p = repo.lookup(opts['parent'])
263 if p not in (p1, p2):
263 if p not in (p1, p2):
264 raise util.Abort(_('%s is not a parent of %s') %
264 raise util.Abort(_('%s is not a parent of %s') %
265 (short(p), short(node)))
265 (short(p), short(node)))
266 parent = p
266 parent = p
267 else:
267 else:
268 if opts.get('parent'):
268 if opts.get('parent'):
269 raise util.Abort(_('cannot use --parent on non-merge changeset'))
269 raise util.Abort(_('cannot use --parent on non-merge changeset'))
270 parent = p1
270 parent = p1
271
271
272 # the backout should appear on the same branch
272 # the backout should appear on the same branch
273 branch = repo.dirstate.branch()
273 branch = repo.dirstate.branch()
274 hg.clean(repo, node, show_stats=False)
274 hg.clean(repo, node, show_stats=False)
275 repo.dirstate.setbranch(branch)
275 repo.dirstate.setbranch(branch)
276 revert_opts = opts.copy()
276 revert_opts = opts.copy()
277 revert_opts['date'] = None
277 revert_opts['date'] = None
278 revert_opts['all'] = True
278 revert_opts['all'] = True
279 revert_opts['rev'] = hex(parent)
279 revert_opts['rev'] = hex(parent)
280 revert_opts['no_backup'] = None
280 revert_opts['no_backup'] = None
281 revert(ui, repo, **revert_opts)
281 revert(ui, repo, **revert_opts)
282 commit_opts = opts.copy()
282 commit_opts = opts.copy()
283 commit_opts['addremove'] = False
283 commit_opts['addremove'] = False
284 if not commit_opts['message'] and not commit_opts['logfile']:
284 if not commit_opts['message'] and not commit_opts['logfile']:
285 # we don't translate commit messages
285 # we don't translate commit messages
286 commit_opts['message'] = "Backed out changeset %s" % short(node)
286 commit_opts['message'] = "Backed out changeset %s" % short(node)
287 commit_opts['force_editor'] = True
287 commit_opts['force_editor'] = True
288 commit(ui, repo, **commit_opts)
288 commit(ui, repo, **commit_opts)
289 def nice(node):
289 def nice(node):
290 return '%d:%s' % (repo.changelog.rev(node), short(node))
290 return '%d:%s' % (repo.changelog.rev(node), short(node))
291 ui.status(_('changeset %s backs out changeset %s\n') %
291 ui.status(_('changeset %s backs out changeset %s\n') %
292 (nice(repo.changelog.tip()), nice(node)))
292 (nice(repo.changelog.tip()), nice(node)))
293 if op1 != node:
293 if op1 != node:
294 hg.clean(repo, op1, show_stats=False)
294 hg.clean(repo, op1, show_stats=False)
295 if opts.get('merge'):
295 if opts.get('merge'):
296 ui.status(_('merging with changeset %s\n')
296 ui.status(_('merging with changeset %s\n')
297 % nice(repo.changelog.tip()))
297 % nice(repo.changelog.tip()))
298 hg.merge(repo, hex(repo.changelog.tip()))
298 hg.merge(repo, hex(repo.changelog.tip()))
299 else:
299 else:
300 ui.status(_('the backout changeset is a new head - '
300 ui.status(_('the backout changeset is a new head - '
301 'do not forget to merge\n'))
301 'do not forget to merge\n'))
302 ui.status(_('(use "backout --merge" '
302 ui.status(_('(use "backout --merge" '
303 'if you want to auto-merge)\n'))
303 'if you want to auto-merge)\n'))
304
304
305 def bisect(ui, repo, rev=None, extra=None, command=None,
305 def bisect(ui, repo, rev=None, extra=None, command=None,
306 reset=None, good=None, bad=None, skip=None, noupdate=None):
306 reset=None, good=None, bad=None, skip=None, noupdate=None):
307 """subdivision search of changesets
307 """subdivision search of changesets
308
308
309 This command helps to find changesets which introduce problems. To
309 This command helps to find changesets which introduce problems. To
310 use, mark the earliest changeset you know exhibits the problem as
310 use, mark the earliest changeset you know exhibits the problem as
311 bad, then mark the latest changeset which is free from the problem
311 bad, then mark the latest changeset which is free from the problem
312 as good. Bisect will update your working directory to a revision
312 as good. Bisect will update your working directory to a revision
313 for testing (unless the -U/--noupdate option is specified). Once
313 for testing (unless the -U/--noupdate option is specified). Once
314 you have performed tests, mark the working directory as good or
314 you have performed tests, mark the working directory as good or
315 bad, and bisect will either update to another candidate changeset
315 bad, and bisect will either update to another candidate changeset
316 or announce that it has found the bad revision.
316 or announce that it has found the bad revision.
317
317
318 As a shortcut, you can also use the revision argument to mark a
318 As a shortcut, you can also use the revision argument to mark a
319 revision as good or bad without checking it out first.
319 revision as good or bad without checking it out first.
320
320
321 If you supply a command, it will be used for automatic bisection.
321 If you supply a command, it will be used for automatic bisection.
322 Its exit status will be used to mark revisions as good or bad:
322 Its exit status will be used to mark revisions as good or bad:
323 status 0 means good, 125 means to skip the revision, 127
323 status 0 means good, 125 means to skip the revision, 127
324 (command not found) will abort the bisection, and any other
324 (command not found) will abort the bisection, and any other
325 non-zero exit status means the revision is bad.
325 non-zero exit status means the revision is bad.
326 """
326 """
327 def print_result(nodes, good):
327 def print_result(nodes, good):
328 displayer = cmdutil.show_changeset(ui, repo, {})
328 displayer = cmdutil.show_changeset(ui, repo, {})
329 if len(nodes) == 1:
329 if len(nodes) == 1:
330 # narrowed it down to a single revision
330 # narrowed it down to a single revision
331 if good:
331 if good:
332 ui.write(_("The first good revision is:\n"))
332 ui.write(_("The first good revision is:\n"))
333 else:
333 else:
334 ui.write(_("The first bad revision is:\n"))
334 ui.write(_("The first bad revision is:\n"))
335 displayer.show(repo[nodes[0]])
335 displayer.show(repo[nodes[0]])
336 else:
336 else:
337 # multiple possible revisions
337 # multiple possible revisions
338 if good:
338 if good:
339 ui.write(_("Due to skipped revisions, the first "
339 ui.write(_("Due to skipped revisions, the first "
340 "good revision could be any of:\n"))
340 "good revision could be any of:\n"))
341 else:
341 else:
342 ui.write(_("Due to skipped revisions, the first "
342 ui.write(_("Due to skipped revisions, the first "
343 "bad revision could be any of:\n"))
343 "bad revision could be any of:\n"))
344 for n in nodes:
344 for n in nodes:
345 displayer.show(repo[n])
345 displayer.show(repo[n])
346 displayer.close()
346 displayer.close()
347
347
348 def check_state(state, interactive=True):
348 def check_state(state, interactive=True):
349 if not state['good'] or not state['bad']:
349 if not state['good'] or not state['bad']:
350 if (good or bad or skip or reset) and interactive:
350 if (good or bad or skip or reset) and interactive:
351 return
351 return
352 if not state['good']:
352 if not state['good']:
353 raise util.Abort(_('cannot bisect (no known good revisions)'))
353 raise util.Abort(_('cannot bisect (no known good revisions)'))
354 else:
354 else:
355 raise util.Abort(_('cannot bisect (no known bad revisions)'))
355 raise util.Abort(_('cannot bisect (no known bad revisions)'))
356 return True
356 return True
357
357
358 # backward compatibility
358 # backward compatibility
359 if rev in "good bad reset init".split():
359 if rev in "good bad reset init".split():
360 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
360 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
361 cmd, rev, extra = rev, extra, None
361 cmd, rev, extra = rev, extra, None
362 if cmd == "good":
362 if cmd == "good":
363 good = True
363 good = True
364 elif cmd == "bad":
364 elif cmd == "bad":
365 bad = True
365 bad = True
366 else:
366 else:
367 reset = True
367 reset = True
368 elif extra or good + bad + skip + reset + bool(command) > 1:
368 elif extra or good + bad + skip + reset + bool(command) > 1:
369 raise util.Abort(_('incompatible arguments'))
369 raise util.Abort(_('incompatible arguments'))
370
370
371 if reset:
371 if reset:
372 p = repo.join("bisect.state")
372 p = repo.join("bisect.state")
373 if os.path.exists(p):
373 if os.path.exists(p):
374 os.unlink(p)
374 os.unlink(p)
375 return
375 return
376
376
377 state = hbisect.load_state(repo)
377 state = hbisect.load_state(repo)
378
378
379 if command:
379 if command:
380 changesets = 1
380 changesets = 1
381 try:
381 try:
382 while changesets:
382 while changesets:
383 # update state
383 # update state
384 status = util.system(command)
384 status = util.system(command)
385 if status == 125:
385 if status == 125:
386 transition = "skip"
386 transition = "skip"
387 elif status == 0:
387 elif status == 0:
388 transition = "good"
388 transition = "good"
389 # status < 0 means process was killed
389 # status < 0 means process was killed
390 elif status == 127:
390 elif status == 127:
391 raise util.Abort(_("failed to execute %s") % command)
391 raise util.Abort(_("failed to execute %s") % command)
392 elif status < 0:
392 elif status < 0:
393 raise util.Abort(_("%s killed") % command)
393 raise util.Abort(_("%s killed") % command)
394 else:
394 else:
395 transition = "bad"
395 transition = "bad"
396 ctx = repo[rev or '.']
396 ctx = repo[rev or '.']
397 state[transition].append(ctx.node())
397 state[transition].append(ctx.node())
398 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
398 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
399 check_state(state, interactive=False)
399 check_state(state, interactive=False)
400 # bisect
400 # bisect
401 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
401 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
402 # update to next check
402 # update to next check
403 cmdutil.bail_if_changed(repo)
403 cmdutil.bail_if_changed(repo)
404 hg.clean(repo, nodes[0], show_stats=False)
404 hg.clean(repo, nodes[0], show_stats=False)
405 finally:
405 finally:
406 hbisect.save_state(repo, state)
406 hbisect.save_state(repo, state)
407 return print_result(nodes, good)
407 return print_result(nodes, good)
408
408
409 # update state
409 # update state
410 node = repo.lookup(rev or '.')
410 node = repo.lookup(rev or '.')
411 if good or bad or skip:
411 if good or bad or skip:
412 if good:
412 if good:
413 state['good'].append(node)
413 state['good'].append(node)
414 elif bad:
414 elif bad:
415 state['bad'].append(node)
415 state['bad'].append(node)
416 elif skip:
416 elif skip:
417 state['skip'].append(node)
417 state['skip'].append(node)
418 hbisect.save_state(repo, state)
418 hbisect.save_state(repo, state)
419
419
420 if not check_state(state):
420 if not check_state(state):
421 return
421 return
422
422
423 # actually bisect
423 # actually bisect
424 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
424 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
425 if changesets == 0:
425 if changesets == 0:
426 print_result(nodes, good)
426 print_result(nodes, good)
427 else:
427 else:
428 assert len(nodes) == 1 # only a single node can be tested next
428 assert len(nodes) == 1 # only a single node can be tested next
429 node = nodes[0]
429 node = nodes[0]
430 # compute the approximate number of remaining tests
430 # compute the approximate number of remaining tests
431 tests, size = 0, 2
431 tests, size = 0, 2
432 while size <= changesets:
432 while size <= changesets:
433 tests, size = tests + 1, size * 2
433 tests, size = tests + 1, size * 2
434 rev = repo.changelog.rev(node)
434 rev = repo.changelog.rev(node)
435 ui.write(_("Testing changeset %d:%s "
435 ui.write(_("Testing changeset %d:%s "
436 "(%d changesets remaining, ~%d tests)\n")
436 "(%d changesets remaining, ~%d tests)\n")
437 % (rev, short(node), changesets, tests))
437 % (rev, short(node), changesets, tests))
438 if not noupdate:
438 if not noupdate:
439 cmdutil.bail_if_changed(repo)
439 cmdutil.bail_if_changed(repo)
440 return hg.clean(repo, node)
440 return hg.clean(repo, node)
441
441
442 def branch(ui, repo, label=None, **opts):
442 def branch(ui, repo, label=None, **opts):
443 """set or show the current branch name
443 """set or show the current branch name
444
444
445 With no argument, show the current branch name. With one argument,
445 With no argument, show the current branch name. With one argument,
446 set the working directory branch name (the branch will not exist
446 set the working directory branch name (the branch will not exist
447 in the repository until the next commit). Standard practice
447 in the repository until the next commit). Standard practice
448 recommends that primary development take place on the 'default'
448 recommends that primary development take place on the 'default'
449 branch.
449 branch.
450
450
451 Unless -f/--force is specified, branch will not let you set a
451 Unless -f/--force is specified, branch will not let you set a
452 branch name that already exists, even if it's inactive.
452 branch name that already exists, even if it's inactive.
453
453
454 Use -C/--clean to reset the working directory branch to that of
454 Use -C/--clean to reset the working directory branch to that of
455 the parent of the working directory, negating a previous branch
455 the parent of the working directory, negating a previous branch
456 change.
456 change.
457
457
458 Use the command :hg:`update` to switch to an existing branch. Use
458 Use the command :hg:`update` to switch to an existing branch. Use
459 :hg:`commit --close-branch` to mark this branch as closed.
459 :hg:`commit --close-branch` to mark this branch as closed.
460 """
460 """
461
461
462 if opts.get('clean'):
462 if opts.get('clean'):
463 label = repo[None].parents()[0].branch()
463 label = repo[None].parents()[0].branch()
464 repo.dirstate.setbranch(label)
464 repo.dirstate.setbranch(label)
465 ui.status(_('reset working directory to branch %s\n') % label)
465 ui.status(_('reset working directory to branch %s\n') % label)
466 elif label:
466 elif label:
467 utflabel = encoding.fromlocal(label)
467 utflabel = encoding.fromlocal(label)
468 if not opts.get('force') and utflabel in repo.branchtags():
468 if not opts.get('force') and utflabel in repo.branchtags():
469 if label not in [p.branch() for p in repo.parents()]:
469 if label not in [p.branch() for p in repo.parents()]:
470 raise util.Abort(_('a branch of the same name already exists'
470 raise util.Abort(_('a branch of the same name already exists'
471 " (use 'hg update' to switch to it)"))
471 " (use 'hg update' to switch to it)"))
472 repo.dirstate.setbranch(utflabel)
472 repo.dirstate.setbranch(utflabel)
473 ui.status(_('marked working directory as branch %s\n') % label)
473 ui.status(_('marked working directory as branch %s\n') % label)
474 else:
474 else:
475 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
475 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
476
476
477 def branches(ui, repo, active=False, closed=False):
477 def branches(ui, repo, active=False, closed=False):
478 """list repository named branches
478 """list repository named branches
479
479
480 List the repository's named branches, indicating which ones are
480 List the repository's named branches, indicating which ones are
481 inactive. If -c/--closed is specified, also list branches which have
481 inactive. If -c/--closed is specified, also list branches which have
482 been marked closed (see hg commit --close-branch).
482 been marked closed (see hg commit --close-branch).
483
483
484 If -a/--active is specified, only show active branches. A branch
484 If -a/--active is specified, only show active branches. A branch
485 is considered active if it contains repository heads.
485 is considered active if it contains repository heads.
486
486
487 Use the command :hg:`update` to switch to an existing branch.
487 Use the command :hg:`update` to switch to an existing branch.
488 """
488 """
489
489
490 hexfunc = ui.debugflag and hex or short
490 hexfunc = ui.debugflag and hex or short
491 activebranches = [repo[n].branch() for n in repo.heads()]
491 activebranches = [repo[n].branch() for n in repo.heads()]
492 def testactive(tag, node):
492 def testactive(tag, node):
493 realhead = tag in activebranches
493 realhead = tag in activebranches
494 open = node in repo.branchheads(tag, closed=False)
494 open = node in repo.branchheads(tag, closed=False)
495 return realhead and open
495 return realhead and open
496 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
496 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
497 for tag, node in repo.branchtags().items()],
497 for tag, node in repo.branchtags().items()],
498 reverse=True)
498 reverse=True)
499
499
500 for isactive, node, tag in branches:
500 for isactive, node, tag in branches:
501 if (not active) or isactive:
501 if (not active) or isactive:
502 encodedtag = encoding.tolocal(tag)
502 encodedtag = encoding.tolocal(tag)
503 if ui.quiet:
503 if ui.quiet:
504 ui.write("%s\n" % encodedtag)
504 ui.write("%s\n" % encodedtag)
505 else:
505 else:
506 hn = repo.lookup(node)
506 hn = repo.lookup(node)
507 if isactive:
507 if isactive:
508 notice = ''
508 notice = ''
509 elif hn not in repo.branchheads(tag, closed=False):
509 elif hn not in repo.branchheads(tag, closed=False):
510 if not closed:
510 if not closed:
511 continue
511 continue
512 notice = _(' (closed)')
512 notice = _(' (closed)')
513 else:
513 else:
514 notice = _(' (inactive)')
514 notice = _(' (inactive)')
515 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
515 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
516 data = encodedtag, rev, hexfunc(hn), notice
516 data = encodedtag, rev, hexfunc(hn), notice
517 ui.write("%s %s:%s%s\n" % data)
517 ui.write("%s %s:%s%s\n" % data)
518
518
519 def bundle(ui, repo, fname, dest=None, **opts):
519 def bundle(ui, repo, fname, dest=None, **opts):
520 """create a changegroup file
520 """create a changegroup file
521
521
522 Generate a compressed changegroup file collecting changesets not
522 Generate a compressed changegroup file collecting changesets not
523 known to be in another repository.
523 known to be in another repository.
524
524
525 If you omit the destination repository, then hg assumes the
525 If you omit the destination repository, then hg assumes the
526 destination will have all the nodes you specify with --base
526 destination will have all the nodes you specify with --base
527 parameters. To create a bundle containing all changesets, use
527 parameters. To create a bundle containing all changesets, use
528 -a/--all (or --base null).
528 -a/--all (or --base null).
529
529
530 You can change compression method with the -t/--type option.
530 You can change compression method with the -t/--type option.
531 The available compression methods are: none, bzip2, and
531 The available compression methods are: none, bzip2, and
532 gzip (by default, bundles are compressed using bzip2).
532 gzip (by default, bundles are compressed using bzip2).
533
533
534 The bundle file can then be transferred using conventional means
534 The bundle file can then be transferred using conventional means
535 and applied to another repository with the unbundle or pull
535 and applied to another repository with the unbundle or pull
536 command. This is useful when direct push and pull are not
536 command. This is useful when direct push and pull are not
537 available or when exporting an entire repository is undesirable.
537 available or when exporting an entire repository is undesirable.
538
538
539 Applying bundles preserves all changeset contents including
539 Applying bundles preserves all changeset contents including
540 permissions, copy/rename information, and revision history.
540 permissions, copy/rename information, and revision history.
541 """
541 """
542 revs = opts.get('rev') or None
542 revs = opts.get('rev') or None
543 if revs:
543 if revs:
544 revs = [repo.lookup(rev) for rev in revs]
544 revs = [repo.lookup(rev) for rev in revs]
545 if opts.get('all'):
545 if opts.get('all'):
546 base = ['null']
546 base = ['null']
547 else:
547 else:
548 base = opts.get('base')
548 base = opts.get('base')
549 if base:
549 if base:
550 if dest:
550 if dest:
551 raise util.Abort(_("--base is incompatible with specifying "
551 raise util.Abort(_("--base is incompatible with specifying "
552 "a destination"))
552 "a destination"))
553 base = [repo.lookup(rev) for rev in base]
553 base = [repo.lookup(rev) for rev in base]
554 # create the right base
554 # create the right base
555 # XXX: nodesbetween / changegroup* should be "fixed" instead
555 # XXX: nodesbetween / changegroup* should be "fixed" instead
556 o = []
556 o = []
557 has = set((nullid,))
557 has = set((nullid,))
558 for n in base:
558 for n in base:
559 has.update(repo.changelog.reachable(n))
559 has.update(repo.changelog.reachable(n))
560 if revs:
560 if revs:
561 visit = list(revs)
561 visit = list(revs)
562 has.difference_update(revs)
562 has.difference_update(revs)
563 else:
563 else:
564 visit = repo.changelog.heads()
564 visit = repo.changelog.heads()
565 seen = {}
565 seen = {}
566 while visit:
566 while visit:
567 n = visit.pop(0)
567 n = visit.pop(0)
568 parents = [p for p in repo.changelog.parents(n) if p not in has]
568 parents = [p for p in repo.changelog.parents(n) if p not in has]
569 if len(parents) == 0:
569 if len(parents) == 0:
570 if n not in has:
570 if n not in has:
571 o.append(n)
571 o.append(n)
572 else:
572 else:
573 for p in parents:
573 for p in parents:
574 if p not in seen:
574 if p not in seen:
575 seen[p] = 1
575 seen[p] = 1
576 visit.append(p)
576 visit.append(p)
577 else:
577 else:
578 dest = ui.expandpath(dest or 'default-push', dest or 'default')
578 dest = ui.expandpath(dest or 'default-push', dest or 'default')
579 dest, branches = hg.parseurl(dest, opts.get('branch'))
579 dest, branches = hg.parseurl(dest, opts.get('branch'))
580 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
580 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
581 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
581 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
582 o = repo.findoutgoing(other, force=opts.get('force'))
582 o = repo.findoutgoing(other, force=opts.get('force'))
583
583
584 if not o:
584 if not o:
585 ui.status(_("no changes found\n"))
585 ui.status(_("no changes found\n"))
586 return
586 return
587
587
588 if revs:
588 if revs:
589 cg = repo.changegroupsubset(o, revs, 'bundle')
589 cg = repo.changegroupsubset(o, revs, 'bundle')
590 else:
590 else:
591 cg = repo.changegroup(o, 'bundle')
591 cg = repo.changegroup(o, 'bundle')
592
592
593 bundletype = opts.get('type', 'bzip2').lower()
593 bundletype = opts.get('type', 'bzip2').lower()
594 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
594 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
595 bundletype = btypes.get(bundletype)
595 bundletype = btypes.get(bundletype)
596 if bundletype not in changegroup.bundletypes:
596 if bundletype not in changegroup.bundletypes:
597 raise util.Abort(_('unknown bundle type specified with --type'))
597 raise util.Abort(_('unknown bundle type specified with --type'))
598
598
599 changegroup.writebundle(cg, fname, bundletype)
599 changegroup.writebundle(cg, fname, bundletype)
600
600
601 def cat(ui, repo, file1, *pats, **opts):
601 def cat(ui, repo, file1, *pats, **opts):
602 """output the current or given revision of files
602 """output the current or given revision of files
603
603
604 Print the specified files as they were at the given revision. If
604 Print the specified files as they were at the given revision. If
605 no revision is given, the parent of the working directory is used,
605 no revision is given, the parent of the working directory is used,
606 or tip if no revision is checked out.
606 or tip if no revision is checked out.
607
607
608 Output may be to a file, in which case the name of the file is
608 Output may be to a file, in which case the name of the file is
609 given using a format string. The formatting rules are the same as
609 given using a format string. The formatting rules are the same as
610 for the export command, with the following additions:
610 for the export command, with the following additions:
611
611
612 :``%s``: basename of file being printed
612 :``%s``: basename of file being printed
613 :``%d``: dirname of file being printed, or '.' if in repository root
613 :``%d``: dirname of file being printed, or '.' if in repository root
614 :``%p``: root-relative path name of file being printed
614 :``%p``: root-relative path name of file being printed
615 """
615 """
616 ctx = repo[opts.get('rev')]
616 ctx = repo[opts.get('rev')]
617 err = 1
617 err = 1
618 m = cmdutil.match(repo, (file1,) + pats, opts)
618 m = cmdutil.match(repo, (file1,) + pats, opts)
619 for abs in ctx.walk(m):
619 for abs in ctx.walk(m):
620 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
620 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
621 data = ctx[abs].data()
621 data = ctx[abs].data()
622 if opts.get('decode'):
622 if opts.get('decode'):
623 data = repo.wwritedata(abs, data)
623 data = repo.wwritedata(abs, data)
624 fp.write(data)
624 fp.write(data)
625 err = 0
625 err = 0
626 return err
626 return err
627
627
628 def clone(ui, source, dest=None, **opts):
628 def clone(ui, source, dest=None, **opts):
629 """make a copy of an existing repository
629 """make a copy of an existing repository
630
630
631 Create a copy of an existing repository in a new directory.
631 Create a copy of an existing repository in a new directory.
632
632
633 If no destination directory name is specified, it defaults to the
633 If no destination directory name is specified, it defaults to the
634 basename of the source.
634 basename of the source.
635
635
636 The location of the source is added to the new repository's
636 The location of the source is added to the new repository's
637 .hg/hgrc file, as the default to be used for future pulls.
637 .hg/hgrc file, as the default to be used for future pulls.
638
638
639 See :hg:`help urls` for valid source format details.
639 See :hg:`help urls` for valid source format details.
640
640
641 It is possible to specify an ``ssh://`` URL as the destination, but no
641 It is possible to specify an ``ssh://`` URL as the destination, but no
642 .hg/hgrc and working directory will be created on the remote side.
642 .hg/hgrc and working directory will be created on the remote side.
643 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
643 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
644
644
645 A set of changesets (tags, or branch names) to pull may be specified
645 A set of changesets (tags, or branch names) to pull may be specified
646 by listing each changeset (tag, or branch name) with -r/--rev.
646 by listing each changeset (tag, or branch name) with -r/--rev.
647 If -r/--rev is used, the cloned repository will contain only a subset
647 If -r/--rev is used, the cloned repository will contain only a subset
648 of the changesets of the source repository. Only the set of changesets
648 of the changesets of the source repository. Only the set of changesets
649 defined by all -r/--rev options (including all their ancestors)
649 defined by all -r/--rev options (including all their ancestors)
650 will be pulled into the destination repository.
650 will be pulled into the destination repository.
651 No subsequent changesets (including subsequent tags) will be present
651 No subsequent changesets (including subsequent tags) will be present
652 in the destination.
652 in the destination.
653
653
654 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
654 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
655 local source repositories.
655 local source repositories.
656
656
657 For efficiency, hardlinks are used for cloning whenever the source
657 For efficiency, hardlinks are used for cloning whenever the source
658 and destination are on the same filesystem (note this applies only
658 and destination are on the same filesystem (note this applies only
659 to the repository data, not to the working directory). Some
659 to the repository data, not to the working directory). Some
660 filesystems, such as AFS, implement hardlinking incorrectly, but
660 filesystems, such as AFS, implement hardlinking incorrectly, but
661 do not report errors. In these cases, use the --pull option to
661 do not report errors. In these cases, use the --pull option to
662 avoid hardlinking.
662 avoid hardlinking.
663
663
664 In some cases, you can clone repositories and the working directory
664 In some cases, you can clone repositories and the working directory
665 using full hardlinks with ::
665 using full hardlinks with ::
666
666
667 $ cp -al REPO REPOCLONE
667 $ cp -al REPO REPOCLONE
668
668
669 This is the fastest way to clone, but it is not always safe. The
669 This is the fastest way to clone, but it is not always safe. The
670 operation is not atomic (making sure REPO is not modified during
670 operation is not atomic (making sure REPO is not modified during
671 the operation is up to you) and you have to make sure your editor
671 the operation is up to you) and you have to make sure your editor
672 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
672 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
673 this is not compatible with certain extensions that place their
673 this is not compatible with certain extensions that place their
674 metadata under the .hg directory, such as mq.
674 metadata under the .hg directory, such as mq.
675
675
676 Mercurial will update the working directory to the first applicable
676 Mercurial will update the working directory to the first applicable
677 revision from this list:
677 revision from this list:
678
678
679 a) null if -U or the source repository has no changesets
679 a) null if -U or the source repository has no changesets
680 b) if -u . and the source repository is local, the first parent of
680 b) if -u . and the source repository is local, the first parent of
681 the source repository's working directory
681 the source repository's working directory
682 c) the changeset specified with -u (if a branch name, this means the
682 c) the changeset specified with -u (if a branch name, this means the
683 latest head of that branch)
683 latest head of that branch)
684 d) the changeset specified with -r
684 d) the changeset specified with -r
685 e) the tipmost head specified with -b
685 e) the tipmost head specified with -b
686 f) the tipmost head specified with the url#branch source syntax
686 f) the tipmost head specified with the url#branch source syntax
687 g) the tipmost head of the default branch
687 g) the tipmost head of the default branch
688 h) tip
688 h) tip
689 """
689 """
690 if opts.get('noupdate') and opts.get('updaterev'):
690 if opts.get('noupdate') and opts.get('updaterev'):
691 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
691 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
692
692
693 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
693 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
694 pull=opts.get('pull'),
694 pull=opts.get('pull'),
695 stream=opts.get('uncompressed'),
695 stream=opts.get('uncompressed'),
696 rev=opts.get('rev'),
696 rev=opts.get('rev'),
697 update=opts.get('updaterev') or not opts.get('noupdate'),
697 update=opts.get('updaterev') or not opts.get('noupdate'),
698 branch=opts.get('branch'))
698 branch=opts.get('branch'))
699
699
700 def commit(ui, repo, *pats, **opts):
700 def commit(ui, repo, *pats, **opts):
701 """commit the specified files or all outstanding changes
701 """commit the specified files or all outstanding changes
702
702
703 Commit changes to the given files into the repository. Unlike a
703 Commit changes to the given files into the repository. Unlike a
704 centralized RCS, this operation is a local operation. See hg push
704 centralized RCS, this operation is a local operation. See hg push
705 for a way to actively distribute your changes.
705 for a way to actively distribute your changes.
706
706
707 If a list of files is omitted, all changes reported by :hg:`status`
707 If a list of files is omitted, all changes reported by :hg:`status`
708 will be committed.
708 will be committed.
709
709
710 If you are committing the result of a merge, do not provide any
710 If you are committing the result of a merge, do not provide any
711 filenames or -I/-X filters.
711 filenames or -I/-X filters.
712
712
713 If no commit message is specified, the configured editor is
713 If no commit message is specified, the configured editor is
714 started to prompt you for a message.
714 started to prompt you for a message.
715
715
716 See :hg:`help dates` for a list of formats valid for -d/--date.
716 See :hg:`help dates` for a list of formats valid for -d/--date.
717 """
717 """
718 extra = {}
718 extra = {}
719 if opts.get('close_branch'):
719 if opts.get('close_branch'):
720 extra['close'] = 1
720 extra['close'] = 1
721 e = cmdutil.commiteditor
721 e = cmdutil.commiteditor
722 if opts.get('force_editor'):
722 if opts.get('force_editor'):
723 e = cmdutil.commitforceeditor
723 e = cmdutil.commitforceeditor
724
724
725 def commitfunc(ui, repo, message, match, opts):
725 def commitfunc(ui, repo, message, match, opts):
726 return repo.commit(message, opts.get('user'), opts.get('date'), match,
726 return repo.commit(message, opts.get('user'), opts.get('date'), match,
727 editor=e, extra=extra)
727 editor=e, extra=extra)
728
728
729 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
729 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
730 if not node:
730 if not node:
731 ui.status(_("nothing changed\n"))
731 ui.status(_("nothing changed\n"))
732 return
732 return
733 cl = repo.changelog
733 cl = repo.changelog
734 rev = cl.rev(node)
734 rev = cl.rev(node)
735 parents = cl.parentrevs(rev)
735 parents = cl.parentrevs(rev)
736 if rev - 1 in parents:
736 if rev - 1 in parents:
737 # one of the parents was the old tip
737 # one of the parents was the old tip
738 pass
738 pass
739 elif (parents == (nullrev, nullrev) or
739 elif (parents == (nullrev, nullrev) or
740 len(cl.heads(cl.node(parents[0]))) > 1 and
740 len(cl.heads(cl.node(parents[0]))) > 1 and
741 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
741 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
742 ui.status(_('created new head\n'))
742 ui.status(_('created new head\n'))
743
743
744 if ui.debugflag:
744 if ui.debugflag:
745 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
745 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
746 elif ui.verbose:
746 elif ui.verbose:
747 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
747 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
748
748
749 def copy(ui, repo, *pats, **opts):
749 def copy(ui, repo, *pats, **opts):
750 """mark files as copied for the next commit
750 """mark files as copied for the next commit
751
751
752 Mark dest as having copies of source files. If dest is a
752 Mark dest as having copies of source files. If dest is a
753 directory, copies are put in that directory. If dest is a file,
753 directory, copies are put in that directory. If dest is a file,
754 the source must be a single file.
754 the source must be a single file.
755
755
756 By default, this command copies the contents of files as they
756 By default, this command copies the contents of files as they
757 exist in the working directory. If invoked with -A/--after, the
757 exist in the working directory. If invoked with -A/--after, the
758 operation is recorded, but no copying is performed.
758 operation is recorded, but no copying is performed.
759
759
760 This command takes effect with the next commit. To undo a copy
760 This command takes effect with the next commit. To undo a copy
761 before that, see hg revert.
761 before that, see hg revert.
762 """
762 """
763 wlock = repo.wlock(False)
763 wlock = repo.wlock(False)
764 try:
764 try:
765 return cmdutil.copy(ui, repo, pats, opts)
765 return cmdutil.copy(ui, repo, pats, opts)
766 finally:
766 finally:
767 wlock.release()
767 wlock.release()
768
768
769 def debugancestor(ui, repo, *args):
769 def debugancestor(ui, repo, *args):
770 """find the ancestor revision of two revisions in a given index"""
770 """find the ancestor revision of two revisions in a given index"""
771 if len(args) == 3:
771 if len(args) == 3:
772 index, rev1, rev2 = args
772 index, rev1, rev2 = args
773 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
773 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
774 lookup = r.lookup
774 lookup = r.lookup
775 elif len(args) == 2:
775 elif len(args) == 2:
776 if not repo:
776 if not repo:
777 raise util.Abort(_("There is no Mercurial repository here "
777 raise util.Abort(_("There is no Mercurial repository here "
778 "(.hg not found)"))
778 "(.hg not found)"))
779 rev1, rev2 = args
779 rev1, rev2 = args
780 r = repo.changelog
780 r = repo.changelog
781 lookup = repo.lookup
781 lookup = repo.lookup
782 else:
782 else:
783 raise util.Abort(_('either two or three arguments required'))
783 raise util.Abort(_('either two or three arguments required'))
784 a = r.ancestor(lookup(rev1), lookup(rev2))
784 a = r.ancestor(lookup(rev1), lookup(rev2))
785 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
785 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
786
786
787 def debugcommands(ui, cmd='', *args):
787 def debugcommands(ui, cmd='', *args):
788 for cmd, vals in sorted(table.iteritems()):
788 for cmd, vals in sorted(table.iteritems()):
789 cmd = cmd.split('|')[0].strip('^')
789 cmd = cmd.split('|')[0].strip('^')
790 opts = ', '.join([i[1] for i in vals[1]])
790 opts = ', '.join([i[1] for i in vals[1]])
791 ui.write('%s: %s\n' % (cmd, opts))
791 ui.write('%s: %s\n' % (cmd, opts))
792
792
793 def debugcomplete(ui, cmd='', **opts):
793 def debugcomplete(ui, cmd='', **opts):
794 """returns the completion list associated with the given command"""
794 """returns the completion list associated with the given command"""
795
795
796 if opts.get('options'):
796 if opts.get('options'):
797 options = []
797 options = []
798 otables = [globalopts]
798 otables = [globalopts]
799 if cmd:
799 if cmd:
800 aliases, entry = cmdutil.findcmd(cmd, table, False)
800 aliases, entry = cmdutil.findcmd(cmd, table, False)
801 otables.append(entry[1])
801 otables.append(entry[1])
802 for t in otables:
802 for t in otables:
803 for o in t:
803 for o in t:
804 if "(DEPRECATED)" in o[3]:
804 if "(DEPRECATED)" in o[3]:
805 continue
805 continue
806 if o[0]:
806 if o[0]:
807 options.append('-%s' % o[0])
807 options.append('-%s' % o[0])
808 options.append('--%s' % o[1])
808 options.append('--%s' % o[1])
809 ui.write("%s\n" % "\n".join(options))
809 ui.write("%s\n" % "\n".join(options))
810 return
810 return
811
811
812 cmdlist = cmdutil.findpossible(cmd, table)
812 cmdlist = cmdutil.findpossible(cmd, table)
813 if ui.verbose:
813 if ui.verbose:
814 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
814 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
815 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
815 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
816
816
817 def debugfsinfo(ui, path = "."):
817 def debugfsinfo(ui, path = "."):
818 open('.debugfsinfo', 'w').write('')
818 open('.debugfsinfo', 'w').write('')
819 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
819 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
820 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
820 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
821 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
821 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
822 and 'yes' or 'no'))
822 and 'yes' or 'no'))
823 os.unlink('.debugfsinfo')
823 os.unlink('.debugfsinfo')
824
824
825 def debugrebuildstate(ui, repo, rev="tip"):
825 def debugrebuildstate(ui, repo, rev="tip"):
826 """rebuild the dirstate as it would look like for the given revision"""
826 """rebuild the dirstate as it would look like for the given revision"""
827 ctx = repo[rev]
827 ctx = repo[rev]
828 wlock = repo.wlock()
828 wlock = repo.wlock()
829 try:
829 try:
830 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
830 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
831 finally:
831 finally:
832 wlock.release()
832 wlock.release()
833
833
834 def debugcheckstate(ui, repo):
834 def debugcheckstate(ui, repo):
835 """validate the correctness of the current dirstate"""
835 """validate the correctness of the current dirstate"""
836 parent1, parent2 = repo.dirstate.parents()
836 parent1, parent2 = repo.dirstate.parents()
837 m1 = repo[parent1].manifest()
837 m1 = repo[parent1].manifest()
838 m2 = repo[parent2].manifest()
838 m2 = repo[parent2].manifest()
839 errors = 0
839 errors = 0
840 for f in repo.dirstate:
840 for f in repo.dirstate:
841 state = repo.dirstate[f]
841 state = repo.dirstate[f]
842 if state in "nr" and f not in m1:
842 if state in "nr" and f not in m1:
843 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
843 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
844 errors += 1
844 errors += 1
845 if state in "a" and f in m1:
845 if state in "a" and f in m1:
846 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
846 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
847 errors += 1
847 errors += 1
848 if state in "m" and f not in m1 and f not in m2:
848 if state in "m" and f not in m1 and f not in m2:
849 ui.warn(_("%s in state %s, but not in either manifest\n") %
849 ui.warn(_("%s in state %s, but not in either manifest\n") %
850 (f, state))
850 (f, state))
851 errors += 1
851 errors += 1
852 for f in m1:
852 for f in m1:
853 state = repo.dirstate[f]
853 state = repo.dirstate[f]
854 if state not in "nrm":
854 if state not in "nrm":
855 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
855 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
856 errors += 1
856 errors += 1
857 if errors:
857 if errors:
858 error = _(".hg/dirstate inconsistent with current parent's manifest")
858 error = _(".hg/dirstate inconsistent with current parent's manifest")
859 raise util.Abort(error)
859 raise util.Abort(error)
860
860
861 def showconfig(ui, repo, *values, **opts):
861 def showconfig(ui, repo, *values, **opts):
862 """show combined config settings from all hgrc files
862 """show combined config settings from all hgrc files
863
863
864 With no arguments, print names and values of all config items.
864 With no arguments, print names and values of all config items.
865
865
866 With one argument of the form section.name, print just the value
866 With one argument of the form section.name, print just the value
867 of that config item.
867 of that config item.
868
868
869 With multiple arguments, print names and values of all config
869 With multiple arguments, print names and values of all config
870 items with matching section names.
870 items with matching section names.
871
871
872 With --debug, the source (filename and line number) is printed
872 With --debug, the source (filename and line number) is printed
873 for each config item.
873 for each config item.
874 """
874 """
875
875
876 for f in util.rcpath():
876 for f in util.rcpath():
877 ui.debug(_('read config from: %s\n') % f)
877 ui.debug(_('read config from: %s\n') % f)
878 untrusted = bool(opts.get('untrusted'))
878 untrusted = bool(opts.get('untrusted'))
879 if values:
879 if values:
880 if len([v for v in values if '.' in v]) > 1:
880 if len([v for v in values if '.' in v]) > 1:
881 raise util.Abort(_('only one config item permitted'))
881 raise util.Abort(_('only one config item permitted'))
882 for section, name, value in ui.walkconfig(untrusted=untrusted):
882 for section, name, value in ui.walkconfig(untrusted=untrusted):
883 sectname = section + '.' + name
883 sectname = section + '.' + name
884 if values:
884 if values:
885 for v in values:
885 for v in values:
886 if v == section:
886 if v == section:
887 ui.debug('%s: ' %
887 ui.debug('%s: ' %
888 ui.configsource(section, name, untrusted))
888 ui.configsource(section, name, untrusted))
889 ui.write('%s=%s\n' % (sectname, value))
889 ui.write('%s=%s\n' % (sectname, value))
890 elif v == sectname:
890 elif v == sectname:
891 ui.debug('%s: ' %
891 ui.debug('%s: ' %
892 ui.configsource(section, name, untrusted))
892 ui.configsource(section, name, untrusted))
893 ui.write(value, '\n')
893 ui.write(value, '\n')
894 else:
894 else:
895 ui.debug('%s: ' %
895 ui.debug('%s: ' %
896 ui.configsource(section, name, untrusted))
896 ui.configsource(section, name, untrusted))
897 ui.write('%s=%s\n' % (sectname, value))
897 ui.write('%s=%s\n' % (sectname, value))
898
898
899 def debugsetparents(ui, repo, rev1, rev2=None):
899 def debugsetparents(ui, repo, rev1, rev2=None):
900 """manually set the parents of the current working directory
900 """manually set the parents of the current working directory
901
901
902 This is useful for writing repository conversion tools, but should
902 This is useful for writing repository conversion tools, but should
903 be used with care.
903 be used with care.
904 """
904 """
905
905
906 if not rev2:
906 if not rev2:
907 rev2 = hex(nullid)
907 rev2 = hex(nullid)
908
908
909 wlock = repo.wlock()
909 wlock = repo.wlock()
910 try:
910 try:
911 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
911 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
912 finally:
912 finally:
913 wlock.release()
913 wlock.release()
914
914
915 def debugstate(ui, repo, nodates=None):
915 def debugstate(ui, repo, nodates=None):
916 """show the contents of the current dirstate"""
916 """show the contents of the current dirstate"""
917 timestr = ""
917 timestr = ""
918 showdate = not nodates
918 showdate = not nodates
919 for file_, ent in sorted(repo.dirstate._map.iteritems()):
919 for file_, ent in sorted(repo.dirstate._map.iteritems()):
920 if showdate:
920 if showdate:
921 if ent[3] == -1:
921 if ent[3] == -1:
922 # Pad or slice to locale representation
922 # Pad or slice to locale representation
923 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
923 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
924 time.localtime(0)))
924 time.localtime(0)))
925 timestr = 'unset'
925 timestr = 'unset'
926 timestr = (timestr[:locale_len] +
926 timestr = (timestr[:locale_len] +
927 ' ' * (locale_len - len(timestr)))
927 ' ' * (locale_len - len(timestr)))
928 else:
928 else:
929 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
929 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
930 time.localtime(ent[3]))
930 time.localtime(ent[3]))
931 if ent[1] & 020000:
931 if ent[1] & 020000:
932 mode = 'lnk'
932 mode = 'lnk'
933 else:
933 else:
934 mode = '%3o' % (ent[1] & 0777)
934 mode = '%3o' % (ent[1] & 0777)
935 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
935 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
936 for f in repo.dirstate.copies():
936 for f in repo.dirstate.copies():
937 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
937 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
938
938
939 def debugsub(ui, repo, rev=None):
939 def debugsub(ui, repo, rev=None):
940 if rev == '':
940 if rev == '':
941 rev = None
941 rev = None
942 for k, v in sorted(repo[rev].substate.items()):
942 for k, v in sorted(repo[rev].substate.items()):
943 ui.write('path %s\n' % k)
943 ui.write('path %s\n' % k)
944 ui.write(' source %s\n' % v[0])
944 ui.write(' source %s\n' % v[0])
945 ui.write(' revision %s\n' % v[1])
945 ui.write(' revision %s\n' % v[1])
946
946
947 def debugdata(ui, file_, rev):
947 def debugdata(ui, file_, rev):
948 """dump the contents of a data file revision"""
948 """dump the contents of a data file revision"""
949 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
949 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
950 try:
950 try:
951 ui.write(r.revision(r.lookup(rev)))
951 ui.write(r.revision(r.lookup(rev)))
952 except KeyError:
952 except KeyError:
953 raise util.Abort(_('invalid revision identifier %s') % rev)
953 raise util.Abort(_('invalid revision identifier %s') % rev)
954
954
955 def debugdate(ui, date, range=None, **opts):
955 def debugdate(ui, date, range=None, **opts):
956 """parse and display a date"""
956 """parse and display a date"""
957 if opts["extended"]:
957 if opts["extended"]:
958 d = util.parsedate(date, util.extendeddateformats)
958 d = util.parsedate(date, util.extendeddateformats)
959 else:
959 else:
960 d = util.parsedate(date)
960 d = util.parsedate(date)
961 ui.write("internal: %s %s\n" % d)
961 ui.write("internal: %s %s\n" % d)
962 ui.write("standard: %s\n" % util.datestr(d))
962 ui.write("standard: %s\n" % util.datestr(d))
963 if range:
963 if range:
964 m = util.matchdate(range)
964 m = util.matchdate(range)
965 ui.write("match: %s\n" % m(d[0]))
965 ui.write("match: %s\n" % m(d[0]))
966
966
967 def debugindex(ui, file_):
967 def debugindex(ui, file_):
968 """dump the contents of an index file"""
968 """dump the contents of an index file"""
969 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
969 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
970 ui.write(" rev offset length base linkrev"
970 ui.write(" rev offset length base linkrev"
971 " nodeid p1 p2\n")
971 " nodeid p1 p2\n")
972 for i in r:
972 for i in r:
973 node = r.node(i)
973 node = r.node(i)
974 try:
974 try:
975 pp = r.parents(node)
975 pp = r.parents(node)
976 except:
976 except:
977 pp = [nullid, nullid]
977 pp = [nullid, nullid]
978 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
978 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
979 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
979 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
980 short(node), short(pp[0]), short(pp[1])))
980 short(node), short(pp[0]), short(pp[1])))
981
981
982 def debugindexdot(ui, file_):
982 def debugindexdot(ui, file_):
983 """dump an index DAG as a graphviz dot file"""
983 """dump an index DAG as a graphviz dot file"""
984 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
984 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
985 ui.write("digraph G {\n")
985 ui.write("digraph G {\n")
986 for i in r:
986 for i in r:
987 node = r.node(i)
987 node = r.node(i)
988 pp = r.parents(node)
988 pp = r.parents(node)
989 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
989 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
990 if pp[1] != nullid:
990 if pp[1] != nullid:
991 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
991 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
992 ui.write("}\n")
992 ui.write("}\n")
993
993
994 def debuginstall(ui):
994 def debuginstall(ui):
995 '''test Mercurial installation'''
995 '''test Mercurial installation'''
996
996
997 def writetemp(contents):
997 def writetemp(contents):
998 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
998 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
999 f = os.fdopen(fd, "wb")
999 f = os.fdopen(fd, "wb")
1000 f.write(contents)
1000 f.write(contents)
1001 f.close()
1001 f.close()
1002 return name
1002 return name
1003
1003
1004 problems = 0
1004 problems = 0
1005
1005
1006 # encoding
1006 # encoding
1007 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1007 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1008 try:
1008 try:
1009 encoding.fromlocal("test")
1009 encoding.fromlocal("test")
1010 except util.Abort, inst:
1010 except util.Abort, inst:
1011 ui.write(" %s\n" % inst)
1011 ui.write(" %s\n" % inst)
1012 ui.write(_(" (check that your locale is properly set)\n"))
1012 ui.write(_(" (check that your locale is properly set)\n"))
1013 problems += 1
1013 problems += 1
1014
1014
1015 # compiled modules
1015 # compiled modules
1016 ui.status(_("Checking extensions...\n"))
1016 ui.status(_("Checking extensions...\n"))
1017 try:
1017 try:
1018 import bdiff, mpatch, base85
1018 import bdiff, mpatch, base85
1019 except Exception, inst:
1019 except Exception, inst:
1020 ui.write(" %s\n" % inst)
1020 ui.write(" %s\n" % inst)
1021 ui.write(_(" One or more extensions could not be found"))
1021 ui.write(_(" One or more extensions could not be found"))
1022 ui.write(_(" (check that you compiled the extensions)\n"))
1022 ui.write(_(" (check that you compiled the extensions)\n"))
1023 problems += 1
1023 problems += 1
1024
1024
1025 # templates
1025 # templates
1026 ui.status(_("Checking templates...\n"))
1026 ui.status(_("Checking templates...\n"))
1027 try:
1027 try:
1028 import templater
1028 import templater
1029 templater.templater(templater.templatepath("map-cmdline.default"))
1029 templater.templater(templater.templatepath("map-cmdline.default"))
1030 except Exception, inst:
1030 except Exception, inst:
1031 ui.write(" %s\n" % inst)
1031 ui.write(" %s\n" % inst)
1032 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1032 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1033 problems += 1
1033 problems += 1
1034
1034
1035 # patch
1035 # patch
1036 ui.status(_("Checking patch...\n"))
1036 ui.status(_("Checking patch...\n"))
1037 patchproblems = 0
1037 patchproblems = 0
1038 a = "1\n2\n3\n4\n"
1038 a = "1\n2\n3\n4\n"
1039 b = "1\n2\n3\ninsert\n4\n"
1039 b = "1\n2\n3\ninsert\n4\n"
1040 fa = writetemp(a)
1040 fa = writetemp(a)
1041 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1041 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1042 os.path.basename(fa))
1042 os.path.basename(fa))
1043 fd = writetemp(d)
1043 fd = writetemp(d)
1044
1044
1045 files = {}
1045 files = {}
1046 try:
1046 try:
1047 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1047 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1048 except util.Abort, e:
1048 except util.Abort, e:
1049 ui.write(_(" patch call failed:\n"))
1049 ui.write(_(" patch call failed:\n"))
1050 ui.write(" " + str(e) + "\n")
1050 ui.write(" " + str(e) + "\n")
1051 patchproblems += 1
1051 patchproblems += 1
1052 else:
1052 else:
1053 if list(files) != [os.path.basename(fa)]:
1053 if list(files) != [os.path.basename(fa)]:
1054 ui.write(_(" unexpected patch output!\n"))
1054 ui.write(_(" unexpected patch output!\n"))
1055 patchproblems += 1
1055 patchproblems += 1
1056 a = open(fa).read()
1056 a = open(fa).read()
1057 if a != b:
1057 if a != b:
1058 ui.write(_(" patch test failed!\n"))
1058 ui.write(_(" patch test failed!\n"))
1059 patchproblems += 1
1059 patchproblems += 1
1060
1060
1061 if patchproblems:
1061 if patchproblems:
1062 if ui.config('ui', 'patch'):
1062 if ui.config('ui', 'patch'):
1063 ui.write(_(" (Current patch tool may be incompatible with patch,"
1063 ui.write(_(" (Current patch tool may be incompatible with patch,"
1064 " or misconfigured. Please check your .hgrc file)\n"))
1064 " or misconfigured. Please check your .hgrc file)\n"))
1065 else:
1065 else:
1066 ui.write(_(" Internal patcher failure, please report this error"
1066 ui.write(_(" Internal patcher failure, please report this error"
1067 " to http://mercurial.selenic.com/bts/\n"))
1067 " to http://mercurial.selenic.com/bts/\n"))
1068 problems += patchproblems
1068 problems += patchproblems
1069
1069
1070 os.unlink(fa)
1070 os.unlink(fa)
1071 os.unlink(fd)
1071 os.unlink(fd)
1072
1072
1073 # editor
1073 # editor
1074 ui.status(_("Checking commit editor...\n"))
1074 ui.status(_("Checking commit editor...\n"))
1075 editor = ui.geteditor()
1075 editor = ui.geteditor()
1076 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1076 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1077 if not cmdpath:
1077 if not cmdpath:
1078 if editor == 'vi':
1078 if editor == 'vi':
1079 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1079 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1080 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1080 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1081 else:
1081 else:
1082 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1082 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1083 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1083 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1084 problems += 1
1084 problems += 1
1085
1085
1086 # check username
1086 # check username
1087 ui.status(_("Checking username...\n"))
1087 ui.status(_("Checking username...\n"))
1088 try:
1088 try:
1089 user = ui.username()
1089 user = ui.username()
1090 except util.Abort, e:
1090 except util.Abort, e:
1091 ui.write(" %s\n" % e)
1091 ui.write(" %s\n" % e)
1092 ui.write(_(" (specify a username in your .hgrc file)\n"))
1092 ui.write(_(" (specify a username in your .hgrc file)\n"))
1093 problems += 1
1093 problems += 1
1094
1094
1095 if not problems:
1095 if not problems:
1096 ui.status(_("No problems detected\n"))
1096 ui.status(_("No problems detected\n"))
1097 else:
1097 else:
1098 ui.write(_("%s problems detected,"
1098 ui.write(_("%s problems detected,"
1099 " please check your install!\n") % problems)
1099 " please check your install!\n") % problems)
1100
1100
1101 return problems
1101 return problems
1102
1102
1103 def debugrename(ui, repo, file1, *pats, **opts):
1103 def debugrename(ui, repo, file1, *pats, **opts):
1104 """dump rename information"""
1104 """dump rename information"""
1105
1105
1106 ctx = repo[opts.get('rev')]
1106 ctx = repo[opts.get('rev')]
1107 m = cmdutil.match(repo, (file1,) + pats, opts)
1107 m = cmdutil.match(repo, (file1,) + pats, opts)
1108 for abs in ctx.walk(m):
1108 for abs in ctx.walk(m):
1109 fctx = ctx[abs]
1109 fctx = ctx[abs]
1110 o = fctx.filelog().renamed(fctx.filenode())
1110 o = fctx.filelog().renamed(fctx.filenode())
1111 rel = m.rel(abs)
1111 rel = m.rel(abs)
1112 if o:
1112 if o:
1113 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1113 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1114 else:
1114 else:
1115 ui.write(_("%s not renamed\n") % rel)
1115 ui.write(_("%s not renamed\n") % rel)
1116
1116
1117 def debugwalk(ui, repo, *pats, **opts):
1117 def debugwalk(ui, repo, *pats, **opts):
1118 """show how files match on given patterns"""
1118 """show how files match on given patterns"""
1119 m = cmdutil.match(repo, pats, opts)
1119 m = cmdutil.match(repo, pats, opts)
1120 items = list(repo.walk(m))
1120 items = list(repo.walk(m))
1121 if not items:
1121 if not items:
1122 return
1122 return
1123 fmt = 'f %%-%ds %%-%ds %%s' % (
1123 fmt = 'f %%-%ds %%-%ds %%s' % (
1124 max([len(abs) for abs in items]),
1124 max([len(abs) for abs in items]),
1125 max([len(m.rel(abs)) for abs in items]))
1125 max([len(m.rel(abs)) for abs in items]))
1126 for abs in items:
1126 for abs in items:
1127 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1127 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1128 ui.write("%s\n" % line.rstrip())
1128 ui.write("%s\n" % line.rstrip())
1129
1129
1130 def diff(ui, repo, *pats, **opts):
1130 def diff(ui, repo, *pats, **opts):
1131 """diff repository (or selected files)
1131 """diff repository (or selected files)
1132
1132
1133 Show differences between revisions for the specified files.
1133 Show differences between revisions for the specified files.
1134
1134
1135 Differences between files are shown using the unified diff format.
1135 Differences between files are shown using the unified diff format.
1136
1136
1137 NOTE: diff may generate unexpected results for merges, as it will
1137 NOTE: diff may generate unexpected results for merges, as it will
1138 default to comparing against the working directory's first parent
1138 default to comparing against the working directory's first parent
1139 changeset if no revisions are specified.
1139 changeset if no revisions are specified.
1140
1140
1141 When two revision arguments are given, then changes are shown
1141 When two revision arguments are given, then changes are shown
1142 between those revisions. If only one revision is specified then
1142 between those revisions. If only one revision is specified then
1143 that revision is compared to the working directory, and, when no
1143 that revision is compared to the working directory, and, when no
1144 revisions are specified, the working directory files are compared
1144 revisions are specified, the working directory files are compared
1145 to its parent.
1145 to its parent.
1146
1146
1147 Alternatively you can specify -c/--change with a revision to see
1147 Alternatively you can specify -c/--change with a revision to see
1148 the changes in that changeset relative to its first parent.
1148 the changes in that changeset relative to its first parent.
1149
1149
1150 Without the -a/--text option, diff will avoid generating diffs of
1150 Without the -a/--text option, diff will avoid generating diffs of
1151 files it detects as binary. With -a, diff will generate a diff
1151 files it detects as binary. With -a, diff will generate a diff
1152 anyway, probably with undesirable results.
1152 anyway, probably with undesirable results.
1153
1153
1154 Use the -g/--git option to generate diffs in the git extended diff
1154 Use the -g/--git option to generate diffs in the git extended diff
1155 format. For more information, read :hg:`help diffs`.
1155 format. For more information, read :hg:`help diffs`.
1156 """
1156 """
1157
1157
1158 revs = opts.get('rev')
1158 revs = opts.get('rev')
1159 change = opts.get('change')
1159 change = opts.get('change')
1160 stat = opts.get('stat')
1160 stat = opts.get('stat')
1161 reverse = opts.get('reverse')
1161 reverse = opts.get('reverse')
1162
1162
1163 if revs and change:
1163 if revs and change:
1164 msg = _('cannot specify --rev and --change at the same time')
1164 msg = _('cannot specify --rev and --change at the same time')
1165 raise util.Abort(msg)
1165 raise util.Abort(msg)
1166 elif change:
1166 elif change:
1167 node2 = repo.lookup(change)
1167 node2 = repo.lookup(change)
1168 node1 = repo[node2].parents()[0].node()
1168 node1 = repo[node2].parents()[0].node()
1169 else:
1169 else:
1170 node1, node2 = cmdutil.revpair(repo, revs)
1170 node1, node2 = cmdutil.revpair(repo, revs)
1171
1171
1172 if reverse:
1172 if reverse:
1173 node1, node2 = node2, node1
1173 node1, node2 = node2, node1
1174
1174
1175 diffopts = patch.diffopts(ui, opts)
1175 diffopts = patch.diffopts(ui, opts)
1176 m = cmdutil.match(repo, pats, opts)
1176 m = cmdutil.match(repo, pats, opts)
1177 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1177 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1178
1178
1179 def export(ui, repo, *changesets, **opts):
1179 def export(ui, repo, *changesets, **opts):
1180 """dump the header and diffs for one or more changesets
1180 """dump the header and diffs for one or more changesets
1181
1181
1182 Print the changeset header and diffs for one or more revisions.
1182 Print the changeset header and diffs for one or more revisions.
1183
1183
1184 The information shown in the changeset header is: author, date,
1184 The information shown in the changeset header is: author, date,
1185 branch name (if non-default), changeset hash, parent(s) and commit
1185 branch name (if non-default), changeset hash, parent(s) and commit
1186 comment.
1186 comment.
1187
1187
1188 NOTE: export may generate unexpected diff output for merge
1188 NOTE: export may generate unexpected diff output for merge
1189 changesets, as it will compare the merge changeset against its
1189 changesets, as it will compare the merge changeset against its
1190 first parent only.
1190 first parent only.
1191
1191
1192 Output may be to a file, in which case the name of the file is
1192 Output may be to a file, in which case the name of the file is
1193 given using a format string. The formatting rules are as follows:
1193 given using a format string. The formatting rules are as follows:
1194
1194
1195 :``%%``: literal "%" character
1195 :``%%``: literal "%" character
1196 :``%H``: changeset hash (40 bytes of hexadecimal)
1196 :``%H``: changeset hash (40 bytes of hexadecimal)
1197 :``%N``: number of patches being generated
1197 :``%N``: number of patches being generated
1198 :``%R``: changeset revision number
1198 :``%R``: changeset revision number
1199 :``%b``: basename of the exporting repository
1199 :``%b``: basename of the exporting repository
1200 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1200 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1201 :``%n``: zero-padded sequence number, starting at 1
1201 :``%n``: zero-padded sequence number, starting at 1
1202 :``%r``: zero-padded changeset revision number
1202 :``%r``: zero-padded changeset revision number
1203
1203
1204 Without the -a/--text option, export will avoid generating diffs
1204 Without the -a/--text option, export will avoid generating diffs
1205 of files it detects as binary. With -a, export will generate a
1205 of files it detects as binary. With -a, export will generate a
1206 diff anyway, probably with undesirable results.
1206 diff anyway, probably with undesirable results.
1207
1207
1208 Use the -g/--git option to generate diffs in the git extended diff
1208 Use the -g/--git option to generate diffs in the git extended diff
1209 format. See :hg:`help diffs` for more information.
1209 format. See :hg:`help diffs` for more information.
1210
1210
1211 With the --switch-parent option, the diff will be against the
1211 With the --switch-parent option, the diff will be against the
1212 second parent. It can be useful to review a merge.
1212 second parent. It can be useful to review a merge.
1213 """
1213 """
1214 changesets += tuple(opts.get('rev', []))
1214 changesets += tuple(opts.get('rev', []))
1215 if not changesets:
1215 if not changesets:
1216 raise util.Abort(_("export requires at least one changeset"))
1216 raise util.Abort(_("export requires at least one changeset"))
1217 revs = cmdutil.revrange(repo, changesets)
1217 revs = cmdutil.revrange(repo, changesets)
1218 if len(revs) > 1:
1218 if len(revs) > 1:
1219 ui.note(_('exporting patches:\n'))
1219 ui.note(_('exporting patches:\n'))
1220 else:
1220 else:
1221 ui.note(_('exporting patch:\n'))
1221 ui.note(_('exporting patch:\n'))
1222 cmdutil.export(repo, revs, template=opts.get('output'),
1222 cmdutil.export(repo, revs, template=opts.get('output'),
1223 switch_parent=opts.get('switch_parent'),
1223 switch_parent=opts.get('switch_parent'),
1224 opts=patch.diffopts(ui, opts))
1224 opts=patch.diffopts(ui, opts))
1225
1225
1226 def forget(ui, repo, *pats, **opts):
1226 def forget(ui, repo, *pats, **opts):
1227 """forget the specified files on the next commit
1227 """forget the specified files on the next commit
1228
1228
1229 Mark the specified files so they will no longer be tracked
1229 Mark the specified files so they will no longer be tracked
1230 after the next commit.
1230 after the next commit.
1231
1231
1232 This only removes files from the current branch, not from the
1232 This only removes files from the current branch, not from the
1233 entire project history, and it does not delete them from the
1233 entire project history, and it does not delete them from the
1234 working directory.
1234 working directory.
1235
1235
1236 To undo a forget before the next commit, see hg add.
1236 To undo a forget before the next commit, see hg add.
1237 """
1237 """
1238
1238
1239 if not pats:
1239 if not pats:
1240 raise util.Abort(_('no files specified'))
1240 raise util.Abort(_('no files specified'))
1241
1241
1242 m = cmdutil.match(repo, pats, opts)
1242 m = cmdutil.match(repo, pats, opts)
1243 s = repo.status(match=m, clean=True)
1243 s = repo.status(match=m, clean=True)
1244 forget = sorted(s[0] + s[1] + s[3] + s[6])
1244 forget = sorted(s[0] + s[1] + s[3] + s[6])
1245
1245
1246 for f in m.files():
1246 for f in m.files():
1247 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1247 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1248 ui.warn(_('not removing %s: file is already untracked\n')
1248 ui.warn(_('not removing %s: file is already untracked\n')
1249 % m.rel(f))
1249 % m.rel(f))
1250
1250
1251 for f in forget:
1251 for f in forget:
1252 if ui.verbose or not m.exact(f):
1252 if ui.verbose or not m.exact(f):
1253 ui.status(_('removing %s\n') % m.rel(f))
1253 ui.status(_('removing %s\n') % m.rel(f))
1254
1254
1255 repo.remove(forget, unlink=False)
1255 repo.remove(forget, unlink=False)
1256
1256
1257 def grep(ui, repo, pattern, *pats, **opts):
1257 def grep(ui, repo, pattern, *pats, **opts):
1258 """search for a pattern in specified files and revisions
1258 """search for a pattern in specified files and revisions
1259
1259
1260 Search revisions of files for a regular expression.
1260 Search revisions of files for a regular expression.
1261
1261
1262 This command behaves differently than Unix grep. It only accepts
1262 This command behaves differently than Unix grep. It only accepts
1263 Python/Perl regexps. It searches repository history, not the
1263 Python/Perl regexps. It searches repository history, not the
1264 working directory. It always prints the revision number in which a
1264 working directory. It always prints the revision number in which a
1265 match appears.
1265 match appears.
1266
1266
1267 By default, grep only prints output for the first revision of a
1267 By default, grep only prints output for the first revision of a
1268 file in which it finds a match. To get it to print every revision
1268 file in which it finds a match. To get it to print every revision
1269 that contains a change in match status ("-" for a match that
1269 that contains a change in match status ("-" for a match that
1270 becomes a non-match, or "+" for a non-match that becomes a match),
1270 becomes a non-match, or "+" for a non-match that becomes a match),
1271 use the --all flag.
1271 use the --all flag.
1272 """
1272 """
1273 reflags = 0
1273 reflags = 0
1274 if opts.get('ignore_case'):
1274 if opts.get('ignore_case'):
1275 reflags |= re.I
1275 reflags |= re.I
1276 try:
1276 try:
1277 regexp = re.compile(pattern, reflags)
1277 regexp = re.compile(pattern, reflags)
1278 except Exception, inst:
1278 except Exception, inst:
1279 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1279 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1280 return None
1280 return None
1281 sep, eol = ':', '\n'
1281 sep, eol = ':', '\n'
1282 if opts.get('print0'):
1282 if opts.get('print0'):
1283 sep = eol = '\0'
1283 sep = eol = '\0'
1284
1284
1285 getfile = util.lrucachefunc(repo.file)
1285 getfile = util.lrucachefunc(repo.file)
1286
1286
1287 def matchlines(body):
1287 def matchlines(body):
1288 begin = 0
1288 begin = 0
1289 linenum = 0
1289 linenum = 0
1290 while True:
1290 while True:
1291 match = regexp.search(body, begin)
1291 match = regexp.search(body, begin)
1292 if not match:
1292 if not match:
1293 break
1293 break
1294 mstart, mend = match.span()
1294 mstart, mend = match.span()
1295 linenum += body.count('\n', begin, mstart) + 1
1295 linenum += body.count('\n', begin, mstart) + 1
1296 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1296 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1297 begin = body.find('\n', mend) + 1 or len(body)
1297 begin = body.find('\n', mend) + 1 or len(body)
1298 lend = begin - 1
1298 lend = begin - 1
1299 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1299 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1300
1300
1301 class linestate(object):
1301 class linestate(object):
1302 def __init__(self, line, linenum, colstart, colend):
1302 def __init__(self, line, linenum, colstart, colend):
1303 self.line = line
1303 self.line = line
1304 self.linenum = linenum
1304 self.linenum = linenum
1305 self.colstart = colstart
1305 self.colstart = colstart
1306 self.colend = colend
1306 self.colend = colend
1307
1307
1308 def __hash__(self):
1308 def __hash__(self):
1309 return hash((self.linenum, self.line))
1309 return hash((self.linenum, self.line))
1310
1310
1311 def __eq__(self, other):
1311 def __eq__(self, other):
1312 return self.line == other.line
1312 return self.line == other.line
1313
1313
1314 matches = {}
1314 matches = {}
1315 copies = {}
1315 copies = {}
1316 def grepbody(fn, rev, body):
1316 def grepbody(fn, rev, body):
1317 matches[rev].setdefault(fn, [])
1317 matches[rev].setdefault(fn, [])
1318 m = matches[rev][fn]
1318 m = matches[rev][fn]
1319 for lnum, cstart, cend, line in matchlines(body):
1319 for lnum, cstart, cend, line in matchlines(body):
1320 s = linestate(line, lnum, cstart, cend)
1320 s = linestate(line, lnum, cstart, cend)
1321 m.append(s)
1321 m.append(s)
1322
1322
1323 def difflinestates(a, b):
1323 def difflinestates(a, b):
1324 sm = difflib.SequenceMatcher(None, a, b)
1324 sm = difflib.SequenceMatcher(None, a, b)
1325 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1325 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1326 if tag == 'insert':
1326 if tag == 'insert':
1327 for i in xrange(blo, bhi):
1327 for i in xrange(blo, bhi):
1328 yield ('+', b[i])
1328 yield ('+', b[i])
1329 elif tag == 'delete':
1329 elif tag == 'delete':
1330 for i in xrange(alo, ahi):
1330 for i in xrange(alo, ahi):
1331 yield ('-', a[i])
1331 yield ('-', a[i])
1332 elif tag == 'replace':
1332 elif tag == 'replace':
1333 for i in xrange(alo, ahi):
1333 for i in xrange(alo, ahi):
1334 yield ('-', a[i])
1334 yield ('-', a[i])
1335 for i in xrange(blo, bhi):
1335 for i in xrange(blo, bhi):
1336 yield ('+', b[i])
1336 yield ('+', b[i])
1337
1337
1338 def display(fn, ctx, pstates, states):
1338 def display(fn, ctx, pstates, states):
1339 rev = ctx.rev()
1339 rev = ctx.rev()
1340 datefunc = ui.quiet and util.shortdate or util.datestr
1340 datefunc = ui.quiet and util.shortdate or util.datestr
1341 found = False
1341 found = False
1342 filerevmatches = {}
1342 filerevmatches = {}
1343 if opts.get('all'):
1343 if opts.get('all'):
1344 iter = difflinestates(pstates, states)
1344 iter = difflinestates(pstates, states)
1345 else:
1345 else:
1346 iter = [('', l) for l in states]
1346 iter = [('', l) for l in states]
1347 for change, l in iter:
1347 for change, l in iter:
1348 cols = [fn, str(rev)]
1348 cols = [fn, str(rev)]
1349 before, match, after = None, None, None
1349 before, match, after = None, None, None
1350 if opts.get('line_number'):
1350 if opts.get('line_number'):
1351 cols.append(str(l.linenum))
1351 cols.append(str(l.linenum))
1352 if opts.get('all'):
1352 if opts.get('all'):
1353 cols.append(change)
1353 cols.append(change)
1354 if opts.get('user'):
1354 if opts.get('user'):
1355 cols.append(ui.shortuser(ctx.user()))
1355 cols.append(ui.shortuser(ctx.user()))
1356 if opts.get('date'):
1356 if opts.get('date'):
1357 cols.append(datefunc(ctx.date()))
1357 cols.append(datefunc(ctx.date()))
1358 if opts.get('files_with_matches'):
1358 if opts.get('files_with_matches'):
1359 c = (fn, rev)
1359 c = (fn, rev)
1360 if c in filerevmatches:
1360 if c in filerevmatches:
1361 continue
1361 continue
1362 filerevmatches[c] = 1
1362 filerevmatches[c] = 1
1363 else:
1363 else:
1364 before = l.line[:l.colstart]
1364 before = l.line[:l.colstart]
1365 match = l.line[l.colstart:l.colend]
1365 match = l.line[l.colstart:l.colend]
1366 after = l.line[l.colend:]
1366 after = l.line[l.colend:]
1367 ui.write(sep.join(cols))
1367 ui.write(sep.join(cols))
1368 if before is not None:
1368 if before is not None:
1369 ui.write(sep + before)
1369 ui.write(sep + before)
1370 ui.write(match, label='grep.match')
1370 ui.write(match, label='grep.match')
1371 ui.write(after)
1371 ui.write(after)
1372 ui.write(eol)
1372 ui.write(eol)
1373 found = True
1373 found = True
1374 return found
1374 return found
1375
1375
1376 skip = {}
1376 skip = {}
1377 revfiles = {}
1377 revfiles = {}
1378 matchfn = cmdutil.match(repo, pats, opts)
1378 matchfn = cmdutil.match(repo, pats, opts)
1379 found = False
1379 found = False
1380 follow = opts.get('follow')
1380 follow = opts.get('follow')
1381
1381
1382 def prep(ctx, fns):
1382 def prep(ctx, fns):
1383 rev = ctx.rev()
1383 rev = ctx.rev()
1384 pctx = ctx.parents()[0]
1384 pctx = ctx.parents()[0]
1385 parent = pctx.rev()
1385 parent = pctx.rev()
1386 matches.setdefault(rev, {})
1386 matches.setdefault(rev, {})
1387 matches.setdefault(parent, {})
1387 matches.setdefault(parent, {})
1388 files = revfiles.setdefault(rev, [])
1388 files = revfiles.setdefault(rev, [])
1389 for fn in fns:
1389 for fn in fns:
1390 flog = getfile(fn)
1390 flog = getfile(fn)
1391 try:
1391 try:
1392 fnode = ctx.filenode(fn)
1392 fnode = ctx.filenode(fn)
1393 except error.LookupError:
1393 except error.LookupError:
1394 continue
1394 continue
1395
1395
1396 copied = flog.renamed(fnode)
1396 copied = flog.renamed(fnode)
1397 copy = follow and copied and copied[0]
1397 copy = follow and copied and copied[0]
1398 if copy:
1398 if copy:
1399 copies.setdefault(rev, {})[fn] = copy
1399 copies.setdefault(rev, {})[fn] = copy
1400 if fn in skip:
1400 if fn in skip:
1401 if copy:
1401 if copy:
1402 skip[copy] = True
1402 skip[copy] = True
1403 continue
1403 continue
1404 files.append(fn)
1404 files.append(fn)
1405
1405
1406 if fn not in matches[rev]:
1406 if fn not in matches[rev]:
1407 grepbody(fn, rev, flog.read(fnode))
1407 grepbody(fn, rev, flog.read(fnode))
1408
1408
1409 pfn = copy or fn
1409 pfn = copy or fn
1410 if pfn not in matches[parent]:
1410 if pfn not in matches[parent]:
1411 try:
1411 try:
1412 fnode = pctx.filenode(pfn)
1412 fnode = pctx.filenode(pfn)
1413 grepbody(pfn, parent, flog.read(fnode))
1413 grepbody(pfn, parent, flog.read(fnode))
1414 except error.LookupError:
1414 except error.LookupError:
1415 pass
1415 pass
1416
1416
1417 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1417 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1418 rev = ctx.rev()
1418 rev = ctx.rev()
1419 parent = ctx.parents()[0].rev()
1419 parent = ctx.parents()[0].rev()
1420 for fn in sorted(revfiles.get(rev, [])):
1420 for fn in sorted(revfiles.get(rev, [])):
1421 states = matches[rev][fn]
1421 states = matches[rev][fn]
1422 copy = copies.get(rev, {}).get(fn)
1422 copy = copies.get(rev, {}).get(fn)
1423 if fn in skip:
1423 if fn in skip:
1424 if copy:
1424 if copy:
1425 skip[copy] = True
1425 skip[copy] = True
1426 continue
1426 continue
1427 pstates = matches.get(parent, {}).get(copy or fn, [])
1427 pstates = matches.get(parent, {}).get(copy or fn, [])
1428 if pstates or states:
1428 if pstates or states:
1429 r = display(fn, ctx, pstates, states)
1429 r = display(fn, ctx, pstates, states)
1430 found = found or r
1430 found = found or r
1431 if r and not opts.get('all'):
1431 if r and not opts.get('all'):
1432 skip[fn] = True
1432 skip[fn] = True
1433 if copy:
1433 if copy:
1434 skip[copy] = True
1434 skip[copy] = True
1435 del matches[rev]
1435 del matches[rev]
1436 del revfiles[rev]
1436 del revfiles[rev]
1437
1437
1438 def heads(ui, repo, *branchrevs, **opts):
1438 def heads(ui, repo, *branchrevs, **opts):
1439 """show current repository heads or show branch heads
1439 """show current repository heads or show branch heads
1440
1440
1441 With no arguments, show all repository branch heads.
1441 With no arguments, show all repository branch heads.
1442
1442
1443 Repository "heads" are changesets with no child changesets. They are
1443 Repository "heads" are changesets with no child changesets. They are
1444 where development generally takes place and are the usual targets
1444 where development generally takes place and are the usual targets
1445 for update and merge operations. Branch heads are changesets that have
1445 for update and merge operations. Branch heads are changesets that have
1446 no child changeset on the same branch.
1446 no child changeset on the same branch.
1447
1447
1448 If one or more REVs are given, only branch heads on the branches
1448 If one or more REVs are given, only branch heads on the branches
1449 associated with the specified changesets are shown.
1449 associated with the specified changesets are shown.
1450
1450
1451 If -c/--closed is specified, also show branch heads marked closed
1451 If -c/--closed is specified, also show branch heads marked closed
1452 (see hg commit --close-branch).
1452 (see hg commit --close-branch).
1453
1453
1454 If STARTREV is specified, only those heads that are descendants of
1454 If STARTREV is specified, only those heads that are descendants of
1455 STARTREV will be displayed.
1455 STARTREV will be displayed.
1456
1456
1457 If -t/--topo is specified, named branch mechanics will be ignored and only
1457 If -t/--topo is specified, named branch mechanics will be ignored and only
1458 changesets without children will be shown.
1458 changesets without children will be shown.
1459 """
1459 """
1460
1460
1461 if opts.get('rev'):
1461 if opts.get('rev'):
1462 start = repo.lookup(opts['rev'])
1462 start = repo.lookup(opts['rev'])
1463 else:
1463 else:
1464 start = None
1464 start = None
1465
1465
1466 if opts.get('topo'):
1466 if opts.get('topo'):
1467 heads = [repo[h] for h in repo.heads(start)]
1467 heads = [repo[h] for h in repo.heads(start)]
1468 else:
1468 else:
1469 heads = []
1469 heads = []
1470 for b, ls in repo.branchmap().iteritems():
1470 for b, ls in repo.branchmap().iteritems():
1471 if start is None:
1471 if start is None:
1472 heads += [repo[h] for h in ls]
1472 heads += [repo[h] for h in ls]
1473 continue
1473 continue
1474 startrev = repo.changelog.rev(start)
1474 startrev = repo.changelog.rev(start)
1475 descendants = set(repo.changelog.descendants(startrev))
1475 descendants = set(repo.changelog.descendants(startrev))
1476 descendants.add(startrev)
1476 descendants.add(startrev)
1477 rev = repo.changelog.rev
1477 rev = repo.changelog.rev
1478 heads += [repo[h] for h in ls if rev(h) in descendants]
1478 heads += [repo[h] for h in ls if rev(h) in descendants]
1479
1479
1480 if branchrevs:
1480 if branchrevs:
1481 decode, encode = encoding.fromlocal, encoding.tolocal
1481 decode, encode = encoding.fromlocal, encoding.tolocal
1482 branches = set(repo[decode(br)].branch() for br in branchrevs)
1482 branches = set(repo[decode(br)].branch() for br in branchrevs)
1483 heads = [h for h in heads if h.branch() in branches]
1483 heads = [h for h in heads if h.branch() in branches]
1484
1484
1485 if not opts.get('closed'):
1485 if not opts.get('closed'):
1486 heads = [h for h in heads if not h.extra().get('close')]
1486 heads = [h for h in heads if not h.extra().get('close')]
1487
1487
1488 if opts.get('active') and branchrevs:
1488 if opts.get('active') and branchrevs:
1489 dagheads = repo.heads(start)
1489 dagheads = repo.heads(start)
1490 heads = [h for h in heads if h.node() in dagheads]
1490 heads = [h for h in heads if h.node() in dagheads]
1491
1491
1492 if branchrevs:
1492 if branchrevs:
1493 haveheads = set(h.branch() for h in heads)
1493 haveheads = set(h.branch() for h in heads)
1494 if branches - haveheads:
1494 if branches - haveheads:
1495 headless = ', '.join(encode(b) for b in branches - haveheads)
1495 headless = ', '.join(encode(b) for b in branches - haveheads)
1496 msg = _('no open branch heads found on branches %s')
1496 msg = _('no open branch heads found on branches %s')
1497 if opts.get('rev'):
1497 if opts.get('rev'):
1498 msg += _(' (started at %s)' % opts['rev'])
1498 msg += _(' (started at %s)' % opts['rev'])
1499 ui.warn((msg + '\n') % headless)
1499 ui.warn((msg + '\n') % headless)
1500
1500
1501 if not heads:
1501 if not heads:
1502 return 1
1502 return 1
1503
1503
1504 heads = sorted(heads, key=lambda x: -x.rev())
1504 heads = sorted(heads, key=lambda x: -x.rev())
1505 displayer = cmdutil.show_changeset(ui, repo, opts)
1505 displayer = cmdutil.show_changeset(ui, repo, opts)
1506 for ctx in heads:
1506 for ctx in heads:
1507 displayer.show(ctx)
1507 displayer.show(ctx)
1508 displayer.close()
1508 displayer.close()
1509
1509
1510 def help_(ui, name=None, with_version=False, unknowncmd=False):
1510 def help_(ui, name=None, with_version=False, unknowncmd=False):
1511 """show help for a given topic or a help overview
1511 """show help for a given topic or a help overview
1512
1512
1513 With no arguments, print a list of commands with short help messages.
1513 With no arguments, print a list of commands with short help messages.
1514
1514
1515 Given a topic, extension, or command name, print help for that
1515 Given a topic, extension, or command name, print help for that
1516 topic."""
1516 topic."""
1517 option_lists = []
1517 option_lists = []
1518 textwidth = util.termwidth() - 2
1518 textwidth = util.termwidth() - 2
1519
1519
1520 def addglobalopts(aliases):
1520 def addglobalopts(aliases):
1521 if ui.verbose:
1521 if ui.verbose:
1522 option_lists.append((_("global options:"), globalopts))
1522 option_lists.append((_("global options:"), globalopts))
1523 if name == 'shortlist':
1523 if name == 'shortlist':
1524 option_lists.append((_('use "hg help" for the full list '
1524 option_lists.append((_('use "hg help" for the full list '
1525 'of commands'), ()))
1525 'of commands'), ()))
1526 else:
1526 else:
1527 if name == 'shortlist':
1527 if name == 'shortlist':
1528 msg = _('use "hg help" for the full list of commands '
1528 msg = _('use "hg help" for the full list of commands '
1529 'or "hg -v" for details')
1529 'or "hg -v" for details')
1530 elif aliases:
1530 elif aliases:
1531 msg = _('use "hg -v help%s" to show aliases and '
1531 msg = _('use "hg -v help%s" to show aliases and '
1532 'global options') % (name and " " + name or "")
1532 'global options') % (name and " " + name or "")
1533 else:
1533 else:
1534 msg = _('use "hg -v help %s" to show global options') % name
1534 msg = _('use "hg -v help %s" to show global options') % name
1535 option_lists.append((msg, ()))
1535 option_lists.append((msg, ()))
1536
1536
1537 def helpcmd(name):
1537 def helpcmd(name):
1538 if with_version:
1538 if with_version:
1539 version_(ui)
1539 version_(ui)
1540 ui.write('\n')
1540 ui.write('\n')
1541
1541
1542 try:
1542 try:
1543 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1543 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1544 except error.AmbiguousCommand, inst:
1544 except error.AmbiguousCommand, inst:
1545 # py3k fix: except vars can't be used outside the scope of the
1545 # py3k fix: except vars can't be used outside the scope of the
1546 # except block, nor can be used inside a lambda. python issue4617
1546 # except block, nor can be used inside a lambda. python issue4617
1547 prefix = inst.args[0]
1547 prefix = inst.args[0]
1548 select = lambda c: c.lstrip('^').startswith(prefix)
1548 select = lambda c: c.lstrip('^').startswith(prefix)
1549 helplist(_('list of commands:\n\n'), select)
1549 helplist(_('list of commands:\n\n'), select)
1550 return
1550 return
1551
1551
1552 # check if it's an invalid alias and display its error if it is
1552 # check if it's an invalid alias and display its error if it is
1553 if getattr(entry[0], 'badalias', False):
1553 if getattr(entry[0], 'badalias', False):
1554 if not unknowncmd:
1554 if not unknowncmd:
1555 entry[0](ui)
1555 entry[0](ui)
1556 return
1556 return
1557
1557
1558 # synopsis
1558 # synopsis
1559 if len(entry) > 2:
1559 if len(entry) > 2:
1560 if entry[2].startswith('hg'):
1560 if entry[2].startswith('hg'):
1561 ui.write("%s\n" % entry[2])
1561 ui.write("%s\n" % entry[2])
1562 else:
1562 else:
1563 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1563 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1564 else:
1564 else:
1565 ui.write('hg %s\n' % aliases[0])
1565 ui.write('hg %s\n' % aliases[0])
1566
1566
1567 # aliases
1567 # aliases
1568 if not ui.quiet and len(aliases) > 1:
1568 if not ui.quiet and len(aliases) > 1:
1569 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1569 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1570
1570
1571 # description
1571 # description
1572 doc = gettext(entry[0].__doc__)
1572 doc = gettext(entry[0].__doc__)
1573 if not doc:
1573 if not doc:
1574 doc = _("(no help text available)")
1574 doc = _("(no help text available)")
1575 if hasattr(entry[0], 'definition'): # aliased command
1575 if hasattr(entry[0], 'definition'): # aliased command
1576 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1576 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1577 if ui.quiet:
1577 if ui.quiet:
1578 doc = doc.splitlines()[0]
1578 doc = doc.splitlines()[0]
1579 keep = ui.verbose and ['verbose'] or []
1579 keep = ui.verbose and ['verbose'] or []
1580 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1580 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1581 ui.write("\n%s\n" % formatted)
1581 ui.write("\n%s\n" % formatted)
1582 if pruned:
1582 if pruned:
1583 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1583 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1584
1584
1585 if not ui.quiet:
1585 if not ui.quiet:
1586 # options
1586 # options
1587 if entry[1]:
1587 if entry[1]:
1588 option_lists.append((_("options:\n"), entry[1]))
1588 option_lists.append((_("options:\n"), entry[1]))
1589
1589
1590 addglobalopts(False)
1590 addglobalopts(False)
1591
1591
1592 def helplist(header, select=None):
1592 def helplist(header, select=None):
1593 h = {}
1593 h = {}
1594 cmds = {}
1594 cmds = {}
1595 for c, e in table.iteritems():
1595 for c, e in table.iteritems():
1596 f = c.split("|", 1)[0]
1596 f = c.split("|", 1)[0]
1597 if select and not select(f):
1597 if select and not select(f):
1598 continue
1598 continue
1599 if (not select and name != 'shortlist' and
1599 if (not select and name != 'shortlist' and
1600 e[0].__module__ != __name__):
1600 e[0].__module__ != __name__):
1601 continue
1601 continue
1602 if name == "shortlist" and not f.startswith("^"):
1602 if name == "shortlist" and not f.startswith("^"):
1603 continue
1603 continue
1604 f = f.lstrip("^")
1604 f = f.lstrip("^")
1605 if not ui.debugflag and f.startswith("debug"):
1605 if not ui.debugflag and f.startswith("debug"):
1606 continue
1606 continue
1607 doc = e[0].__doc__
1607 doc = e[0].__doc__
1608 if doc and 'DEPRECATED' in doc and not ui.verbose:
1608 if doc and 'DEPRECATED' in doc and not ui.verbose:
1609 continue
1609 continue
1610 doc = gettext(doc)
1610 doc = gettext(doc)
1611 if not doc:
1611 if not doc:
1612 doc = _("(no help text available)")
1612 doc = _("(no help text available)")
1613 h[f] = doc.splitlines()[0].rstrip()
1613 h[f] = doc.splitlines()[0].rstrip()
1614 cmds[f] = c.lstrip("^")
1614 cmds[f] = c.lstrip("^")
1615
1615
1616 if not h:
1616 if not h:
1617 ui.status(_('no commands defined\n'))
1617 ui.status(_('no commands defined\n'))
1618 return
1618 return
1619
1619
1620 ui.status(header)
1620 ui.status(header)
1621 fns = sorted(h)
1621 fns = sorted(h)
1622 m = max(map(len, fns))
1622 m = max(map(len, fns))
1623 for f in fns:
1623 for f in fns:
1624 if ui.verbose:
1624 if ui.verbose:
1625 commands = cmds[f].replace("|",", ")
1625 commands = cmds[f].replace("|",", ")
1626 ui.write(" %s:\n %s\n"%(commands, h[f]))
1626 ui.write(" %s:\n %s\n"%(commands, h[f]))
1627 else:
1627 else:
1628 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1628 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1629
1629
1630 if not ui.quiet:
1630 if not ui.quiet:
1631 addglobalopts(True)
1631 addglobalopts(True)
1632
1632
1633 def helptopic(name):
1633 def helptopic(name):
1634 for names, header, doc in help.helptable:
1634 for names, header, doc in help.helptable:
1635 if name in names:
1635 if name in names:
1636 break
1636 break
1637 else:
1637 else:
1638 raise error.UnknownCommand(name)
1638 raise error.UnknownCommand(name)
1639
1639
1640 # description
1640 # description
1641 if not doc:
1641 if not doc:
1642 doc = _("(no help text available)")
1642 doc = _("(no help text available)")
1643 if hasattr(doc, '__call__'):
1643 if hasattr(doc, '__call__'):
1644 doc = doc()
1644 doc = doc()
1645
1645
1646 ui.write("%s\n\n" % header)
1646 ui.write("%s\n\n" % header)
1647 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1647 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1648
1648
1649 def helpext(name):
1649 def helpext(name):
1650 try:
1650 try:
1651 mod = extensions.find(name)
1651 mod = extensions.find(name)
1652 doc = gettext(mod.__doc__) or _('no help text available')
1652 doc = gettext(mod.__doc__) or _('no help text available')
1653 except KeyError:
1653 except KeyError:
1654 mod = None
1654 mod = None
1655 doc = extensions.disabledext(name)
1655 doc = extensions.disabledext(name)
1656 if not doc:
1656 if not doc:
1657 raise error.UnknownCommand(name)
1657 raise error.UnknownCommand(name)
1658
1658
1659 if '\n' not in doc:
1659 if '\n' not in doc:
1660 head, tail = doc, ""
1660 head, tail = doc, ""
1661 else:
1661 else:
1662 head, tail = doc.split('\n', 1)
1662 head, tail = doc.split('\n', 1)
1663 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1663 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1664 if tail:
1664 if tail:
1665 ui.write(minirst.format(tail, textwidth))
1665 ui.write(minirst.format(tail, textwidth))
1666 ui.status('\n\n')
1666 ui.status('\n\n')
1667
1667
1668 if mod:
1668 if mod:
1669 try:
1669 try:
1670 ct = mod.cmdtable
1670 ct = mod.cmdtable
1671 except AttributeError:
1671 except AttributeError:
1672 ct = {}
1672 ct = {}
1673 modcmds = set([c.split('|', 1)[0] for c in ct])
1673 modcmds = set([c.split('|', 1)[0] for c in ct])
1674 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1674 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1675 else:
1675 else:
1676 ui.write(_('use "hg help extensions" for information on enabling '
1676 ui.write(_('use "hg help extensions" for information on enabling '
1677 'extensions\n'))
1677 'extensions\n'))
1678
1678
1679 def helpextcmd(name):
1679 def helpextcmd(name):
1680 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1680 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1681 doc = gettext(mod.__doc__).splitlines()[0]
1681 doc = gettext(mod.__doc__).splitlines()[0]
1682
1682
1683 msg = help.listexts(_("'%s' is provided by the following "
1683 msg = help.listexts(_("'%s' is provided by the following "
1684 "extension:") % cmd, {ext: doc}, len(ext),
1684 "extension:") % cmd, {ext: doc}, len(ext),
1685 indent=4)
1685 indent=4)
1686 ui.write(minirst.format(msg, textwidth))
1686 ui.write(minirst.format(msg, textwidth))
1687 ui.write('\n\n')
1687 ui.write('\n\n')
1688 ui.write(_('use "hg help extensions" for information on enabling '
1688 ui.write(_('use "hg help extensions" for information on enabling '
1689 'extensions\n'))
1689 'extensions\n'))
1690
1690
1691 if name and name != 'shortlist':
1691 if name and name != 'shortlist':
1692 i = None
1692 i = None
1693 if unknowncmd:
1693 if unknowncmd:
1694 queries = (helpextcmd,)
1694 queries = (helpextcmd,)
1695 else:
1695 else:
1696 queries = (helptopic, helpcmd, helpext, helpextcmd)
1696 queries = (helptopic, helpcmd, helpext, helpextcmd)
1697 for f in queries:
1697 for f in queries:
1698 try:
1698 try:
1699 f(name)
1699 f(name)
1700 i = None
1700 i = None
1701 break
1701 break
1702 except error.UnknownCommand, inst:
1702 except error.UnknownCommand, inst:
1703 i = inst
1703 i = inst
1704 if i:
1704 if i:
1705 raise i
1705 raise i
1706
1706
1707 else:
1707 else:
1708 # program name
1708 # program name
1709 if ui.verbose or with_version:
1709 if ui.verbose or with_version:
1710 version_(ui)
1710 version_(ui)
1711 else:
1711 else:
1712 ui.status(_("Mercurial Distributed SCM\n"))
1712 ui.status(_("Mercurial Distributed SCM\n"))
1713 ui.status('\n')
1713 ui.status('\n')
1714
1714
1715 # list of commands
1715 # list of commands
1716 if name == "shortlist":
1716 if name == "shortlist":
1717 header = _('basic commands:\n\n')
1717 header = _('basic commands:\n\n')
1718 else:
1718 else:
1719 header = _('list of commands:\n\n')
1719 header = _('list of commands:\n\n')
1720
1720
1721 helplist(header)
1721 helplist(header)
1722 if name != 'shortlist':
1722 if name != 'shortlist':
1723 exts, maxlength = extensions.enabled()
1723 exts, maxlength = extensions.enabled()
1724 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1724 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1725 if text:
1725 if text:
1726 ui.write("\n%s\n" % minirst.format(text, textwidth))
1726 ui.write("\n%s\n" % minirst.format(text, textwidth))
1727
1727
1728 # list all option lists
1728 # list all option lists
1729 opt_output = []
1729 opt_output = []
1730 for title, options in option_lists:
1730 for title, options in option_lists:
1731 opt_output.append(("\n%s" % title, None))
1731 opt_output.append(("\n%s" % title, None))
1732 for shortopt, longopt, default, desc in options:
1732 for shortopt, longopt, default, desc in options:
1733 if _("DEPRECATED") in desc and not ui.verbose:
1733 if _("DEPRECATED") in desc and not ui.verbose:
1734 continue
1734 continue
1735 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1735 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1736 longopt and " --%s" % longopt),
1736 longopt and " --%s" % longopt),
1737 "%s%s" % (desc,
1737 "%s%s" % (desc,
1738 default
1738 default
1739 and _(" (default: %s)") % default
1739 and _(" (default: %s)") % default
1740 or "")))
1740 or "")))
1741
1741
1742 if not name:
1742 if not name:
1743 ui.write(_("\nadditional help topics:\n\n"))
1743 ui.write(_("\nadditional help topics:\n\n"))
1744 topics = []
1744 topics = []
1745 for names, header, doc in help.helptable:
1745 for names, header, doc in help.helptable:
1746 topics.append((sorted(names, key=len, reverse=True)[0], header))
1746 topics.append((sorted(names, key=len, reverse=True)[0], header))
1747 topics_len = max([len(s[0]) for s in topics])
1747 topics_len = max([len(s[0]) for s in topics])
1748 for t, desc in topics:
1748 for t, desc in topics:
1749 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1749 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1750
1750
1751 if opt_output:
1751 if opt_output:
1752 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1752 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1753 for first, second in opt_output:
1753 for first, second in opt_output:
1754 if second:
1754 if second:
1755 second = util.wrap(second, opts_len + 3)
1755 second = util.wrap(second, opts_len + 3)
1756 ui.write(" %-*s %s\n" % (opts_len, first, second))
1756 ui.write(" %-*s %s\n" % (opts_len, first, second))
1757 else:
1757 else:
1758 ui.write("%s\n" % first)
1758 ui.write("%s\n" % first)
1759
1759
1760 def identify(ui, repo, source=None,
1760 def identify(ui, repo, source=None,
1761 rev=None, num=None, id=None, branch=None, tags=None):
1761 rev=None, num=None, id=None, branch=None, tags=None):
1762 """identify the working copy or specified revision
1762 """identify the working copy or specified revision
1763
1763
1764 With no revision, print a summary of the current state of the
1764 With no revision, print a summary of the current state of the
1765 repository.
1765 repository.
1766
1766
1767 Specifying a path to a repository root or Mercurial bundle will
1767 Specifying a path to a repository root or Mercurial bundle will
1768 cause lookup to operate on that repository/bundle.
1768 cause lookup to operate on that repository/bundle.
1769
1769
1770 This summary identifies the repository state using one or two
1770 This summary identifies the repository state using one or two
1771 parent hash identifiers, followed by a "+" if there are
1771 parent hash identifiers, followed by a "+" if there are
1772 uncommitted changes in the working directory, a list of tags for
1772 uncommitted changes in the working directory, a list of tags for
1773 this revision and a branch name for non-default branches.
1773 this revision and a branch name for non-default branches.
1774 """
1774 """
1775
1775
1776 if not repo and not source:
1776 if not repo and not source:
1777 raise util.Abort(_("There is no Mercurial repository here "
1777 raise util.Abort(_("There is no Mercurial repository here "
1778 "(.hg not found)"))
1778 "(.hg not found)"))
1779
1779
1780 hexfunc = ui.debugflag and hex or short
1780 hexfunc = ui.debugflag and hex or short
1781 default = not (num or id or branch or tags)
1781 default = not (num or id or branch or tags)
1782 output = []
1782 output = []
1783
1783
1784 revs = []
1784 revs = []
1785 if source:
1785 if source:
1786 source, branches = hg.parseurl(ui.expandpath(source))
1786 source, branches = hg.parseurl(ui.expandpath(source))
1787 repo = hg.repository(ui, source)
1787 repo = hg.repository(ui, source)
1788 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1788 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1789
1789
1790 if not repo.local():
1790 if not repo.local():
1791 if not rev and revs:
1791 if not rev and revs:
1792 rev = revs[0]
1792 rev = revs[0]
1793 if not rev:
1793 if not rev:
1794 rev = "tip"
1794 rev = "tip"
1795 if num or branch or tags:
1795 if num or branch or tags:
1796 raise util.Abort(
1796 raise util.Abort(
1797 "can't query remote revision number, branch, or tags")
1797 "can't query remote revision number, branch, or tags")
1798 output = [hexfunc(repo.lookup(rev))]
1798 output = [hexfunc(repo.lookup(rev))]
1799 elif not rev:
1799 elif not rev:
1800 ctx = repo[None]
1800 ctx = repo[None]
1801 parents = ctx.parents()
1801 parents = ctx.parents()
1802 changed = False
1802 changed = False
1803 if default or id or num:
1803 if default or id or num:
1804 changed = util.any(repo.status())
1804 changed = util.any(repo.status())
1805 if default or id:
1805 if default or id:
1806 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1806 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1807 (changed) and "+" or "")]
1807 (changed) and "+" or "")]
1808 if num:
1808 if num:
1809 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1809 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1810 (changed) and "+" or ""))
1810 (changed) and "+" or ""))
1811 else:
1811 else:
1812 ctx = repo[rev]
1812 ctx = repo[rev]
1813 if default or id:
1813 if default or id:
1814 output = [hexfunc(ctx.node())]
1814 output = [hexfunc(ctx.node())]
1815 if num:
1815 if num:
1816 output.append(str(ctx.rev()))
1816 output.append(str(ctx.rev()))
1817
1817
1818 if repo.local() and default and not ui.quiet:
1818 if repo.local() and default and not ui.quiet:
1819 b = encoding.tolocal(ctx.branch())
1819 b = encoding.tolocal(ctx.branch())
1820 if b != 'default':
1820 if b != 'default':
1821 output.append("(%s)" % b)
1821 output.append("(%s)" % b)
1822
1822
1823 # multiple tags for a single parent separated by '/'
1823 # multiple tags for a single parent separated by '/'
1824 t = "/".join(ctx.tags())
1824 t = "/".join(ctx.tags())
1825 if t:
1825 if t:
1826 output.append(t)
1826 output.append(t)
1827
1827
1828 if branch:
1828 if branch:
1829 output.append(encoding.tolocal(ctx.branch()))
1829 output.append(encoding.tolocal(ctx.branch()))
1830
1830
1831 if tags:
1831 if tags:
1832 output.extend(ctx.tags())
1832 output.extend(ctx.tags())
1833
1833
1834 ui.write("%s\n" % ' '.join(output))
1834 ui.write("%s\n" % ' '.join(output))
1835
1835
1836 def import_(ui, repo, patch1, *patches, **opts):
1836 def import_(ui, repo, patch1, *patches, **opts):
1837 """import an ordered set of patches
1837 """import an ordered set of patches
1838
1838
1839 Import a list of patches and commit them individually (unless
1839 Import a list of patches and commit them individually (unless
1840 --no-commit is specified).
1840 --no-commit is specified).
1841
1841
1842 If there are outstanding changes in the working directory, import
1842 If there are outstanding changes in the working directory, import
1843 will abort unless given the -f/--force flag.
1843 will abort unless given the -f/--force flag.
1844
1844
1845 You can import a patch straight from a mail message. Even patches
1845 You can import a patch straight from a mail message. Even patches
1846 as attachments work (to use the body part, it must have type
1846 as attachments work (to use the body part, it must have type
1847 text/plain or text/x-patch). From and Subject headers of email
1847 text/plain or text/x-patch). From and Subject headers of email
1848 message are used as default committer and commit message. All
1848 message are used as default committer and commit message. All
1849 text/plain body parts before first diff are added to commit
1849 text/plain body parts before first diff are added to commit
1850 message.
1850 message.
1851
1851
1852 If the imported patch was generated by hg export, user and
1852 If the imported patch was generated by hg export, user and
1853 description from patch override values from message headers and
1853 description from patch override values from message headers and
1854 body. Values given on command line with -m/--message and -u/--user
1854 body. Values given on command line with -m/--message and -u/--user
1855 override these.
1855 override these.
1856
1856
1857 If --exact is specified, import will set the working directory to
1857 If --exact is specified, import will set the working directory to
1858 the parent of each patch before applying it, and will abort if the
1858 the parent of each patch before applying it, and will abort if the
1859 resulting changeset has a different ID than the one recorded in
1859 resulting changeset has a different ID than the one recorded in
1860 the patch. This may happen due to character set problems or other
1860 the patch. This may happen due to character set problems or other
1861 deficiencies in the text patch format.
1861 deficiencies in the text patch format.
1862
1862
1863 With -s/--similarity, hg will attempt to discover renames and
1863 With -s/--similarity, hg will attempt to discover renames and
1864 copies in the patch in the same way as 'addremove'.
1864 copies in the patch in the same way as 'addremove'.
1865
1865
1866 To read a patch from standard input, use "-" as the patch name. If
1866 To read a patch from standard input, use "-" as the patch name. If
1867 a URL is specified, the patch will be downloaded from it.
1867 a URL is specified, the patch will be downloaded from it.
1868 See :hg:`help dates` for a list of formats valid for -d/--date.
1868 See :hg:`help dates` for a list of formats valid for -d/--date.
1869 """
1869 """
1870 patches = (patch1,) + patches
1870 patches = (patch1,) + patches
1871
1871
1872 date = opts.get('date')
1872 date = opts.get('date')
1873 if date:
1873 if date:
1874 opts['date'] = util.parsedate(date)
1874 opts['date'] = util.parsedate(date)
1875
1875
1876 try:
1876 try:
1877 sim = float(opts.get('similarity') or 0)
1877 sim = float(opts.get('similarity') or 0)
1878 except ValueError:
1878 except ValueError:
1879 raise util.Abort(_('similarity must be a number'))
1879 raise util.Abort(_('similarity must be a number'))
1880 if sim < 0 or sim > 100:
1880 if sim < 0 or sim > 100:
1881 raise util.Abort(_('similarity must be between 0 and 100'))
1881 raise util.Abort(_('similarity must be between 0 and 100'))
1882
1882
1883 if opts.get('exact') or not opts.get('force'):
1883 if opts.get('exact') or not opts.get('force'):
1884 cmdutil.bail_if_changed(repo)
1884 cmdutil.bail_if_changed(repo)
1885
1885
1886 d = opts["base"]
1886 d = opts["base"]
1887 strip = opts["strip"]
1887 strip = opts["strip"]
1888 wlock = lock = None
1888 wlock = lock = None
1889
1889
1890 def tryone(ui, hunk):
1890 def tryone(ui, hunk):
1891 tmpname, message, user, date, branch, nodeid, p1, p2 = \
1891 tmpname, message, user, date, branch, nodeid, p1, p2 = \
1892 patch.extract(ui, hunk)
1892 patch.extract(ui, hunk)
1893
1893
1894 if not tmpname:
1894 if not tmpname:
1895 return None
1895 return None
1896 commitid = _('to working directory')
1896 commitid = _('to working directory')
1897
1897
1898 try:
1898 try:
1899 cmdline_message = cmdutil.logmessage(opts)
1899 cmdline_message = cmdutil.logmessage(opts)
1900 if cmdline_message:
1900 if cmdline_message:
1901 # pickup the cmdline msg
1901 # pickup the cmdline msg
1902 message = cmdline_message
1902 message = cmdline_message
1903 elif message:
1903 elif message:
1904 # pickup the patch msg
1904 # pickup the patch msg
1905 message = message.strip()
1905 message = message.strip()
1906 else:
1906 else:
1907 # launch the editor
1907 # launch the editor
1908 message = None
1908 message = None
1909 ui.debug('message:\n%s\n' % message)
1909 ui.debug('message:\n%s\n' % message)
1910
1910
1911 wp = repo.parents()
1911 wp = repo.parents()
1912 if opts.get('exact'):
1912 if opts.get('exact'):
1913 if not nodeid or not p1:
1913 if not nodeid or not p1:
1914 raise util.Abort(_('not a Mercurial patch'))
1914 raise util.Abort(_('not a Mercurial patch'))
1915 p1 = repo.lookup(p1)
1915 p1 = repo.lookup(p1)
1916 p2 = repo.lookup(p2 or hex(nullid))
1916 p2 = repo.lookup(p2 or hex(nullid))
1917
1917
1918 if p1 != wp[0].node():
1918 if p1 != wp[0].node():
1919 hg.clean(repo, p1)
1919 hg.clean(repo, p1)
1920 repo.dirstate.setparents(p1, p2)
1920 repo.dirstate.setparents(p1, p2)
1921 elif p2:
1921 elif p2:
1922 try:
1922 try:
1923 p1 = repo.lookup(p1)
1923 p1 = repo.lookup(p1)
1924 p2 = repo.lookup(p2)
1924 p2 = repo.lookup(p2)
1925 if p1 == wp[0].node():
1925 if p1 == wp[0].node():
1926 repo.dirstate.setparents(p1, p2)
1926 repo.dirstate.setparents(p1, p2)
1927 except error.RepoError:
1927 except error.RepoError:
1928 pass
1928 pass
1929 if opts.get('exact') or opts.get('import_branch'):
1929 if opts.get('exact') or opts.get('import_branch'):
1930 repo.dirstate.setbranch(branch or 'default')
1930 repo.dirstate.setbranch(branch or 'default')
1931
1931
1932 files = {}
1932 files = {}
1933 try:
1933 try:
1934 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1934 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1935 files=files, eolmode=None)
1935 files=files, eolmode=None)
1936 finally:
1936 finally:
1937 files = patch.updatedir(ui, repo, files,
1937 files = patch.updatedir(ui, repo, files,
1938 similarity=sim / 100.0)
1938 similarity=sim / 100.0)
1939 if not opts.get('no_commit'):
1939 if not opts.get('no_commit'):
1940 if opts.get('exact'):
1940 if opts.get('exact'):
1941 m = None
1941 m = None
1942 else:
1942 else:
1943 m = cmdutil.matchfiles(repo, files or [])
1943 m = cmdutil.matchfiles(repo, files or [])
1944 n = repo.commit(message, opts.get('user') or user,
1944 n = repo.commit(message, opts.get('user') or user,
1945 opts.get('date') or date, match=m,
1945 opts.get('date') or date, match=m,
1946 editor=cmdutil.commiteditor)
1946 editor=cmdutil.commiteditor)
1947 if opts.get('exact'):
1947 if opts.get('exact'):
1948 if hex(n) != nodeid:
1948 if hex(n) != nodeid:
1949 repo.rollback()
1949 repo.rollback()
1950 raise util.Abort(_('patch is damaged'
1950 raise util.Abort(_('patch is damaged'
1951 ' or loses information'))
1951 ' or loses information'))
1952 # Force a dirstate write so that the next transaction
1952 # Force a dirstate write so that the next transaction
1953 # backups an up-do-date file.
1953 # backups an up-do-date file.
1954 repo.dirstate.write()
1954 repo.dirstate.write()
1955 if n:
1955 if n:
1956 commitid = short(n)
1956 commitid = short(n)
1957
1957
1958 return commitid
1958 return commitid
1959 finally:
1959 finally:
1960 os.unlink(tmpname)
1960 os.unlink(tmpname)
1961
1961
1962 try:
1962 try:
1963 wlock = repo.wlock()
1963 wlock = repo.wlock()
1964 lock = repo.lock()
1964 lock = repo.lock()
1965 lastcommit = None
1965 lastcommit = None
1966 for p in patches:
1966 for p in patches:
1967 pf = os.path.join(d, p)
1967 pf = os.path.join(d, p)
1968
1968
1969 if pf == '-':
1969 if pf == '-':
1970 ui.status(_("applying patch from stdin\n"))
1970 ui.status(_("applying patch from stdin\n"))
1971 pf = sys.stdin
1971 pf = sys.stdin
1972 else:
1972 else:
1973 ui.status(_("applying %s\n") % p)
1973 ui.status(_("applying %s\n") % p)
1974 pf = url.open(ui, pf)
1974 pf = url.open(ui, pf)
1975
1975
1976 haspatch = False
1976 haspatch = False
1977 for hunk in patch.split(pf):
1977 for hunk in patch.split(pf):
1978 commitid = tryone(ui, hunk)
1978 commitid = tryone(ui, hunk)
1979 if commitid:
1979 if commitid:
1980 haspatch = True
1980 haspatch = True
1981 if lastcommit:
1981 if lastcommit:
1982 ui.status(_('applied %s\n') % lastcommit)
1982 ui.status(_('applied %s\n') % lastcommit)
1983 lastcommit = commitid
1983 lastcommit = commitid
1984
1984
1985 if not haspatch:
1985 if not haspatch:
1986 raise util.Abort(_('no diffs found'))
1986 raise util.Abort(_('no diffs found'))
1987
1987
1988 finally:
1988 finally:
1989 release(lock, wlock)
1989 release(lock, wlock)
1990
1990
1991 def incoming(ui, repo, source="default", **opts):
1991 def incoming(ui, repo, source="default", **opts):
1992 """show new changesets found in source
1992 """show new changesets found in source
1993
1993
1994 Show new changesets found in the specified path/URL or the default
1994 Show new changesets found in the specified path/URL or the default
1995 pull location. These are the changesets that would have been pulled
1995 pull location. These are the changesets that would have been pulled
1996 if a pull at the time you issued this command.
1996 if a pull at the time you issued this command.
1997
1997
1998 For remote repository, using --bundle avoids downloading the
1998 For remote repository, using --bundle avoids downloading the
1999 changesets twice if the incoming is followed by a pull.
1999 changesets twice if the incoming is followed by a pull.
2000
2000
2001 See pull for valid source format details.
2001 See pull for valid source format details.
2002 """
2002 """
2003 limit = cmdutil.loglimit(opts)
2003 limit = cmdutil.loglimit(opts)
2004 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2004 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2005 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2005 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2006 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2006 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2007 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2007 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2008 if revs:
2008 if revs:
2009 revs = [other.lookup(rev) for rev in revs]
2009 revs = [other.lookup(rev) for rev in revs]
2010 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
2010 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
2011 force=opts["force"])
2011 force=opts["force"])
2012 if not incoming:
2012 if not incoming:
2013 try:
2013 try:
2014 os.unlink(opts["bundle"])
2014 os.unlink(opts["bundle"])
2015 except:
2015 except:
2016 pass
2016 pass
2017 ui.status(_("no changes found\n"))
2017 ui.status(_("no changes found\n"))
2018 return 1
2018 return 1
2019
2019
2020 cleanup = None
2020 cleanup = None
2021 try:
2021 try:
2022 fname = opts["bundle"]
2022 fname = opts["bundle"]
2023 if fname or not other.local():
2023 if fname or not other.local():
2024 # create a bundle (uncompressed if other repo is not local)
2024 # create a bundle (uncompressed if other repo is not local)
2025
2025
2026 if revs is None and other.capable('changegroupsubset'):
2026 if revs is None and other.capable('changegroupsubset'):
2027 revs = rheads
2027 revs = rheads
2028
2028
2029 if revs is None:
2029 if revs is None:
2030 cg = other.changegroup(incoming, "incoming")
2030 cg = other.changegroup(incoming, "incoming")
2031 else:
2031 else:
2032 cg = other.changegroupsubset(incoming, revs, 'incoming')
2032 cg = other.changegroupsubset(incoming, revs, 'incoming')
2033 bundletype = other.local() and "HG10BZ" or "HG10UN"
2033 bundletype = other.local() and "HG10BZ" or "HG10UN"
2034 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2034 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2035 # keep written bundle?
2035 # keep written bundle?
2036 if opts["bundle"]:
2036 if opts["bundle"]:
2037 cleanup = None
2037 cleanup = None
2038 if not other.local():
2038 if not other.local():
2039 # use the created uncompressed bundlerepo
2039 # use the created uncompressed bundlerepo
2040 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2040 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2041
2041
2042 o = other.changelog.nodesbetween(incoming, revs)[0]
2042 o = other.changelog.nodesbetween(incoming, revs)[0]
2043 if opts.get('newest_first'):
2043 if opts.get('newest_first'):
2044 o.reverse()
2044 o.reverse()
2045 displayer = cmdutil.show_changeset(ui, other, opts)
2045 displayer = cmdutil.show_changeset(ui, other, opts)
2046 count = 0
2046 count = 0
2047 for n in o:
2047 for n in o:
2048 if limit is not None and count >= limit:
2048 if limit is not None and count >= limit:
2049 break
2049 break
2050 parents = [p for p in other.changelog.parents(n) if p != nullid]
2050 parents = [p for p in other.changelog.parents(n) if p != nullid]
2051 if opts.get('no_merges') and len(parents) == 2:
2051 if opts.get('no_merges') and len(parents) == 2:
2052 continue
2052 continue
2053 count += 1
2053 count += 1
2054 displayer.show(other[n])
2054 displayer.show(other[n])
2055 displayer.close()
2055 displayer.close()
2056 finally:
2056 finally:
2057 if hasattr(other, 'close'):
2057 if hasattr(other, 'close'):
2058 other.close()
2058 other.close()
2059 if cleanup:
2059 if cleanup:
2060 os.unlink(cleanup)
2060 os.unlink(cleanup)
2061
2061
2062 def init(ui, dest=".", **opts):
2062 def init(ui, dest=".", **opts):
2063 """create a new repository in the given directory
2063 """create a new repository in the given directory
2064
2064
2065 Initialize a new repository in the given directory. If the given
2065 Initialize a new repository in the given directory. If the given
2066 directory does not exist, it will be created.
2066 directory does not exist, it will be created.
2067
2067
2068 If no directory is given, the current directory is used.
2068 If no directory is given, the current directory is used.
2069
2069
2070 It is possible to specify an ``ssh://`` URL as the destination.
2070 It is possible to specify an ``ssh://`` URL as the destination.
2071 See :hg:`help urls` for more information.
2071 See :hg:`help urls` for more information.
2072 """
2072 """
2073 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
2073 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
2074
2074
2075 def locate(ui, repo, *pats, **opts):
2075 def locate(ui, repo, *pats, **opts):
2076 """locate files matching specific patterns
2076 """locate files matching specific patterns
2077
2077
2078 Print files under Mercurial control in the working directory whose
2078 Print files under Mercurial control in the working directory whose
2079 names match the given patterns.
2079 names match the given patterns.
2080
2080
2081 By default, this command searches all directories in the working
2081 By default, this command searches all directories in the working
2082 directory. To search just the current directory and its
2082 directory. To search just the current directory and its
2083 subdirectories, use "--include .".
2083 subdirectories, use "--include .".
2084
2084
2085 If no patterns are given to match, this command prints the names
2085 If no patterns are given to match, this command prints the names
2086 of all files under Mercurial control in the working directory.
2086 of all files under Mercurial control in the working directory.
2087
2087
2088 If you want to feed the output of this command into the "xargs"
2088 If you want to feed the output of this command into the "xargs"
2089 command, use the -0 option to both this command and "xargs". This
2089 command, use the -0 option to both this command and "xargs". This
2090 will avoid the problem of "xargs" treating single filenames that
2090 will avoid the problem of "xargs" treating single filenames that
2091 contain whitespace as multiple filenames.
2091 contain whitespace as multiple filenames.
2092 """
2092 """
2093 end = opts.get('print0') and '\0' or '\n'
2093 end = opts.get('print0') and '\0' or '\n'
2094 rev = opts.get('rev') or None
2094 rev = opts.get('rev') or None
2095
2095
2096 ret = 1
2096 ret = 1
2097 m = cmdutil.match(repo, pats, opts, default='relglob')
2097 m = cmdutil.match(repo, pats, opts, default='relglob')
2098 m.bad = lambda x, y: False
2098 m.bad = lambda x, y: False
2099 for abs in repo[rev].walk(m):
2099 for abs in repo[rev].walk(m):
2100 if not rev and abs not in repo.dirstate:
2100 if not rev and abs not in repo.dirstate:
2101 continue
2101 continue
2102 if opts.get('fullpath'):
2102 if opts.get('fullpath'):
2103 ui.write(repo.wjoin(abs), end)
2103 ui.write(repo.wjoin(abs), end)
2104 else:
2104 else:
2105 ui.write(((pats and m.rel(abs)) or abs), end)
2105 ui.write(((pats and m.rel(abs)) or abs), end)
2106 ret = 0
2106 ret = 0
2107
2107
2108 return ret
2108 return ret
2109
2109
2110 def log(ui, repo, *pats, **opts):
2110 def log(ui, repo, *pats, **opts):
2111 """show revision history of entire repository or files
2111 """show revision history of entire repository or files
2112
2112
2113 Print the revision history of the specified files or the entire
2113 Print the revision history of the specified files or the entire
2114 project.
2114 project.
2115
2115
2116 File history is shown without following rename or copy history of
2116 File history is shown without following rename or copy history of
2117 files. Use -f/--follow with a filename to follow history across
2117 files. Use -f/--follow with a filename to follow history across
2118 renames and copies. --follow without a filename will only show
2118 renames and copies. --follow without a filename will only show
2119 ancestors or descendants of the starting revision. --follow-first
2119 ancestors or descendants of the starting revision. --follow-first
2120 only follows the first parent of merge revisions.
2120 only follows the first parent of merge revisions.
2121
2121
2122 If no revision range is specified, the default is tip:0 unless
2122 If no revision range is specified, the default is tip:0 unless
2123 --follow is set, in which case the working directory parent is
2123 --follow is set, in which case the working directory parent is
2124 used as the starting revision.
2124 used as the starting revision.
2125
2125
2126 See :hg:`help dates` for a list of formats valid for -d/--date.
2126 See :hg:`help dates` for a list of formats valid for -d/--date.
2127
2127
2128 By default this command prints revision number and changeset id,
2128 By default this command prints revision number and changeset id,
2129 tags, non-trivial parents, user, date and time, and a summary for
2129 tags, non-trivial parents, user, date and time, and a summary for
2130 each commit. When the -v/--verbose switch is used, the list of
2130 each commit. When the -v/--verbose switch is used, the list of
2131 changed files and full commit message are shown.
2131 changed files and full commit message are shown.
2132
2132
2133 NOTE: log -p/--patch may generate unexpected diff output for merge
2133 NOTE: log -p/--patch may generate unexpected diff output for merge
2134 changesets, as it will only compare the merge changeset against
2134 changesets, as it will only compare the merge changeset against
2135 its first parent. Also, only files different from BOTH parents
2135 its first parent. Also, only files different from BOTH parents
2136 will appear in files:.
2136 will appear in files:.
2137 """
2137 """
2138
2138
2139 matchfn = cmdutil.match(repo, pats, opts)
2139 matchfn = cmdutil.match(repo, pats, opts)
2140 limit = cmdutil.loglimit(opts)
2140 limit = cmdutil.loglimit(opts)
2141 count = 0
2141 count = 0
2142
2142
2143 endrev = None
2143 endrev = None
2144 if opts.get('copies') and opts.get('rev'):
2144 if opts.get('copies') and opts.get('rev'):
2145 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2145 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2146
2146
2147 df = False
2147 df = False
2148 if opts["date"]:
2148 if opts["date"]:
2149 df = util.matchdate(opts["date"])
2149 df = util.matchdate(opts["date"])
2150
2150
2151 branches = opts.get('branch', []) + opts.get('only_branch', [])
2151 branches = opts.get('branch', []) + opts.get('only_branch', [])
2152 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2152 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2153
2153
2154 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2154 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2155 def prep(ctx, fns):
2155 def prep(ctx, fns):
2156 rev = ctx.rev()
2156 rev = ctx.rev()
2157 parents = [p for p in repo.changelog.parentrevs(rev)
2157 parents = [p for p in repo.changelog.parentrevs(rev)
2158 if p != nullrev]
2158 if p != nullrev]
2159 if opts.get('no_merges') and len(parents) == 2:
2159 if opts.get('no_merges') and len(parents) == 2:
2160 return
2160 return
2161 if opts.get('only_merges') and len(parents) != 2:
2161 if opts.get('only_merges') and len(parents) != 2:
2162 return
2162 return
2163 if opts.get('branch') and ctx.branch() not in opts['branch']:
2163 if opts.get('branch') and ctx.branch() not in opts['branch']:
2164 return
2164 return
2165 if df and not df(ctx.date()[0]):
2165 if df and not df(ctx.date()[0]):
2166 return
2166 return
2167 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2167 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2168 return
2168 return
2169 if opts.get('keyword'):
2169 if opts.get('keyword'):
2170 for k in [kw.lower() for kw in opts['keyword']]:
2170 for k in [kw.lower() for kw in opts['keyword']]:
2171 if (k in ctx.user().lower() or
2171 if (k in ctx.user().lower() or
2172 k in ctx.description().lower() or
2172 k in ctx.description().lower() or
2173 k in " ".join(ctx.files()).lower()):
2173 k in " ".join(ctx.files()).lower()):
2174 break
2174 break
2175 else:
2175 else:
2176 return
2176 return
2177
2177
2178 copies = None
2178 copies = None
2179 if opts.get('copies') and rev:
2179 if opts.get('copies') and rev:
2180 copies = []
2180 copies = []
2181 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2181 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2182 for fn in ctx.files():
2182 for fn in ctx.files():
2183 rename = getrenamed(fn, rev)
2183 rename = getrenamed(fn, rev)
2184 if rename:
2184 if rename:
2185 copies.append((fn, rename[0]))
2185 copies.append((fn, rename[0]))
2186
2186
2187 displayer.show(ctx, copies=copies)
2187 displayer.show(ctx, copies=copies)
2188
2188
2189 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2189 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2190 if count == limit:
2190 if count == limit:
2191 break
2191 break
2192 if displayer.flush(ctx.rev()):
2192 if displayer.flush(ctx.rev()):
2193 count += 1
2193 count += 1
2194 displayer.close()
2194 displayer.close()
2195
2195
2196 def manifest(ui, repo, node=None, rev=None):
2196 def manifest(ui, repo, node=None, rev=None):
2197 """output the current or given revision of the project manifest
2197 """output the current or given revision of the project manifest
2198
2198
2199 Print a list of version controlled files for the given revision.
2199 Print a list of version controlled files for the given revision.
2200 If no revision is given, the first parent of the working directory
2200 If no revision is given, the first parent of the working directory
2201 is used, or the null revision if no revision is checked out.
2201 is used, or the null revision if no revision is checked out.
2202
2202
2203 With -v, print file permissions, symlink and executable bits.
2203 With -v, print file permissions, symlink and executable bits.
2204 With --debug, print file revision hashes.
2204 With --debug, print file revision hashes.
2205 """
2205 """
2206
2206
2207 if rev and node:
2207 if rev and node:
2208 raise util.Abort(_("please specify just one revision"))
2208 raise util.Abort(_("please specify just one revision"))
2209
2209
2210 if not node:
2210 if not node:
2211 node = rev
2211 node = rev
2212
2212
2213 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2213 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2214 ctx = repo[node]
2214 ctx = repo[node]
2215 for f in ctx:
2215 for f in ctx:
2216 if ui.debugflag:
2216 if ui.debugflag:
2217 ui.write("%40s " % hex(ctx.manifest()[f]))
2217 ui.write("%40s " % hex(ctx.manifest()[f]))
2218 if ui.verbose:
2218 if ui.verbose:
2219 ui.write(decor[ctx.flags(f)])
2219 ui.write(decor[ctx.flags(f)])
2220 ui.write("%s\n" % f)
2220 ui.write("%s\n" % f)
2221
2221
2222 def merge(ui, repo, node=None, **opts):
2222 def merge(ui, repo, node=None, **opts):
2223 """merge working directory with another revision
2223 """merge working directory with another revision
2224
2224
2225 The current working directory is updated with all changes made in
2225 The current working directory is updated with all changes made in
2226 the requested revision since the last common predecessor revision.
2226 the requested revision since the last common predecessor revision.
2227
2227
2228 Files that changed between either parent are marked as changed for
2228 Files that changed between either parent are marked as changed for
2229 the next commit and a commit must be performed before any further
2229 the next commit and a commit must be performed before any further
2230 updates to the repository are allowed. The next commit will have
2230 updates to the repository are allowed. The next commit will have
2231 two parents.
2231 two parents.
2232
2232
2233 If no revision is specified, the working directory's parent is a
2233 If no revision is specified, the working directory's parent is a
2234 head revision, and the current branch contains exactly one other
2234 head revision, and the current branch contains exactly one other
2235 head, the other head is merged with by default. Otherwise, an
2235 head, the other head is merged with by default. Otherwise, an
2236 explicit revision with which to merge with must be provided.
2236 explicit revision with which to merge with must be provided.
2237 """
2237 """
2238
2238
2239 if opts.get('rev') and node:
2239 if opts.get('rev') and node:
2240 raise util.Abort(_("please specify just one revision"))
2240 raise util.Abort(_("please specify just one revision"))
2241 if not node:
2241 if not node:
2242 node = opts.get('rev')
2242 node = opts.get('rev')
2243
2243
2244 if not node:
2244 if not node:
2245 branch = repo.changectx(None).branch()
2245 branch = repo.changectx(None).branch()
2246 bheads = repo.branchheads(branch)
2246 bheads = repo.branchheads(branch)
2247 if len(bheads) > 2:
2247 if len(bheads) > 2:
2248 ui.warn(_("abort: branch '%s' has %d heads - "
2248 ui.warn(_("abort: branch '%s' has %d heads - "
2249 "please merge with an explicit rev\n")
2249 "please merge with an explicit rev\n")
2250 % (branch, len(bheads)))
2250 % (branch, len(bheads)))
2251 ui.status(_("(run 'hg heads .' to see heads)\n"))
2251 ui.status(_("(run 'hg heads .' to see heads)\n"))
2252 return False
2252 return False
2253
2253
2254 parent = repo.dirstate.parents()[0]
2254 parent = repo.dirstate.parents()[0]
2255 if len(bheads) == 1:
2255 if len(bheads) == 1:
2256 if len(repo.heads()) > 1:
2256 if len(repo.heads()) > 1:
2257 ui.warn(_("abort: branch '%s' has one head - "
2257 ui.warn(_("abort: branch '%s' has one head - "
2258 "please merge with an explicit rev\n" % branch))
2258 "please merge with an explicit rev\n" % branch))
2259 ui.status(_("(run 'hg heads' to see all heads)\n"))
2259 ui.status(_("(run 'hg heads' to see all heads)\n"))
2260 return False
2260 return False
2261 msg = _('there is nothing to merge')
2261 msg = _('there is nothing to merge')
2262 if parent != repo.lookup(repo[None].branch()):
2262 if parent != repo.lookup(repo[None].branch()):
2263 msg = _('%s - use "hg update" instead') % msg
2263 msg = _('%s - use "hg update" instead') % msg
2264 raise util.Abort(msg)
2264 raise util.Abort(msg)
2265
2265
2266 if parent not in bheads:
2266 if parent not in bheads:
2267 raise util.Abort(_('working dir not at a head rev - '
2267 raise util.Abort(_('working dir not at a head rev - '
2268 'use "hg update" or merge with an explicit rev'))
2268 'use "hg update" or merge with an explicit rev'))
2269 node = parent == bheads[0] and bheads[-1] or bheads[0]
2269 node = parent == bheads[0] and bheads[-1] or bheads[0]
2270
2270
2271 if opts.get('preview'):
2271 if opts.get('preview'):
2272 # find nodes that are ancestors of p2 but not of p1
2272 # find nodes that are ancestors of p2 but not of p1
2273 p1 = repo.lookup('.')
2273 p1 = repo.lookup('.')
2274 p2 = repo.lookup(node)
2274 p2 = repo.lookup(node)
2275 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2275 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2276
2276
2277 displayer = cmdutil.show_changeset(ui, repo, opts)
2277 displayer = cmdutil.show_changeset(ui, repo, opts)
2278 for node in nodes:
2278 for node in nodes:
2279 displayer.show(repo[node])
2279 displayer.show(repo[node])
2280 displayer.close()
2280 displayer.close()
2281 return 0
2281 return 0
2282
2282
2283 return hg.merge(repo, node, force=opts.get('force'))
2283 return hg.merge(repo, node, force=opts.get('force'))
2284
2284
2285 def outgoing(ui, repo, dest=None, **opts):
2285 def outgoing(ui, repo, dest=None, **opts):
2286 """show changesets not found in the destination
2286 """show changesets not found in the destination
2287
2287
2288 Show changesets not found in the specified destination repository
2288 Show changesets not found in the specified destination repository
2289 or the default push location. These are the changesets that would
2289 or the default push location. These are the changesets that would
2290 be pushed if a push was requested.
2290 be pushed if a push was requested.
2291
2291
2292 See pull for details of valid destination formats.
2292 See pull for details of valid destination formats.
2293 """
2293 """
2294 limit = cmdutil.loglimit(opts)
2294 limit = cmdutil.loglimit(opts)
2295 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2295 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2296 dest, branches = hg.parseurl(dest, opts.get('branch'))
2296 dest, branches = hg.parseurl(dest, opts.get('branch'))
2297 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2297 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2298 if revs:
2298 if revs:
2299 revs = [repo.lookup(rev) for rev in revs]
2299 revs = [repo.lookup(rev) for rev in revs]
2300
2300
2301 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2301 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2302 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2302 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2303 o = repo.findoutgoing(other, force=opts.get('force'))
2303 o = repo.findoutgoing(other, force=opts.get('force'))
2304 if not o:
2304 if not o:
2305 ui.status(_("no changes found\n"))
2305 ui.status(_("no changes found\n"))
2306 return 1
2306 return 1
2307 o = repo.changelog.nodesbetween(o, revs)[0]
2307 o = repo.changelog.nodesbetween(o, revs)[0]
2308 if opts.get('newest_first'):
2308 if opts.get('newest_first'):
2309 o.reverse()
2309 o.reverse()
2310 displayer = cmdutil.show_changeset(ui, repo, opts)
2310 displayer = cmdutil.show_changeset(ui, repo, opts)
2311 count = 0
2311 count = 0
2312 for n in o:
2312 for n in o:
2313 if limit is not None and count >= limit:
2313 if limit is not None and count >= limit:
2314 break
2314 break
2315 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2315 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2316 if opts.get('no_merges') and len(parents) == 2:
2316 if opts.get('no_merges') and len(parents) == 2:
2317 continue
2317 continue
2318 count += 1
2318 count += 1
2319 displayer.show(repo[n])
2319 displayer.show(repo[n])
2320 displayer.close()
2320 displayer.close()
2321
2321
2322 def parents(ui, repo, file_=None, **opts):
2322 def parents(ui, repo, file_=None, **opts):
2323 """show the parents of the working directory or revision
2323 """show the parents of the working directory or revision
2324
2324
2325 Print the working directory's parent revisions. If a revision is
2325 Print the working directory's parent revisions. If a revision is
2326 given via -r/--rev, the parent of that revision will be printed.
2326 given via -r/--rev, the parent of that revision will be printed.
2327 If a file argument is given, the revision in which the file was
2327 If a file argument is given, the revision in which the file was
2328 last changed (before the working directory revision or the
2328 last changed (before the working directory revision or the
2329 argument to --rev if given) is printed.
2329 argument to --rev if given) is printed.
2330 """
2330 """
2331 rev = opts.get('rev')
2331 rev = opts.get('rev')
2332 if rev:
2332 if rev:
2333 ctx = repo[rev]
2333 ctx = repo[rev]
2334 else:
2334 else:
2335 ctx = repo[None]
2335 ctx = repo[None]
2336
2336
2337 if file_:
2337 if file_:
2338 m = cmdutil.match(repo, (file_,), opts)
2338 m = cmdutil.match(repo, (file_,), opts)
2339 if m.anypats() or len(m.files()) != 1:
2339 if m.anypats() or len(m.files()) != 1:
2340 raise util.Abort(_('can only specify an explicit filename'))
2340 raise util.Abort(_('can only specify an explicit filename'))
2341 file_ = m.files()[0]
2341 file_ = m.files()[0]
2342 filenodes = []
2342 filenodes = []
2343 for cp in ctx.parents():
2343 for cp in ctx.parents():
2344 if not cp:
2344 if not cp:
2345 continue
2345 continue
2346 try:
2346 try:
2347 filenodes.append(cp.filenode(file_))
2347 filenodes.append(cp.filenode(file_))
2348 except error.LookupError:
2348 except error.LookupError:
2349 pass
2349 pass
2350 if not filenodes:
2350 if not filenodes:
2351 raise util.Abort(_("'%s' not found in manifest!") % file_)
2351 raise util.Abort(_("'%s' not found in manifest!") % file_)
2352 fl = repo.file(file_)
2352 fl = repo.file(file_)
2353 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2353 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2354 else:
2354 else:
2355 p = [cp.node() for cp in ctx.parents()]
2355 p = [cp.node() for cp in ctx.parents()]
2356
2356
2357 displayer = cmdutil.show_changeset(ui, repo, opts)
2357 displayer = cmdutil.show_changeset(ui, repo, opts)
2358 for n in p:
2358 for n in p:
2359 if n != nullid:
2359 if n != nullid:
2360 displayer.show(repo[n])
2360 displayer.show(repo[n])
2361 displayer.close()
2361 displayer.close()
2362
2362
2363 def paths(ui, repo, search=None):
2363 def paths(ui, repo, search=None):
2364 """show aliases for remote repositories
2364 """show aliases for remote repositories
2365
2365
2366 Show definition of symbolic path name NAME. If no name is given,
2366 Show definition of symbolic path name NAME. If no name is given,
2367 show definition of all available names.
2367 show definition of all available names.
2368
2368
2369 Path names are defined in the [paths] section of
2369 Path names are defined in the [paths] section of
2370 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2370 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2371 repository, ``.hg/hgrc`` is used, too.
2371 repository, ``.hg/hgrc`` is used, too.
2372
2372
2373 The path names ``default`` and ``default-push`` have a special
2373 The path names ``default`` and ``default-push`` have a special
2374 meaning. When performing a push or pull operation, they are used
2374 meaning. When performing a push or pull operation, they are used
2375 as fallbacks if no location is specified on the command-line.
2375 as fallbacks if no location is specified on the command-line.
2376 When ``default-push`` is set, it will be used for push and
2376 When ``default-push`` is set, it will be used for push and
2377 ``default`` will be used for pull; otherwise ``default`` is used
2377 ``default`` will be used for pull; otherwise ``default`` is used
2378 as the fallback for both. When cloning a repository, the clone
2378 as the fallback for both. When cloning a repository, the clone
2379 source is written as ``default`` in ``.hg/hgrc``. Note that
2379 source is written as ``default`` in ``.hg/hgrc``. Note that
2380 ``default`` and ``default-push`` apply to all inbound (e.g.
2380 ``default`` and ``default-push`` apply to all inbound (e.g.
2381 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2381 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2382 :hg:`bundle`) operations.
2382 :hg:`bundle`) operations.
2383
2383
2384 See :hg:`help urls` for more information.
2384 See :hg:`help urls` for more information.
2385 """
2385 """
2386 if search:
2386 if search:
2387 for name, path in ui.configitems("paths"):
2387 for name, path in ui.configitems("paths"):
2388 if name == search:
2388 if name == search:
2389 ui.write("%s\n" % url.hidepassword(path))
2389 ui.write("%s\n" % url.hidepassword(path))
2390 return
2390 return
2391 ui.warn(_("not found!\n"))
2391 ui.warn(_("not found!\n"))
2392 return 1
2392 return 1
2393 else:
2393 else:
2394 for name, path in ui.configitems("paths"):
2394 for name, path in ui.configitems("paths"):
2395 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2395 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2396
2396
2397 def postincoming(ui, repo, modheads, optupdate, checkout):
2397 def postincoming(ui, repo, modheads, optupdate, checkout):
2398 if modheads == 0:
2398 if modheads == 0:
2399 return
2399 return
2400 if optupdate:
2400 if optupdate:
2401 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2401 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2402 return hg.update(repo, checkout)
2402 return hg.update(repo, checkout)
2403 else:
2403 else:
2404 ui.status(_("not updating, since new heads added\n"))
2404 ui.status(_("not updating, since new heads added\n"))
2405 if modheads > 1:
2405 if modheads > 1:
2406 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2406 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2407 else:
2407 else:
2408 ui.status(_("(run 'hg update' to get a working copy)\n"))
2408 ui.status(_("(run 'hg update' to get a working copy)\n"))
2409
2409
2410 def pull(ui, repo, source="default", **opts):
2410 def pull(ui, repo, source="default", **opts):
2411 """pull changes from the specified source
2411 """pull changes from the specified source
2412
2412
2413 Pull changes from a remote repository to a local one.
2413 Pull changes from a remote repository to a local one.
2414
2414
2415 This finds all changes from the repository at the specified path
2415 This finds all changes from the repository at the specified path
2416 or URL and adds them to a local repository (the current one unless
2416 or URL and adds them to a local repository (the current one unless
2417 -R is specified). By default, this does not update the copy of the
2417 -R is specified). By default, this does not update the copy of the
2418 project in the working directory.
2418 project in the working directory.
2419
2419
2420 Use hg incoming if you want to see what would have been added by a
2420 Use hg incoming if you want to see what would have been added by a
2421 pull at the time you issued this command. If you then decide to
2421 pull at the time you issued this command. If you then decide to
2422 added those changes to the repository, you should use pull -r X
2422 added those changes to the repository, you should use pull -r X
2423 where X is the last changeset listed by hg incoming.
2423 where X is the last changeset listed by hg incoming.
2424
2424
2425 If SOURCE is omitted, the 'default' path will be used.
2425 If SOURCE is omitted, the 'default' path will be used.
2426 See :hg:`help urls` for more information.
2426 See :hg:`help urls` for more information.
2427 """
2427 """
2428 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2428 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2429 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2429 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2430 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2430 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2431 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2431 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2432 if revs:
2432 if revs:
2433 try:
2433 try:
2434 revs = [other.lookup(rev) for rev in revs]
2434 revs = [other.lookup(rev) for rev in revs]
2435 except error.CapabilityError:
2435 except error.CapabilityError:
2436 err = _("Other repository doesn't support revision lookup, "
2436 err = _("Other repository doesn't support revision lookup, "
2437 "so a rev cannot be specified.")
2437 "so a rev cannot be specified.")
2438 raise util.Abort(err)
2438 raise util.Abort(err)
2439
2439
2440 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2440 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2441 if checkout:
2441 if checkout:
2442 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2442 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2443 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2443 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2444
2444
2445 def push(ui, repo, dest=None, **opts):
2445 def push(ui, repo, dest=None, **opts):
2446 """push changes to the specified destination
2446 """push changes to the specified destination
2447
2447
2448 Push changes from the local repository to the specified destination.
2448 Push changes from the local repository to the specified destination.
2449
2449
2450 This is the symmetrical operation for pull. It moves changes from
2450 This is the symmetrical operation for pull. It moves changes from
2451 the current repository to a different one. If the destination is
2451 the current repository to a different one. If the destination is
2452 local this is identical to a pull in that directory from the
2452 local this is identical to a pull in that directory from the
2453 current one.
2453 current one.
2454
2454
2455 By default, push will refuse to run if it detects the result would
2455 By default, push will refuse to run if it detects the result would
2456 increase the number of remote heads. This generally indicates the
2456 increase the number of remote heads. This generally indicates the
2457 user forgot to pull and merge before pushing.
2457 user forgot to pull and merge before pushing.
2458
2458
2459 If -r/--rev is used, the named revision and all its ancestors will
2459 If -r/--rev is used, the named revision and all its ancestors will
2460 be pushed to the remote repository.
2460 be pushed to the remote repository.
2461
2461
2462 Please see :hg:`help urls` for important details about ``ssh://``
2462 Please see :hg:`help urls` for important details about ``ssh://``
2463 URLs. If DESTINATION is omitted, a default path will be used.
2463 URLs. If DESTINATION is omitted, a default path will be used.
2464 """
2464 """
2465 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2465 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2466 dest, branches = hg.parseurl(dest, opts.get('branch'))
2466 dest, branches = hg.parseurl(dest, opts.get('branch'))
2467 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2467 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2468 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2468 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2469 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2469 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2470 if revs:
2470 if revs:
2471 revs = [repo.lookup(rev) for rev in revs]
2471 revs = [repo.lookup(rev) for rev in revs]
2472
2472
2473 # push subrepos depth-first for coherent ordering
2473 # push subrepos depth-first for coherent ordering
2474 c = repo['']
2474 c = repo['']
2475 subs = c.substate # only repos that are committed
2475 subs = c.substate # only repos that are committed
2476 for s in sorted(subs):
2476 for s in sorted(subs):
2477 c.sub(s).push(opts.get('force'))
2477 if not c.sub(s).push(opts.get('force')):
2478 return False
2478
2479
2479 r = repo.push(other, opts.get('force'), revs=revs)
2480 r = repo.push(other, opts.get('force'), revs=revs)
2480 return r == 0
2481 return r == 0
2481
2482
2482 def recover(ui, repo):
2483 def recover(ui, repo):
2483 """roll back an interrupted transaction
2484 """roll back an interrupted transaction
2484
2485
2485 Recover from an interrupted commit or pull.
2486 Recover from an interrupted commit or pull.
2486
2487
2487 This command tries to fix the repository status after an
2488 This command tries to fix the repository status after an
2488 interrupted operation. It should only be necessary when Mercurial
2489 interrupted operation. It should only be necessary when Mercurial
2489 suggests it.
2490 suggests it.
2490 """
2491 """
2491 if repo.recover():
2492 if repo.recover():
2492 return hg.verify(repo)
2493 return hg.verify(repo)
2493 return 1
2494 return 1
2494
2495
2495 def remove(ui, repo, *pats, **opts):
2496 def remove(ui, repo, *pats, **opts):
2496 """remove the specified files on the next commit
2497 """remove the specified files on the next commit
2497
2498
2498 Schedule the indicated files for removal from the repository.
2499 Schedule the indicated files for removal from the repository.
2499
2500
2500 This only removes files from the current branch, not from the
2501 This only removes files from the current branch, not from the
2501 entire project history. -A/--after can be used to remove only
2502 entire project history. -A/--after can be used to remove only
2502 files that have already been deleted, -f/--force can be used to
2503 files that have already been deleted, -f/--force can be used to
2503 force deletion, and -Af can be used to remove files from the next
2504 force deletion, and -Af can be used to remove files from the next
2504 revision without deleting them from the working directory.
2505 revision without deleting them from the working directory.
2505
2506
2506 The following table details the behavior of remove for different
2507 The following table details the behavior of remove for different
2507 file states (columns) and option combinations (rows). The file
2508 file states (columns) and option combinations (rows). The file
2508 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2509 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2509 reported by hg status). The actions are Warn, Remove (from branch)
2510 reported by hg status). The actions are Warn, Remove (from branch)
2510 and Delete (from disk)::
2511 and Delete (from disk)::
2511
2512
2512 A C M !
2513 A C M !
2513 none W RD W R
2514 none W RD W R
2514 -f R RD RD R
2515 -f R RD RD R
2515 -A W W W R
2516 -A W W W R
2516 -Af R R R R
2517 -Af R R R R
2517
2518
2518 This command schedules the files to be removed at the next commit.
2519 This command schedules the files to be removed at the next commit.
2519 To undo a remove before that, see hg revert.
2520 To undo a remove before that, see hg revert.
2520 """
2521 """
2521
2522
2522 after, force = opts.get('after'), opts.get('force')
2523 after, force = opts.get('after'), opts.get('force')
2523 if not pats and not after:
2524 if not pats and not after:
2524 raise util.Abort(_('no files specified'))
2525 raise util.Abort(_('no files specified'))
2525
2526
2526 m = cmdutil.match(repo, pats, opts)
2527 m = cmdutil.match(repo, pats, opts)
2527 s = repo.status(match=m, clean=True)
2528 s = repo.status(match=m, clean=True)
2528 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2529 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2529
2530
2530 for f in m.files():
2531 for f in m.files():
2531 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2532 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2532 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2533 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2533
2534
2534 def warn(files, reason):
2535 def warn(files, reason):
2535 for f in files:
2536 for f in files:
2536 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2537 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2537 % (m.rel(f), reason))
2538 % (m.rel(f), reason))
2538
2539
2539 if force:
2540 if force:
2540 remove, forget = modified + deleted + clean, added
2541 remove, forget = modified + deleted + clean, added
2541 elif after:
2542 elif after:
2542 remove, forget = deleted, []
2543 remove, forget = deleted, []
2543 warn(modified + added + clean, _('still exists'))
2544 warn(modified + added + clean, _('still exists'))
2544 else:
2545 else:
2545 remove, forget = deleted + clean, []
2546 remove, forget = deleted + clean, []
2546 warn(modified, _('is modified'))
2547 warn(modified, _('is modified'))
2547 warn(added, _('has been marked for add'))
2548 warn(added, _('has been marked for add'))
2548
2549
2549 for f in sorted(remove + forget):
2550 for f in sorted(remove + forget):
2550 if ui.verbose or not m.exact(f):
2551 if ui.verbose or not m.exact(f):
2551 ui.status(_('removing %s\n') % m.rel(f))
2552 ui.status(_('removing %s\n') % m.rel(f))
2552
2553
2553 repo.forget(forget)
2554 repo.forget(forget)
2554 repo.remove(remove, unlink=not after)
2555 repo.remove(remove, unlink=not after)
2555
2556
2556 def rename(ui, repo, *pats, **opts):
2557 def rename(ui, repo, *pats, **opts):
2557 """rename files; equivalent of copy + remove
2558 """rename files; equivalent of copy + remove
2558
2559
2559 Mark dest as copies of sources; mark sources for deletion. If dest
2560 Mark dest as copies of sources; mark sources for deletion. If dest
2560 is a directory, copies are put in that directory. If dest is a
2561 is a directory, copies are put in that directory. If dest is a
2561 file, there can only be one source.
2562 file, there can only be one source.
2562
2563
2563 By default, this command copies the contents of files as they
2564 By default, this command copies the contents of files as they
2564 exist in the working directory. If invoked with -A/--after, the
2565 exist in the working directory. If invoked with -A/--after, the
2565 operation is recorded, but no copying is performed.
2566 operation is recorded, but no copying is performed.
2566
2567
2567 This command takes effect at the next commit. To undo a rename
2568 This command takes effect at the next commit. To undo a rename
2568 before that, see hg revert.
2569 before that, see hg revert.
2569 """
2570 """
2570 wlock = repo.wlock(False)
2571 wlock = repo.wlock(False)
2571 try:
2572 try:
2572 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2573 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2573 finally:
2574 finally:
2574 wlock.release()
2575 wlock.release()
2575
2576
2576 def resolve(ui, repo, *pats, **opts):
2577 def resolve(ui, repo, *pats, **opts):
2577 """various operations to help finish a merge
2578 """various operations to help finish a merge
2578
2579
2579 This command includes several actions that are often useful while
2580 This command includes several actions that are often useful while
2580 performing a merge, after running ``merge`` but before running
2581 performing a merge, after running ``merge`` but before running
2581 ``commit``. (It is only meaningful if your working directory has
2582 ``commit``. (It is only meaningful if your working directory has
2582 two parents.) It is most relevant for merges with unresolved
2583 two parents.) It is most relevant for merges with unresolved
2583 conflicts, which are typically a result of non-interactive merging with
2584 conflicts, which are typically a result of non-interactive merging with
2584 ``internal:merge`` or a command-line merge tool like ``diff3``.
2585 ``internal:merge`` or a command-line merge tool like ``diff3``.
2585
2586
2586 The available actions are:
2587 The available actions are:
2587
2588
2588 1) list files that were merged with conflicts (U, for unresolved)
2589 1) list files that were merged with conflicts (U, for unresolved)
2589 and without conflicts (R, for resolved): ``hg resolve -l``
2590 and without conflicts (R, for resolved): ``hg resolve -l``
2590 (this is like ``status`` for merges)
2591 (this is like ``status`` for merges)
2591 2) record that you have resolved conflicts in certain files:
2592 2) record that you have resolved conflicts in certain files:
2592 ``hg resolve -m [file ...]`` (default: mark all unresolved files)
2593 ``hg resolve -m [file ...]`` (default: mark all unresolved files)
2593 3) forget that you have resolved conflicts in certain files:
2594 3) forget that you have resolved conflicts in certain files:
2594 ``hg resolve -u [file ...]`` (default: unmark all resolved files)
2595 ``hg resolve -u [file ...]`` (default: unmark all resolved files)
2595 4) discard your current attempt(s) at resolving conflicts and
2596 4) discard your current attempt(s) at resolving conflicts and
2596 restart the merge from scratch: ``hg resolve file...``
2597 restart the merge from scratch: ``hg resolve file...``
2597 (or ``-a`` for all unresolved files)
2598 (or ``-a`` for all unresolved files)
2598
2599
2599 Note that Mercurial will not let you commit files with unresolved merge
2600 Note that Mercurial will not let you commit files with unresolved merge
2600 conflicts. You must use ``hg resolve -m ...`` before you can commit
2601 conflicts. You must use ``hg resolve -m ...`` before you can commit
2601 after a conflicting merge.
2602 after a conflicting merge.
2602 """
2603 """
2603
2604
2604 all, mark, unmark, show, nostatus = \
2605 all, mark, unmark, show, nostatus = \
2605 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2606 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2606
2607
2607 if (show and (mark or unmark)) or (mark and unmark):
2608 if (show and (mark or unmark)) or (mark and unmark):
2608 raise util.Abort(_("too many options specified"))
2609 raise util.Abort(_("too many options specified"))
2609 if pats and all:
2610 if pats and all:
2610 raise util.Abort(_("can't specify --all and patterns"))
2611 raise util.Abort(_("can't specify --all and patterns"))
2611 if not (all or pats or show or mark or unmark):
2612 if not (all or pats or show or mark or unmark):
2612 raise util.Abort(_('no files or directories specified; '
2613 raise util.Abort(_('no files or directories specified; '
2613 'use --all to remerge all files'))
2614 'use --all to remerge all files'))
2614
2615
2615 ms = mergemod.mergestate(repo)
2616 ms = mergemod.mergestate(repo)
2616 m = cmdutil.match(repo, pats, opts)
2617 m = cmdutil.match(repo, pats, opts)
2617
2618
2618 for f in ms:
2619 for f in ms:
2619 if m(f):
2620 if m(f):
2620 if show:
2621 if show:
2621 if nostatus:
2622 if nostatus:
2622 ui.write("%s\n" % f)
2623 ui.write("%s\n" % f)
2623 else:
2624 else:
2624 ui.write("%s %s\n" % (ms[f].upper(), f),
2625 ui.write("%s %s\n" % (ms[f].upper(), f),
2625 label='resolve.' +
2626 label='resolve.' +
2626 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
2627 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
2627 elif mark:
2628 elif mark:
2628 ms.mark(f, "r")
2629 ms.mark(f, "r")
2629 elif unmark:
2630 elif unmark:
2630 ms.mark(f, "u")
2631 ms.mark(f, "u")
2631 else:
2632 else:
2632 wctx = repo[None]
2633 wctx = repo[None]
2633 mctx = wctx.parents()[-1]
2634 mctx = wctx.parents()[-1]
2634
2635
2635 # backup pre-resolve (merge uses .orig for its own purposes)
2636 # backup pre-resolve (merge uses .orig for its own purposes)
2636 a = repo.wjoin(f)
2637 a = repo.wjoin(f)
2637 util.copyfile(a, a + ".resolve")
2638 util.copyfile(a, a + ".resolve")
2638
2639
2639 # resolve file
2640 # resolve file
2640 ms.resolve(f, wctx, mctx)
2641 ms.resolve(f, wctx, mctx)
2641
2642
2642 # replace filemerge's .orig file with our resolve file
2643 # replace filemerge's .orig file with our resolve file
2643 util.rename(a + ".resolve", a + ".orig")
2644 util.rename(a + ".resolve", a + ".orig")
2644
2645
2645 def revert(ui, repo, *pats, **opts):
2646 def revert(ui, repo, *pats, **opts):
2646 """restore individual files or directories to an earlier state
2647 """restore individual files or directories to an earlier state
2647
2648
2648 (Use update -r to check out earlier revisions, revert does not
2649 (Use update -r to check out earlier revisions, revert does not
2649 change the working directory parents.)
2650 change the working directory parents.)
2650
2651
2651 With no revision specified, revert the named files or directories
2652 With no revision specified, revert the named files or directories
2652 to the contents they had in the parent of the working directory.
2653 to the contents they had in the parent of the working directory.
2653 This restores the contents of the affected files to an unmodified
2654 This restores the contents of the affected files to an unmodified
2654 state and unschedules adds, removes, copies, and renames. If the
2655 state and unschedules adds, removes, copies, and renames. If the
2655 working directory has two parents, you must explicitly specify a
2656 working directory has two parents, you must explicitly specify a
2656 revision.
2657 revision.
2657
2658
2658 Using the -r/--rev option, revert the given files or directories
2659 Using the -r/--rev option, revert the given files or directories
2659 to their contents as of a specific revision. This can be helpful
2660 to their contents as of a specific revision. This can be helpful
2660 to "roll back" some or all of an earlier change. See :hg:`help
2661 to "roll back" some or all of an earlier change. See :hg:`help
2661 dates` for a list of formats valid for -d/--date.
2662 dates` for a list of formats valid for -d/--date.
2662
2663
2663 Revert modifies the working directory. It does not commit any
2664 Revert modifies the working directory. It does not commit any
2664 changes, or change the parent of the working directory. If you
2665 changes, or change the parent of the working directory. If you
2665 revert to a revision other than the parent of the working
2666 revert to a revision other than the parent of the working
2666 directory, the reverted files will thus appear modified
2667 directory, the reverted files will thus appear modified
2667 afterwards.
2668 afterwards.
2668
2669
2669 If a file has been deleted, it is restored. If the executable mode
2670 If a file has been deleted, it is restored. If the executable mode
2670 of a file was changed, it is reset.
2671 of a file was changed, it is reset.
2671
2672
2672 If names are given, all files matching the names are reverted.
2673 If names are given, all files matching the names are reverted.
2673 If no arguments are given, no files are reverted.
2674 If no arguments are given, no files are reverted.
2674
2675
2675 Modified files are saved with a .orig suffix before reverting.
2676 Modified files are saved with a .orig suffix before reverting.
2676 To disable these backups, use --no-backup.
2677 To disable these backups, use --no-backup.
2677 """
2678 """
2678
2679
2679 if opts["date"]:
2680 if opts["date"]:
2680 if opts["rev"]:
2681 if opts["rev"]:
2681 raise util.Abort(_("you can't specify a revision and a date"))
2682 raise util.Abort(_("you can't specify a revision and a date"))
2682 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2683 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2683
2684
2684 if not pats and not opts.get('all'):
2685 if not pats and not opts.get('all'):
2685 raise util.Abort(_('no files or directories specified; '
2686 raise util.Abort(_('no files or directories specified; '
2686 'use --all to revert the whole repo'))
2687 'use --all to revert the whole repo'))
2687
2688
2688 parent, p2 = repo.dirstate.parents()
2689 parent, p2 = repo.dirstate.parents()
2689 if not opts.get('rev') and p2 != nullid:
2690 if not opts.get('rev') and p2 != nullid:
2690 raise util.Abort(_('uncommitted merge - please provide a '
2691 raise util.Abort(_('uncommitted merge - please provide a '
2691 'specific revision'))
2692 'specific revision'))
2692 ctx = repo[opts.get('rev')]
2693 ctx = repo[opts.get('rev')]
2693 node = ctx.node()
2694 node = ctx.node()
2694 mf = ctx.manifest()
2695 mf = ctx.manifest()
2695 if node == parent:
2696 if node == parent:
2696 pmf = mf
2697 pmf = mf
2697 else:
2698 else:
2698 pmf = None
2699 pmf = None
2699
2700
2700 # need all matching names in dirstate and manifest of target rev,
2701 # need all matching names in dirstate and manifest of target rev,
2701 # so have to walk both. do not print errors if files exist in one
2702 # so have to walk both. do not print errors if files exist in one
2702 # but not other.
2703 # but not other.
2703
2704
2704 names = {}
2705 names = {}
2705
2706
2706 wlock = repo.wlock()
2707 wlock = repo.wlock()
2707 try:
2708 try:
2708 # walk dirstate.
2709 # walk dirstate.
2709
2710
2710 m = cmdutil.match(repo, pats, opts)
2711 m = cmdutil.match(repo, pats, opts)
2711 m.bad = lambda x, y: False
2712 m.bad = lambda x, y: False
2712 for abs in repo.walk(m):
2713 for abs in repo.walk(m):
2713 names[abs] = m.rel(abs), m.exact(abs)
2714 names[abs] = m.rel(abs), m.exact(abs)
2714
2715
2715 # walk target manifest.
2716 # walk target manifest.
2716
2717
2717 def badfn(path, msg):
2718 def badfn(path, msg):
2718 if path in names:
2719 if path in names:
2719 return
2720 return
2720 path_ = path + '/'
2721 path_ = path + '/'
2721 for f in names:
2722 for f in names:
2722 if f.startswith(path_):
2723 if f.startswith(path_):
2723 return
2724 return
2724 ui.warn("%s: %s\n" % (m.rel(path), msg))
2725 ui.warn("%s: %s\n" % (m.rel(path), msg))
2725
2726
2726 m = cmdutil.match(repo, pats, opts)
2727 m = cmdutil.match(repo, pats, opts)
2727 m.bad = badfn
2728 m.bad = badfn
2728 for abs in repo[node].walk(m):
2729 for abs in repo[node].walk(m):
2729 if abs not in names:
2730 if abs not in names:
2730 names[abs] = m.rel(abs), m.exact(abs)
2731 names[abs] = m.rel(abs), m.exact(abs)
2731
2732
2732 m = cmdutil.matchfiles(repo, names)
2733 m = cmdutil.matchfiles(repo, names)
2733 changes = repo.status(match=m)[:4]
2734 changes = repo.status(match=m)[:4]
2734 modified, added, removed, deleted = map(set, changes)
2735 modified, added, removed, deleted = map(set, changes)
2735
2736
2736 # if f is a rename, also revert the source
2737 # if f is a rename, also revert the source
2737 cwd = repo.getcwd()
2738 cwd = repo.getcwd()
2738 for f in added:
2739 for f in added:
2739 src = repo.dirstate.copied(f)
2740 src = repo.dirstate.copied(f)
2740 if src and src not in names and repo.dirstate[src] == 'r':
2741 if src and src not in names and repo.dirstate[src] == 'r':
2741 removed.add(src)
2742 removed.add(src)
2742 names[src] = (repo.pathto(src, cwd), True)
2743 names[src] = (repo.pathto(src, cwd), True)
2743
2744
2744 def removeforget(abs):
2745 def removeforget(abs):
2745 if repo.dirstate[abs] == 'a':
2746 if repo.dirstate[abs] == 'a':
2746 return _('forgetting %s\n')
2747 return _('forgetting %s\n')
2747 return _('removing %s\n')
2748 return _('removing %s\n')
2748
2749
2749 revert = ([], _('reverting %s\n'))
2750 revert = ([], _('reverting %s\n'))
2750 add = ([], _('adding %s\n'))
2751 add = ([], _('adding %s\n'))
2751 remove = ([], removeforget)
2752 remove = ([], removeforget)
2752 undelete = ([], _('undeleting %s\n'))
2753 undelete = ([], _('undeleting %s\n'))
2753
2754
2754 disptable = (
2755 disptable = (
2755 # dispatch table:
2756 # dispatch table:
2756 # file state
2757 # file state
2757 # action if in target manifest
2758 # action if in target manifest
2758 # action if not in target manifest
2759 # action if not in target manifest
2759 # make backup if in target manifest
2760 # make backup if in target manifest
2760 # make backup if not in target manifest
2761 # make backup if not in target manifest
2761 (modified, revert, remove, True, True),
2762 (modified, revert, remove, True, True),
2762 (added, revert, remove, True, False),
2763 (added, revert, remove, True, False),
2763 (removed, undelete, None, False, False),
2764 (removed, undelete, None, False, False),
2764 (deleted, revert, remove, False, False),
2765 (deleted, revert, remove, False, False),
2765 )
2766 )
2766
2767
2767 for abs, (rel, exact) in sorted(names.items()):
2768 for abs, (rel, exact) in sorted(names.items()):
2768 mfentry = mf.get(abs)
2769 mfentry = mf.get(abs)
2769 target = repo.wjoin(abs)
2770 target = repo.wjoin(abs)
2770 def handle(xlist, dobackup):
2771 def handle(xlist, dobackup):
2771 xlist[0].append(abs)
2772 xlist[0].append(abs)
2772 if dobackup and not opts.get('no_backup') and util.lexists(target):
2773 if dobackup and not opts.get('no_backup') and util.lexists(target):
2773 bakname = "%s.orig" % rel
2774 bakname = "%s.orig" % rel
2774 ui.note(_('saving current version of %s as %s\n') %
2775 ui.note(_('saving current version of %s as %s\n') %
2775 (rel, bakname))
2776 (rel, bakname))
2776 if not opts.get('dry_run'):
2777 if not opts.get('dry_run'):
2777 util.copyfile(target, bakname)
2778 util.copyfile(target, bakname)
2778 if ui.verbose or not exact:
2779 if ui.verbose or not exact:
2779 msg = xlist[1]
2780 msg = xlist[1]
2780 if not isinstance(msg, basestring):
2781 if not isinstance(msg, basestring):
2781 msg = msg(abs)
2782 msg = msg(abs)
2782 ui.status(msg % rel)
2783 ui.status(msg % rel)
2783 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2784 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2784 if abs not in table:
2785 if abs not in table:
2785 continue
2786 continue
2786 # file has changed in dirstate
2787 # file has changed in dirstate
2787 if mfentry:
2788 if mfentry:
2788 handle(hitlist, backuphit)
2789 handle(hitlist, backuphit)
2789 elif misslist is not None:
2790 elif misslist is not None:
2790 handle(misslist, backupmiss)
2791 handle(misslist, backupmiss)
2791 break
2792 break
2792 else:
2793 else:
2793 if abs not in repo.dirstate:
2794 if abs not in repo.dirstate:
2794 if mfentry:
2795 if mfentry:
2795 handle(add, True)
2796 handle(add, True)
2796 elif exact:
2797 elif exact:
2797 ui.warn(_('file not managed: %s\n') % rel)
2798 ui.warn(_('file not managed: %s\n') % rel)
2798 continue
2799 continue
2799 # file has not changed in dirstate
2800 # file has not changed in dirstate
2800 if node == parent:
2801 if node == parent:
2801 if exact:
2802 if exact:
2802 ui.warn(_('no changes needed to %s\n') % rel)
2803 ui.warn(_('no changes needed to %s\n') % rel)
2803 continue
2804 continue
2804 if pmf is None:
2805 if pmf is None:
2805 # only need parent manifest in this unlikely case,
2806 # only need parent manifest in this unlikely case,
2806 # so do not read by default
2807 # so do not read by default
2807 pmf = repo[parent].manifest()
2808 pmf = repo[parent].manifest()
2808 if abs in pmf:
2809 if abs in pmf:
2809 if mfentry:
2810 if mfentry:
2810 # if version of file is same in parent and target
2811 # if version of file is same in parent and target
2811 # manifests, do nothing
2812 # manifests, do nothing
2812 if (pmf[abs] != mfentry or
2813 if (pmf[abs] != mfentry or
2813 pmf.flags(abs) != mf.flags(abs)):
2814 pmf.flags(abs) != mf.flags(abs)):
2814 handle(revert, False)
2815 handle(revert, False)
2815 else:
2816 else:
2816 handle(remove, False)
2817 handle(remove, False)
2817
2818
2818 if not opts.get('dry_run'):
2819 if not opts.get('dry_run'):
2819 def checkout(f):
2820 def checkout(f):
2820 fc = ctx[f]
2821 fc = ctx[f]
2821 repo.wwrite(f, fc.data(), fc.flags())
2822 repo.wwrite(f, fc.data(), fc.flags())
2822
2823
2823 audit_path = util.path_auditor(repo.root)
2824 audit_path = util.path_auditor(repo.root)
2824 for f in remove[0]:
2825 for f in remove[0]:
2825 if repo.dirstate[f] == 'a':
2826 if repo.dirstate[f] == 'a':
2826 repo.dirstate.forget(f)
2827 repo.dirstate.forget(f)
2827 continue
2828 continue
2828 audit_path(f)
2829 audit_path(f)
2829 try:
2830 try:
2830 util.unlink(repo.wjoin(f))
2831 util.unlink(repo.wjoin(f))
2831 except OSError:
2832 except OSError:
2832 pass
2833 pass
2833 repo.dirstate.remove(f)
2834 repo.dirstate.remove(f)
2834
2835
2835 normal = None
2836 normal = None
2836 if node == parent:
2837 if node == parent:
2837 # We're reverting to our parent. If possible, we'd like status
2838 # We're reverting to our parent. If possible, we'd like status
2838 # to report the file as clean. We have to use normallookup for
2839 # to report the file as clean. We have to use normallookup for
2839 # merges to avoid losing information about merged/dirty files.
2840 # merges to avoid losing information about merged/dirty files.
2840 if p2 != nullid:
2841 if p2 != nullid:
2841 normal = repo.dirstate.normallookup
2842 normal = repo.dirstate.normallookup
2842 else:
2843 else:
2843 normal = repo.dirstate.normal
2844 normal = repo.dirstate.normal
2844 for f in revert[0]:
2845 for f in revert[0]:
2845 checkout(f)
2846 checkout(f)
2846 if normal:
2847 if normal:
2847 normal(f)
2848 normal(f)
2848
2849
2849 for f in add[0]:
2850 for f in add[0]:
2850 checkout(f)
2851 checkout(f)
2851 repo.dirstate.add(f)
2852 repo.dirstate.add(f)
2852
2853
2853 normal = repo.dirstate.normallookup
2854 normal = repo.dirstate.normallookup
2854 if node == parent and p2 == nullid:
2855 if node == parent and p2 == nullid:
2855 normal = repo.dirstate.normal
2856 normal = repo.dirstate.normal
2856 for f in undelete[0]:
2857 for f in undelete[0]:
2857 checkout(f)
2858 checkout(f)
2858 normal(f)
2859 normal(f)
2859
2860
2860 finally:
2861 finally:
2861 wlock.release()
2862 wlock.release()
2862
2863
2863 def rollback(ui, repo, **opts):
2864 def rollback(ui, repo, **opts):
2864 """roll back the last transaction (dangerous)
2865 """roll back the last transaction (dangerous)
2865
2866
2866 This command should be used with care. There is only one level of
2867 This command should be used with care. There is only one level of
2867 rollback, and there is no way to undo a rollback. It will also
2868 rollback, and there is no way to undo a rollback. It will also
2868 restore the dirstate at the time of the last transaction, losing
2869 restore the dirstate at the time of the last transaction, losing
2869 any dirstate changes since that time. This command does not alter
2870 any dirstate changes since that time. This command does not alter
2870 the working directory.
2871 the working directory.
2871
2872
2872 Transactions are used to encapsulate the effects of all commands
2873 Transactions are used to encapsulate the effects of all commands
2873 that create new changesets or propagate existing changesets into a
2874 that create new changesets or propagate existing changesets into a
2874 repository. For example, the following commands are transactional,
2875 repository. For example, the following commands are transactional,
2875 and their effects can be rolled back:
2876 and their effects can be rolled back:
2876
2877
2877 - commit
2878 - commit
2878 - import
2879 - import
2879 - pull
2880 - pull
2880 - push (with this repository as the destination)
2881 - push (with this repository as the destination)
2881 - unbundle
2882 - unbundle
2882
2883
2883 This command is not intended for use on public repositories. Once
2884 This command is not intended for use on public repositories. Once
2884 changes are visible for pull by other users, rolling a transaction
2885 changes are visible for pull by other users, rolling a transaction
2885 back locally is ineffective (someone else may already have pulled
2886 back locally is ineffective (someone else may already have pulled
2886 the changes). Furthermore, a race is possible with readers of the
2887 the changes). Furthermore, a race is possible with readers of the
2887 repository; for example an in-progress pull from the repository
2888 repository; for example an in-progress pull from the repository
2888 may fail if a rollback is performed.
2889 may fail if a rollback is performed.
2889 """
2890 """
2890 repo.rollback(opts.get('dry_run'))
2891 repo.rollback(opts.get('dry_run'))
2891
2892
2892 def root(ui, repo):
2893 def root(ui, repo):
2893 """print the root (top) of the current working directory
2894 """print the root (top) of the current working directory
2894
2895
2895 Print the root directory of the current repository.
2896 Print the root directory of the current repository.
2896 """
2897 """
2897 ui.write(repo.root + "\n")
2898 ui.write(repo.root + "\n")
2898
2899
2899 def serve(ui, repo, **opts):
2900 def serve(ui, repo, **opts):
2900 """start stand-alone webserver
2901 """start stand-alone webserver
2901
2902
2902 Start a local HTTP repository browser and pull server.
2903 Start a local HTTP repository browser and pull server.
2903
2904
2904 By default, the server logs accesses to stdout and errors to
2905 By default, the server logs accesses to stdout and errors to
2905 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2906 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2906 files.
2907 files.
2907
2908
2908 To have the server choose a free port number to listen on, specify
2909 To have the server choose a free port number to listen on, specify
2909 a port number of 0; in this case, the server will print the port
2910 a port number of 0; in this case, the server will print the port
2910 number it uses.
2911 number it uses.
2911 """
2912 """
2912
2913
2913 if opts["stdio"]:
2914 if opts["stdio"]:
2914 if repo is None:
2915 if repo is None:
2915 raise error.RepoError(_("There is no Mercurial repository here"
2916 raise error.RepoError(_("There is no Mercurial repository here"
2916 " (.hg not found)"))
2917 " (.hg not found)"))
2917 s = sshserver.sshserver(ui, repo)
2918 s = sshserver.sshserver(ui, repo)
2918 s.serve_forever()
2919 s.serve_forever()
2919
2920
2920 # this way we can check if something was given in the command-line
2921 # this way we can check if something was given in the command-line
2921 if opts.get('port'):
2922 if opts.get('port'):
2922 opts['port'] = int(opts.get('port'))
2923 opts['port'] = int(opts.get('port'))
2923
2924
2924 baseui = repo and repo.baseui or ui
2925 baseui = repo and repo.baseui or ui
2925 optlist = ("name templates style address port prefix ipv6"
2926 optlist = ("name templates style address port prefix ipv6"
2926 " accesslog errorlog certificate encoding")
2927 " accesslog errorlog certificate encoding")
2927 for o in optlist.split():
2928 for o in optlist.split():
2928 val = opts.get(o, '')
2929 val = opts.get(o, '')
2929 if val in (None, ''): # should check against default options instead
2930 if val in (None, ''): # should check against default options instead
2930 continue
2931 continue
2931 baseui.setconfig("web", o, val)
2932 baseui.setconfig("web", o, val)
2932 if repo and repo.ui != baseui:
2933 if repo and repo.ui != baseui:
2933 repo.ui.setconfig("web", o, val)
2934 repo.ui.setconfig("web", o, val)
2934
2935
2935 o = opts.get('web_conf') or opts.get('webdir_conf')
2936 o = opts.get('web_conf') or opts.get('webdir_conf')
2936 if not o:
2937 if not o:
2937 if not repo:
2938 if not repo:
2938 raise error.RepoError(_("There is no Mercurial repository"
2939 raise error.RepoError(_("There is no Mercurial repository"
2939 " here (.hg not found)"))
2940 " here (.hg not found)"))
2940 o = repo.root
2941 o = repo.root
2941
2942
2942 app = hgweb.hgweb(o, baseui=ui)
2943 app = hgweb.hgweb(o, baseui=ui)
2943
2944
2944 class service(object):
2945 class service(object):
2945 def init(self):
2946 def init(self):
2946 util.set_signal_handler()
2947 util.set_signal_handler()
2947 self.httpd = hgweb.server.create_server(ui, app)
2948 self.httpd = hgweb.server.create_server(ui, app)
2948
2949
2949 if opts['port'] and not ui.verbose:
2950 if opts['port'] and not ui.verbose:
2950 return
2951 return
2951
2952
2952 if self.httpd.prefix:
2953 if self.httpd.prefix:
2953 prefix = self.httpd.prefix.strip('/') + '/'
2954 prefix = self.httpd.prefix.strip('/') + '/'
2954 else:
2955 else:
2955 prefix = ''
2956 prefix = ''
2956
2957
2957 port = ':%d' % self.httpd.port
2958 port = ':%d' % self.httpd.port
2958 if port == ':80':
2959 if port == ':80':
2959 port = ''
2960 port = ''
2960
2961
2961 bindaddr = self.httpd.addr
2962 bindaddr = self.httpd.addr
2962 if bindaddr == '0.0.0.0':
2963 if bindaddr == '0.0.0.0':
2963 bindaddr = '*'
2964 bindaddr = '*'
2964 elif ':' in bindaddr: # IPv6
2965 elif ':' in bindaddr: # IPv6
2965 bindaddr = '[%s]' % bindaddr
2966 bindaddr = '[%s]' % bindaddr
2966
2967
2967 fqaddr = self.httpd.fqaddr
2968 fqaddr = self.httpd.fqaddr
2968 if ':' in fqaddr:
2969 if ':' in fqaddr:
2969 fqaddr = '[%s]' % fqaddr
2970 fqaddr = '[%s]' % fqaddr
2970 if opts['port']:
2971 if opts['port']:
2971 write = ui.status
2972 write = ui.status
2972 else:
2973 else:
2973 write = ui.write
2974 write = ui.write
2974 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2975 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2975 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2976 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2976
2977
2977 def run(self):
2978 def run(self):
2978 self.httpd.serve_forever()
2979 self.httpd.serve_forever()
2979
2980
2980 service = service()
2981 service = service()
2981
2982
2982 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2983 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2983
2984
2984 def status(ui, repo, *pats, **opts):
2985 def status(ui, repo, *pats, **opts):
2985 """show changed files in the working directory
2986 """show changed files in the working directory
2986
2987
2987 Show status of files in the repository. If names are given, only
2988 Show status of files in the repository. If names are given, only
2988 files that match are shown. Files that are clean or ignored or
2989 files that match are shown. Files that are clean or ignored or
2989 the source of a copy/move operation, are not listed unless
2990 the source of a copy/move operation, are not listed unless
2990 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2991 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2991 Unless options described with "show only ..." are given, the
2992 Unless options described with "show only ..." are given, the
2992 options -mardu are used.
2993 options -mardu are used.
2993
2994
2994 Option -q/--quiet hides untracked (unknown and ignored) files
2995 Option -q/--quiet hides untracked (unknown and ignored) files
2995 unless explicitly requested with -u/--unknown or -i/--ignored.
2996 unless explicitly requested with -u/--unknown or -i/--ignored.
2996
2997
2997 NOTE: status may appear to disagree with diff if permissions have
2998 NOTE: status may appear to disagree with diff if permissions have
2998 changed or a merge has occurred. The standard diff format does not
2999 changed or a merge has occurred. The standard diff format does not
2999 report permission changes and diff only reports changes relative
3000 report permission changes and diff only reports changes relative
3000 to one merge parent.
3001 to one merge parent.
3001
3002
3002 If one revision is given, it is used as the base revision.
3003 If one revision is given, it is used as the base revision.
3003 If two revisions are given, the differences between them are
3004 If two revisions are given, the differences between them are
3004 shown. The --change option can also be used as a shortcut to list
3005 shown. The --change option can also be used as a shortcut to list
3005 the changed files of a revision from its first parent.
3006 the changed files of a revision from its first parent.
3006
3007
3007 The codes used to show the status of files are::
3008 The codes used to show the status of files are::
3008
3009
3009 M = modified
3010 M = modified
3010 A = added
3011 A = added
3011 R = removed
3012 R = removed
3012 C = clean
3013 C = clean
3013 ! = missing (deleted by non-hg command, but still tracked)
3014 ! = missing (deleted by non-hg command, but still tracked)
3014 ? = not tracked
3015 ? = not tracked
3015 I = ignored
3016 I = ignored
3016 = origin of the previous file listed as A (added)
3017 = origin of the previous file listed as A (added)
3017 """
3018 """
3018
3019
3019 revs = opts.get('rev')
3020 revs = opts.get('rev')
3020 change = opts.get('change')
3021 change = opts.get('change')
3021
3022
3022 if revs and change:
3023 if revs and change:
3023 msg = _('cannot specify --rev and --change at the same time')
3024 msg = _('cannot specify --rev and --change at the same time')
3024 raise util.Abort(msg)
3025 raise util.Abort(msg)
3025 elif change:
3026 elif change:
3026 node2 = repo.lookup(change)
3027 node2 = repo.lookup(change)
3027 node1 = repo[node2].parents()[0].node()
3028 node1 = repo[node2].parents()[0].node()
3028 else:
3029 else:
3029 node1, node2 = cmdutil.revpair(repo, revs)
3030 node1, node2 = cmdutil.revpair(repo, revs)
3030
3031
3031 cwd = (pats and repo.getcwd()) or ''
3032 cwd = (pats and repo.getcwd()) or ''
3032 end = opts.get('print0') and '\0' or '\n'
3033 end = opts.get('print0') and '\0' or '\n'
3033 copy = {}
3034 copy = {}
3034 states = 'modified added removed deleted unknown ignored clean'.split()
3035 states = 'modified added removed deleted unknown ignored clean'.split()
3035 show = [k for k in states if opts.get(k)]
3036 show = [k for k in states if opts.get(k)]
3036 if opts.get('all'):
3037 if opts.get('all'):
3037 show += ui.quiet and (states[:4] + ['clean']) or states
3038 show += ui.quiet and (states[:4] + ['clean']) or states
3038 if not show:
3039 if not show:
3039 show = ui.quiet and states[:4] or states[:5]
3040 show = ui.quiet and states[:4] or states[:5]
3040
3041
3041 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3042 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3042 'ignored' in show, 'clean' in show, 'unknown' in show)
3043 'ignored' in show, 'clean' in show, 'unknown' in show)
3043 changestates = zip(states, 'MAR!?IC', stat)
3044 changestates = zip(states, 'MAR!?IC', stat)
3044
3045
3045 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3046 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3046 ctxn = repo[nullid]
3047 ctxn = repo[nullid]
3047 ctx1 = repo[node1]
3048 ctx1 = repo[node1]
3048 ctx2 = repo[node2]
3049 ctx2 = repo[node2]
3049 added = stat[1]
3050 added = stat[1]
3050 if node2 is None:
3051 if node2 is None:
3051 added = stat[0] + stat[1] # merged?
3052 added = stat[0] + stat[1] # merged?
3052
3053
3053 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3054 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3054 if k in added:
3055 if k in added:
3055 copy[k] = v
3056 copy[k] = v
3056 elif v in added:
3057 elif v in added:
3057 copy[v] = k
3058 copy[v] = k
3058
3059
3059 for state, char, files in changestates:
3060 for state, char, files in changestates:
3060 if state in show:
3061 if state in show:
3061 format = "%s %%s%s" % (char, end)
3062 format = "%s %%s%s" % (char, end)
3062 if opts.get('no_status'):
3063 if opts.get('no_status'):
3063 format = "%%s%s" % end
3064 format = "%%s%s" % end
3064
3065
3065 for f in files:
3066 for f in files:
3066 ui.write(format % repo.pathto(f, cwd),
3067 ui.write(format % repo.pathto(f, cwd),
3067 label='status.' + state)
3068 label='status.' + state)
3068 if f in copy:
3069 if f in copy:
3069 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3070 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3070 label='status.copied')
3071 label='status.copied')
3071
3072
3072 def summary(ui, repo, **opts):
3073 def summary(ui, repo, **opts):
3073 """summarize working directory state
3074 """summarize working directory state
3074
3075
3075 This generates a brief summary of the working directory state,
3076 This generates a brief summary of the working directory state,
3076 including parents, branch, commit status, and available updates.
3077 including parents, branch, commit status, and available updates.
3077
3078
3078 With the --remote option, this will check the default paths for
3079 With the --remote option, this will check the default paths for
3079 incoming and outgoing changes. This can be time-consuming.
3080 incoming and outgoing changes. This can be time-consuming.
3080 """
3081 """
3081
3082
3082 ctx = repo[None]
3083 ctx = repo[None]
3083 parents = ctx.parents()
3084 parents = ctx.parents()
3084 pnode = parents[0].node()
3085 pnode = parents[0].node()
3085
3086
3086 for p in parents:
3087 for p in parents:
3087 # label with log.changeset (instead of log.parent) since this
3088 # label with log.changeset (instead of log.parent) since this
3088 # shows a working directory parent *changeset*:
3089 # shows a working directory parent *changeset*:
3089 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3090 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3090 label='log.changeset')
3091 label='log.changeset')
3091 ui.write(' '.join(p.tags()), label='log.tag')
3092 ui.write(' '.join(p.tags()), label='log.tag')
3092 if p.rev() == -1:
3093 if p.rev() == -1:
3093 if not len(repo):
3094 if not len(repo):
3094 ui.write(_(' (empty repository)'))
3095 ui.write(_(' (empty repository)'))
3095 else:
3096 else:
3096 ui.write(_(' (no revision checked out)'))
3097 ui.write(_(' (no revision checked out)'))
3097 ui.write('\n')
3098 ui.write('\n')
3098 if p.description():
3099 if p.description():
3099 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3100 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3100 label='log.summary')
3101 label='log.summary')
3101
3102
3102 branch = ctx.branch()
3103 branch = ctx.branch()
3103 bheads = repo.branchheads(branch)
3104 bheads = repo.branchheads(branch)
3104 m = _('branch: %s\n') % branch
3105 m = _('branch: %s\n') % branch
3105 if branch != 'default':
3106 if branch != 'default':
3106 ui.write(m, label='log.branch')
3107 ui.write(m, label='log.branch')
3107 else:
3108 else:
3108 ui.status(m, label='log.branch')
3109 ui.status(m, label='log.branch')
3109
3110
3110 st = list(repo.status(unknown=True))[:6]
3111 st = list(repo.status(unknown=True))[:6]
3111 ms = mergemod.mergestate(repo)
3112 ms = mergemod.mergestate(repo)
3112 st.append([f for f in ms if ms[f] == 'u'])
3113 st.append([f for f in ms if ms[f] == 'u'])
3113 labels = [ui.label(_('%d modified'), 'status.modified'),
3114 labels = [ui.label(_('%d modified'), 'status.modified'),
3114 ui.label(_('%d added'), 'status.added'),
3115 ui.label(_('%d added'), 'status.added'),
3115 ui.label(_('%d removed'), 'status.removed'),
3116 ui.label(_('%d removed'), 'status.removed'),
3116 ui.label(_('%d deleted'), 'status.deleted'),
3117 ui.label(_('%d deleted'), 'status.deleted'),
3117 ui.label(_('%d unknown'), 'status.unknown'),
3118 ui.label(_('%d unknown'), 'status.unknown'),
3118 ui.label(_('%d ignored'), 'status.ignored'),
3119 ui.label(_('%d ignored'), 'status.ignored'),
3119 ui.label(_('%d unresolved'), 'resolve.unresolved')]
3120 ui.label(_('%d unresolved'), 'resolve.unresolved')]
3120 t = []
3121 t = []
3121 for s, l in zip(st, labels):
3122 for s, l in zip(st, labels):
3122 if s:
3123 if s:
3123 t.append(l % len(s))
3124 t.append(l % len(s))
3124
3125
3125 t = ', '.join(t)
3126 t = ', '.join(t)
3126 cleanworkdir = False
3127 cleanworkdir = False
3127
3128
3128 if len(parents) > 1:
3129 if len(parents) > 1:
3129 t += _(' (merge)')
3130 t += _(' (merge)')
3130 elif branch != parents[0].branch():
3131 elif branch != parents[0].branch():
3131 t += _(' (new branch)')
3132 t += _(' (new branch)')
3132 elif (not st[0] and not st[1] and not st[2]):
3133 elif (not st[0] and not st[1] and not st[2]):
3133 t += _(' (clean)')
3134 t += _(' (clean)')
3134 cleanworkdir = True
3135 cleanworkdir = True
3135 elif pnode not in bheads:
3136 elif pnode not in bheads:
3136 t += _(' (new branch head)')
3137 t += _(' (new branch head)')
3137
3138
3138 if cleanworkdir:
3139 if cleanworkdir:
3139 ui.status(_('commit: %s\n') % t.strip())
3140 ui.status(_('commit: %s\n') % t.strip())
3140 else:
3141 else:
3141 ui.write(_('commit: %s\n') % t.strip())
3142 ui.write(_('commit: %s\n') % t.strip())
3142
3143
3143 # all ancestors of branch heads - all ancestors of parent = new csets
3144 # all ancestors of branch heads - all ancestors of parent = new csets
3144 new = [0] * len(repo)
3145 new = [0] * len(repo)
3145 cl = repo.changelog
3146 cl = repo.changelog
3146 for a in [cl.rev(n) for n in bheads]:
3147 for a in [cl.rev(n) for n in bheads]:
3147 new[a] = 1
3148 new[a] = 1
3148 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3149 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3149 new[a] = 1
3150 new[a] = 1
3150 for a in [p.rev() for p in parents]:
3151 for a in [p.rev() for p in parents]:
3151 if a >= 0:
3152 if a >= 0:
3152 new[a] = 0
3153 new[a] = 0
3153 for a in cl.ancestors(*[p.rev() for p in parents]):
3154 for a in cl.ancestors(*[p.rev() for p in parents]):
3154 new[a] = 0
3155 new[a] = 0
3155 new = sum(new)
3156 new = sum(new)
3156
3157
3157 if new == 0:
3158 if new == 0:
3158 ui.status(_('update: (current)\n'))
3159 ui.status(_('update: (current)\n'))
3159 elif pnode not in bheads:
3160 elif pnode not in bheads:
3160 ui.write(_('update: %d new changesets (update)\n') % new)
3161 ui.write(_('update: %d new changesets (update)\n') % new)
3161 else:
3162 else:
3162 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3163 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3163 (new, len(bheads)))
3164 (new, len(bheads)))
3164
3165
3165 if opts.get('remote'):
3166 if opts.get('remote'):
3166 t = []
3167 t = []
3167 source, branches = hg.parseurl(ui.expandpath('default'))
3168 source, branches = hg.parseurl(ui.expandpath('default'))
3168 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3169 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3169 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3170 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3170 ui.debug('comparing with %s\n' % url.hidepassword(source))
3171 ui.debug('comparing with %s\n' % url.hidepassword(source))
3171 repo.ui.pushbuffer()
3172 repo.ui.pushbuffer()
3172 common, incoming, rheads = repo.findcommonincoming(other)
3173 common, incoming, rheads = repo.findcommonincoming(other)
3173 repo.ui.popbuffer()
3174 repo.ui.popbuffer()
3174 if incoming:
3175 if incoming:
3175 t.append(_('1 or more incoming'))
3176 t.append(_('1 or more incoming'))
3176
3177
3177 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3178 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3178 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3179 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3179 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3180 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3180 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3181 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3181 repo.ui.pushbuffer()
3182 repo.ui.pushbuffer()
3182 o = repo.findoutgoing(other)
3183 o = repo.findoutgoing(other)
3183 repo.ui.popbuffer()
3184 repo.ui.popbuffer()
3184 o = repo.changelog.nodesbetween(o, None)[0]
3185 o = repo.changelog.nodesbetween(o, None)[0]
3185 if o:
3186 if o:
3186 t.append(_('%d outgoing') % len(o))
3187 t.append(_('%d outgoing') % len(o))
3187
3188
3188 if t:
3189 if t:
3189 ui.write(_('remote: %s\n') % (', '.join(t)))
3190 ui.write(_('remote: %s\n') % (', '.join(t)))
3190 else:
3191 else:
3191 ui.status(_('remote: (synced)\n'))
3192 ui.status(_('remote: (synced)\n'))
3192
3193
3193 def tag(ui, repo, name1, *names, **opts):
3194 def tag(ui, repo, name1, *names, **opts):
3194 """add one or more tags for the current or given revision
3195 """add one or more tags for the current or given revision
3195
3196
3196 Name a particular revision using <name>.
3197 Name a particular revision using <name>.
3197
3198
3198 Tags are used to name particular revisions of the repository and are
3199 Tags are used to name particular revisions of the repository and are
3199 very useful to compare different revisions, to go back to significant
3200 very useful to compare different revisions, to go back to significant
3200 earlier versions or to mark branch points as releases, etc.
3201 earlier versions or to mark branch points as releases, etc.
3201
3202
3202 If no revision is given, the parent of the working directory is
3203 If no revision is given, the parent of the working directory is
3203 used, or tip if no revision is checked out.
3204 used, or tip if no revision is checked out.
3204
3205
3205 To facilitate version control, distribution, and merging of tags,
3206 To facilitate version control, distribution, and merging of tags,
3206 they are stored as a file named ".hgtags" which is managed
3207 they are stored as a file named ".hgtags" which is managed
3207 similarly to other project files and can be hand-edited if
3208 similarly to other project files and can be hand-edited if
3208 necessary. The file '.hg/localtags' is used for local tags (not
3209 necessary. The file '.hg/localtags' is used for local tags (not
3209 shared among repositories).
3210 shared among repositories).
3210
3211
3211 See :hg:`help dates` for a list of formats valid for -d/--date.
3212 See :hg:`help dates` for a list of formats valid for -d/--date.
3212
3213
3213 Since tag names have priority over branch names during revision
3214 Since tag names have priority over branch names during revision
3214 lookup, using an existing branch name as a tag name is discouraged.
3215 lookup, using an existing branch name as a tag name is discouraged.
3215 """
3216 """
3216
3217
3217 rev_ = "."
3218 rev_ = "."
3218 names = (name1,) + names
3219 names = (name1,) + names
3219 if len(names) != len(set(names)):
3220 if len(names) != len(set(names)):
3220 raise util.Abort(_('tag names must be unique'))
3221 raise util.Abort(_('tag names must be unique'))
3221 for n in names:
3222 for n in names:
3222 if n in ['tip', '.', 'null']:
3223 if n in ['tip', '.', 'null']:
3223 raise util.Abort(_('the name \'%s\' is reserved') % n)
3224 raise util.Abort(_('the name \'%s\' is reserved') % n)
3224 if opts.get('rev') and opts.get('remove'):
3225 if opts.get('rev') and opts.get('remove'):
3225 raise util.Abort(_("--rev and --remove are incompatible"))
3226 raise util.Abort(_("--rev and --remove are incompatible"))
3226 if opts.get('rev'):
3227 if opts.get('rev'):
3227 rev_ = opts['rev']
3228 rev_ = opts['rev']
3228 message = opts.get('message')
3229 message = opts.get('message')
3229 if opts.get('remove'):
3230 if opts.get('remove'):
3230 expectedtype = opts.get('local') and 'local' or 'global'
3231 expectedtype = opts.get('local') and 'local' or 'global'
3231 for n in names:
3232 for n in names:
3232 if not repo.tagtype(n):
3233 if not repo.tagtype(n):
3233 raise util.Abort(_('tag \'%s\' does not exist') % n)
3234 raise util.Abort(_('tag \'%s\' does not exist') % n)
3234 if repo.tagtype(n) != expectedtype:
3235 if repo.tagtype(n) != expectedtype:
3235 if expectedtype == 'global':
3236 if expectedtype == 'global':
3236 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3237 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3237 else:
3238 else:
3238 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3239 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3239 rev_ = nullid
3240 rev_ = nullid
3240 if not message:
3241 if not message:
3241 # we don't translate commit messages
3242 # we don't translate commit messages
3242 message = 'Removed tag %s' % ', '.join(names)
3243 message = 'Removed tag %s' % ', '.join(names)
3243 elif not opts.get('force'):
3244 elif not opts.get('force'):
3244 for n in names:
3245 for n in names:
3245 if n in repo.tags():
3246 if n in repo.tags():
3246 raise util.Abort(_('tag \'%s\' already exists '
3247 raise util.Abort(_('tag \'%s\' already exists '
3247 '(use -f to force)') % n)
3248 '(use -f to force)') % n)
3248 if not rev_ and repo.dirstate.parents()[1] != nullid:
3249 if not rev_ and repo.dirstate.parents()[1] != nullid:
3249 raise util.Abort(_('uncommitted merge - please provide a '
3250 raise util.Abort(_('uncommitted merge - please provide a '
3250 'specific revision'))
3251 'specific revision'))
3251 r = repo[rev_].node()
3252 r = repo[rev_].node()
3252
3253
3253 if not message:
3254 if not message:
3254 # we don't translate commit messages
3255 # we don't translate commit messages
3255 message = ('Added tag %s for changeset %s' %
3256 message = ('Added tag %s for changeset %s' %
3256 (', '.join(names), short(r)))
3257 (', '.join(names), short(r)))
3257
3258
3258 date = opts.get('date')
3259 date = opts.get('date')
3259 if date:
3260 if date:
3260 date = util.parsedate(date)
3261 date = util.parsedate(date)
3261
3262
3262 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3263 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3263
3264
3264 def tags(ui, repo):
3265 def tags(ui, repo):
3265 """list repository tags
3266 """list repository tags
3266
3267
3267 This lists both regular and local tags. When the -v/--verbose
3268 This lists both regular and local tags. When the -v/--verbose
3268 switch is used, a third column "local" is printed for local tags.
3269 switch is used, a third column "local" is printed for local tags.
3269 """
3270 """
3270
3271
3271 hexfunc = ui.debugflag and hex or short
3272 hexfunc = ui.debugflag and hex or short
3272 tagtype = ""
3273 tagtype = ""
3273
3274
3274 for t, n in reversed(repo.tagslist()):
3275 for t, n in reversed(repo.tagslist()):
3275 if ui.quiet:
3276 if ui.quiet:
3276 ui.write("%s\n" % t)
3277 ui.write("%s\n" % t)
3277 continue
3278 continue
3278
3279
3279 try:
3280 try:
3280 hn = hexfunc(n)
3281 hn = hexfunc(n)
3281 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3282 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3282 except error.LookupError:
3283 except error.LookupError:
3283 r = " ?:%s" % hn
3284 r = " ?:%s" % hn
3284 else:
3285 else:
3285 spaces = " " * (30 - encoding.colwidth(t))
3286 spaces = " " * (30 - encoding.colwidth(t))
3286 if ui.verbose:
3287 if ui.verbose:
3287 if repo.tagtype(t) == 'local':
3288 if repo.tagtype(t) == 'local':
3288 tagtype = " local"
3289 tagtype = " local"
3289 else:
3290 else:
3290 tagtype = ""
3291 tagtype = ""
3291 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3292 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3292
3293
3293 def tip(ui, repo, **opts):
3294 def tip(ui, repo, **opts):
3294 """show the tip revision
3295 """show the tip revision
3295
3296
3296 The tip revision (usually just called the tip) is the changeset
3297 The tip revision (usually just called the tip) is the changeset
3297 most recently added to the repository (and therefore the most
3298 most recently added to the repository (and therefore the most
3298 recently changed head).
3299 recently changed head).
3299
3300
3300 If you have just made a commit, that commit will be the tip. If
3301 If you have just made a commit, that commit will be the tip. If
3301 you have just pulled changes from another repository, the tip of
3302 you have just pulled changes from another repository, the tip of
3302 that repository becomes the current tip. The "tip" tag is special
3303 that repository becomes the current tip. The "tip" tag is special
3303 and cannot be renamed or assigned to a different changeset.
3304 and cannot be renamed or assigned to a different changeset.
3304 """
3305 """
3305 displayer = cmdutil.show_changeset(ui, repo, opts)
3306 displayer = cmdutil.show_changeset(ui, repo, opts)
3306 displayer.show(repo[len(repo) - 1])
3307 displayer.show(repo[len(repo) - 1])
3307 displayer.close()
3308 displayer.close()
3308
3309
3309 def unbundle(ui, repo, fname1, *fnames, **opts):
3310 def unbundle(ui, repo, fname1, *fnames, **opts):
3310 """apply one or more changegroup files
3311 """apply one or more changegroup files
3311
3312
3312 Apply one or more compressed changegroup files generated by the
3313 Apply one or more compressed changegroup files generated by the
3313 bundle command.
3314 bundle command.
3314 """
3315 """
3315 fnames = (fname1,) + fnames
3316 fnames = (fname1,) + fnames
3316
3317
3317 lock = repo.lock()
3318 lock = repo.lock()
3318 try:
3319 try:
3319 for fname in fnames:
3320 for fname in fnames:
3320 f = url.open(ui, fname)
3321 f = url.open(ui, fname)
3321 gen = changegroup.readbundle(f, fname)
3322 gen = changegroup.readbundle(f, fname)
3322 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3323 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3323 finally:
3324 finally:
3324 lock.release()
3325 lock.release()
3325
3326
3326 return postincoming(ui, repo, modheads, opts.get('update'), None)
3327 return postincoming(ui, repo, modheads, opts.get('update'), None)
3327
3328
3328 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3329 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3329 """update working directory (or switch revisions)
3330 """update working directory (or switch revisions)
3330
3331
3331 Update the repository's working directory to the specified
3332 Update the repository's working directory to the specified
3332 changeset.
3333 changeset.
3333
3334
3334 If no changeset is specified, attempt to update to the head of the
3335 If no changeset is specified, attempt to update to the head of the
3335 current branch. If this head is a descendant of the working
3336 current branch. If this head is a descendant of the working
3336 directory's parent, update to it, otherwise abort.
3337 directory's parent, update to it, otherwise abort.
3337
3338
3338 The following rules apply when the working directory contains
3339 The following rules apply when the working directory contains
3339 uncommitted changes:
3340 uncommitted changes:
3340
3341
3341 1. If neither -c/--check nor -C/--clean is specified, and if
3342 1. If neither -c/--check nor -C/--clean is specified, and if
3342 the requested changeset is an ancestor or descendant of
3343 the requested changeset is an ancestor or descendant of
3343 the working directory's parent, the uncommitted changes
3344 the working directory's parent, the uncommitted changes
3344 are merged into the requested changeset and the merged
3345 are merged into the requested changeset and the merged
3345 result is left uncommitted. If the requested changeset is
3346 result is left uncommitted. If the requested changeset is
3346 not an ancestor or descendant (that is, it is on another
3347 not an ancestor or descendant (that is, it is on another
3347 branch), the update is aborted and the uncommitted changes
3348 branch), the update is aborted and the uncommitted changes
3348 are preserved.
3349 are preserved.
3349
3350
3350 2. With the -c/--check option, the update is aborted and the
3351 2. With the -c/--check option, the update is aborted and the
3351 uncommitted changes are preserved.
3352 uncommitted changes are preserved.
3352
3353
3353 3. With the -C/--clean option, uncommitted changes are discarded and
3354 3. With the -C/--clean option, uncommitted changes are discarded and
3354 the working directory is updated to the requested changeset.
3355 the working directory is updated to the requested changeset.
3355
3356
3356 Use null as the changeset to remove the working directory (like
3357 Use null as the changeset to remove the working directory (like
3357 :hg:`clone -U`).
3358 :hg:`clone -U`).
3358
3359
3359 If you want to update just one file to an older changeset, use :hg:`revert`.
3360 If you want to update just one file to an older changeset, use :hg:`revert`.
3360
3361
3361 See :hg:`help dates` for a list of formats valid for -d/--date.
3362 See :hg:`help dates` for a list of formats valid for -d/--date.
3362 """
3363 """
3363 if rev and node:
3364 if rev and node:
3364 raise util.Abort(_("please specify just one revision"))
3365 raise util.Abort(_("please specify just one revision"))
3365
3366
3366 if not rev:
3367 if not rev:
3367 rev = node
3368 rev = node
3368
3369
3369 if check and clean:
3370 if check and clean:
3370 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3371 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3371
3372
3372 if check:
3373 if check:
3373 # we could use dirty() but we can ignore merge and branch trivia
3374 # we could use dirty() but we can ignore merge and branch trivia
3374 c = repo[None]
3375 c = repo[None]
3375 if c.modified() or c.added() or c.removed():
3376 if c.modified() or c.added() or c.removed():
3376 raise util.Abort(_("uncommitted local changes"))
3377 raise util.Abort(_("uncommitted local changes"))
3377
3378
3378 if date:
3379 if date:
3379 if rev:
3380 if rev:
3380 raise util.Abort(_("you can't specify a revision and a date"))
3381 raise util.Abort(_("you can't specify a revision and a date"))
3381 rev = cmdutil.finddate(ui, repo, date)
3382 rev = cmdutil.finddate(ui, repo, date)
3382
3383
3383 if clean or check:
3384 if clean or check:
3384 return hg.clean(repo, rev)
3385 return hg.clean(repo, rev)
3385 else:
3386 else:
3386 return hg.update(repo, rev)
3387 return hg.update(repo, rev)
3387
3388
3388 def verify(ui, repo):
3389 def verify(ui, repo):
3389 """verify the integrity of the repository
3390 """verify the integrity of the repository
3390
3391
3391 Verify the integrity of the current repository.
3392 Verify the integrity of the current repository.
3392
3393
3393 This will perform an extensive check of the repository's
3394 This will perform an extensive check of the repository's
3394 integrity, validating the hashes and checksums of each entry in
3395 integrity, validating the hashes and checksums of each entry in
3395 the changelog, manifest, and tracked files, as well as the
3396 the changelog, manifest, and tracked files, as well as the
3396 integrity of their crosslinks and indices.
3397 integrity of their crosslinks and indices.
3397 """
3398 """
3398 return hg.verify(repo)
3399 return hg.verify(repo)
3399
3400
3400 def version_(ui):
3401 def version_(ui):
3401 """output version and copyright information"""
3402 """output version and copyright information"""
3402 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3403 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3403 % util.version())
3404 % util.version())
3404 ui.status(_(
3405 ui.status(_(
3405 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3406 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3406 "This is free software; see the source for copying conditions. "
3407 "This is free software; see the source for copying conditions. "
3407 "There is NO\nwarranty; "
3408 "There is NO\nwarranty; "
3408 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3409 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3409 ))
3410 ))
3410
3411
3411 # Command options and aliases are listed here, alphabetically
3412 # Command options and aliases are listed here, alphabetically
3412
3413
3413 globalopts = [
3414 globalopts = [
3414 ('R', 'repository', '',
3415 ('R', 'repository', '',
3415 _('repository root directory or name of overlay bundle file')),
3416 _('repository root directory or name of overlay bundle file')),
3416 ('', 'cwd', '', _('change working directory')),
3417 ('', 'cwd', '', _('change working directory')),
3417 ('y', 'noninteractive', None,
3418 ('y', 'noninteractive', None,
3418 _('do not prompt, assume \'yes\' for any required answers')),
3419 _('do not prompt, assume \'yes\' for any required answers')),
3419 ('q', 'quiet', None, _('suppress output')),
3420 ('q', 'quiet', None, _('suppress output')),
3420 ('v', 'verbose', None, _('enable additional output')),
3421 ('v', 'verbose', None, _('enable additional output')),
3421 ('', 'config', [],
3422 ('', 'config', [],
3422 _('set/override config option (use \'section.name=value\')')),
3423 _('set/override config option (use \'section.name=value\')')),
3423 ('', 'debug', None, _('enable debugging output')),
3424 ('', 'debug', None, _('enable debugging output')),
3424 ('', 'debugger', None, _('start debugger')),
3425 ('', 'debugger', None, _('start debugger')),
3425 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3426 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3426 ('', 'encodingmode', encoding.encodingmode,
3427 ('', 'encodingmode', encoding.encodingmode,
3427 _('set the charset encoding mode')),
3428 _('set the charset encoding mode')),
3428 ('', 'traceback', None, _('always print a traceback on exception')),
3429 ('', 'traceback', None, _('always print a traceback on exception')),
3429 ('', 'time', None, _('time how long the command takes')),
3430 ('', 'time', None, _('time how long the command takes')),
3430 ('', 'profile', None, _('print command execution profile')),
3431 ('', 'profile', None, _('print command execution profile')),
3431 ('', 'version', None, _('output version information and exit')),
3432 ('', 'version', None, _('output version information and exit')),
3432 ('h', 'help', None, _('display help and exit')),
3433 ('h', 'help', None, _('display help and exit')),
3433 ]
3434 ]
3434
3435
3435 dryrunopts = [('n', 'dry-run', None,
3436 dryrunopts = [('n', 'dry-run', None,
3436 _('do not perform actions, just print output'))]
3437 _('do not perform actions, just print output'))]
3437
3438
3438 remoteopts = [
3439 remoteopts = [
3439 ('e', 'ssh', '', _('specify ssh command to use')),
3440 ('e', 'ssh', '', _('specify ssh command to use')),
3440 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3441 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3441 ]
3442 ]
3442
3443
3443 walkopts = [
3444 walkopts = [
3444 ('I', 'include', [], _('include names matching the given patterns')),
3445 ('I', 'include', [], _('include names matching the given patterns')),
3445 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3446 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3446 ]
3447 ]
3447
3448
3448 commitopts = [
3449 commitopts = [
3449 ('m', 'message', '', _('use <text> as commit message')),
3450 ('m', 'message', '', _('use <text> as commit message')),
3450 ('l', 'logfile', '', _('read commit message from <file>')),
3451 ('l', 'logfile', '', _('read commit message from <file>')),
3451 ]
3452 ]
3452
3453
3453 commitopts2 = [
3454 commitopts2 = [
3454 ('d', 'date', '', _('record datecode as commit date')),
3455 ('d', 'date', '', _('record datecode as commit date')),
3455 ('u', 'user', '', _('record the specified user as committer')),
3456 ('u', 'user', '', _('record the specified user as committer')),
3456 ]
3457 ]
3457
3458
3458 templateopts = [
3459 templateopts = [
3459 ('', 'style', '', _('display using template map file')),
3460 ('', 'style', '', _('display using template map file')),
3460 ('', 'template', '', _('display with template')),
3461 ('', 'template', '', _('display with template')),
3461 ]
3462 ]
3462
3463
3463 logopts = [
3464 logopts = [
3464 ('p', 'patch', None, _('show patch')),
3465 ('p', 'patch', None, _('show patch')),
3465 ('g', 'git', None, _('use git extended diff format')),
3466 ('g', 'git', None, _('use git extended diff format')),
3466 ('l', 'limit', '', _('limit number of changes displayed')),
3467 ('l', 'limit', '', _('limit number of changes displayed')),
3467 ('M', 'no-merges', None, _('do not show merges')),
3468 ('M', 'no-merges', None, _('do not show merges')),
3468 ('', 'stat', None, _('output diffstat-style summary of changes')),
3469 ('', 'stat', None, _('output diffstat-style summary of changes')),
3469 ] + templateopts
3470 ] + templateopts
3470
3471
3471 diffopts = [
3472 diffopts = [
3472 ('a', 'text', None, _('treat all files as text')),
3473 ('a', 'text', None, _('treat all files as text')),
3473 ('g', 'git', None, _('use git extended diff format')),
3474 ('g', 'git', None, _('use git extended diff format')),
3474 ('', 'nodates', None, _('omit dates from diff headers'))
3475 ('', 'nodates', None, _('omit dates from diff headers'))
3475 ]
3476 ]
3476
3477
3477 diffopts2 = [
3478 diffopts2 = [
3478 ('p', 'show-function', None, _('show which function each change is in')),
3479 ('p', 'show-function', None, _('show which function each change is in')),
3479 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3480 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3480 ('w', 'ignore-all-space', None,
3481 ('w', 'ignore-all-space', None,
3481 _('ignore white space when comparing lines')),
3482 _('ignore white space when comparing lines')),
3482 ('b', 'ignore-space-change', None,
3483 ('b', 'ignore-space-change', None,
3483 _('ignore changes in the amount of white space')),
3484 _('ignore changes in the amount of white space')),
3484 ('B', 'ignore-blank-lines', None,
3485 ('B', 'ignore-blank-lines', None,
3485 _('ignore changes whose lines are all blank')),
3486 _('ignore changes whose lines are all blank')),
3486 ('U', 'unified', '', _('number of lines of context to show')),
3487 ('U', 'unified', '', _('number of lines of context to show')),
3487 ('', 'stat', None, _('output diffstat-style summary of changes')),
3488 ('', 'stat', None, _('output diffstat-style summary of changes')),
3488 ]
3489 ]
3489
3490
3490 similarityopts = [
3491 similarityopts = [
3491 ('s', 'similarity', '',
3492 ('s', 'similarity', '',
3492 _('guess renamed files by similarity (0<=s<=100)'))
3493 _('guess renamed files by similarity (0<=s<=100)'))
3493 ]
3494 ]
3494
3495
3495 table = {
3496 table = {
3496 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3497 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3497 "addremove":
3498 "addremove":
3498 (addremove, similarityopts + walkopts + dryrunopts,
3499 (addremove, similarityopts + walkopts + dryrunopts,
3499 _('[OPTION]... [FILE]...')),
3500 _('[OPTION]... [FILE]...')),
3500 "^annotate|blame":
3501 "^annotate|blame":
3501 (annotate,
3502 (annotate,
3502 [('r', 'rev', '', _('annotate the specified revision')),
3503 [('r', 'rev', '', _('annotate the specified revision')),
3503 ('', 'follow', None,
3504 ('', 'follow', None,
3504 _('follow copies/renames and list the filename (DEPRECATED)')),
3505 _('follow copies/renames and list the filename (DEPRECATED)')),
3505 ('', 'no-follow', None, _("don't follow copies and renames")),
3506 ('', 'no-follow', None, _("don't follow copies and renames")),
3506 ('a', 'text', None, _('treat all files as text')),
3507 ('a', 'text', None, _('treat all files as text')),
3507 ('u', 'user', None, _('list the author (long with -v)')),
3508 ('u', 'user', None, _('list the author (long with -v)')),
3508 ('f', 'file', None, _('list the filename')),
3509 ('f', 'file', None, _('list the filename')),
3509 ('d', 'date', None, _('list the date (short with -q)')),
3510 ('d', 'date', None, _('list the date (short with -q)')),
3510 ('n', 'number', None, _('list the revision number (default)')),
3511 ('n', 'number', None, _('list the revision number (default)')),
3511 ('c', 'changeset', None, _('list the changeset')),
3512 ('c', 'changeset', None, _('list the changeset')),
3512 ('l', 'line-number', None,
3513 ('l', 'line-number', None,
3513 _('show line number at the first appearance'))
3514 _('show line number at the first appearance'))
3514 ] + walkopts,
3515 ] + walkopts,
3515 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3516 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3516 "archive":
3517 "archive":
3517 (archive,
3518 (archive,
3518 [('', 'no-decode', None, _('do not pass files through decoders')),
3519 [('', 'no-decode', None, _('do not pass files through decoders')),
3519 ('p', 'prefix', '', _('directory prefix for files in archive')),
3520 ('p', 'prefix', '', _('directory prefix for files in archive')),
3520 ('r', 'rev', '', _('revision to distribute')),
3521 ('r', 'rev', '', _('revision to distribute')),
3521 ('t', 'type', '', _('type of distribution to create')),
3522 ('t', 'type', '', _('type of distribution to create')),
3522 ] + walkopts,
3523 ] + walkopts,
3523 _('[OPTION]... DEST')),
3524 _('[OPTION]... DEST')),
3524 "backout":
3525 "backout":
3525 (backout,
3526 (backout,
3526 [('', 'merge', None,
3527 [('', 'merge', None,
3527 _('merge with old dirstate parent after backout')),
3528 _('merge with old dirstate parent after backout')),
3528 ('', 'parent', '', _('parent to choose when backing out merge')),
3529 ('', 'parent', '', _('parent to choose when backing out merge')),
3529 ('r', 'rev', '', _('revision to backout')),
3530 ('r', 'rev', '', _('revision to backout')),
3530 ] + walkopts + commitopts + commitopts2,
3531 ] + walkopts + commitopts + commitopts2,
3531 _('[OPTION]... [-r] REV')),
3532 _('[OPTION]... [-r] REV')),
3532 "bisect":
3533 "bisect":
3533 (bisect,
3534 (bisect,
3534 [('r', 'reset', False, _('reset bisect state')),
3535 [('r', 'reset', False, _('reset bisect state')),
3535 ('g', 'good', False, _('mark changeset good')),
3536 ('g', 'good', False, _('mark changeset good')),
3536 ('b', 'bad', False, _('mark changeset bad')),
3537 ('b', 'bad', False, _('mark changeset bad')),
3537 ('s', 'skip', False, _('skip testing changeset')),
3538 ('s', 'skip', False, _('skip testing changeset')),
3538 ('c', 'command', '', _('use command to check changeset state')),
3539 ('c', 'command', '', _('use command to check changeset state')),
3539 ('U', 'noupdate', False, _('do not update to target'))],
3540 ('U', 'noupdate', False, _('do not update to target'))],
3540 _("[-gbsr] [-U] [-c CMD] [REV]")),
3541 _("[-gbsr] [-U] [-c CMD] [REV]")),
3541 "branch":
3542 "branch":
3542 (branch,
3543 (branch,
3543 [('f', 'force', None,
3544 [('f', 'force', None,
3544 _('set branch name even if it shadows an existing branch')),
3545 _('set branch name even if it shadows an existing branch')),
3545 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3546 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3546 _('[-fC] [NAME]')),
3547 _('[-fC] [NAME]')),
3547 "branches":
3548 "branches":
3548 (branches,
3549 (branches,
3549 [('a', 'active', False,
3550 [('a', 'active', False,
3550 _('show only branches that have unmerged heads')),
3551 _('show only branches that have unmerged heads')),
3551 ('c', 'closed', False,
3552 ('c', 'closed', False,
3552 _('show normal and closed branches'))],
3553 _('show normal and closed branches'))],
3553 _('[-ac]')),
3554 _('[-ac]')),
3554 "bundle":
3555 "bundle":
3555 (bundle,
3556 (bundle,
3556 [('f', 'force', None,
3557 [('f', 'force', None,
3557 _('run even when the destination is unrelated')),
3558 _('run even when the destination is unrelated')),
3558 ('r', 'rev', [],
3559 ('r', 'rev', [],
3559 _('a changeset intended to be added to the destination')),
3560 _('a changeset intended to be added to the destination')),
3560 ('b', 'branch', [],
3561 ('b', 'branch', [],
3561 _('a specific branch you would like to bundle')),
3562 _('a specific branch you would like to bundle')),
3562 ('', 'base', [],
3563 ('', 'base', [],
3563 _('a base changeset assumed to be available at the destination')),
3564 _('a base changeset assumed to be available at the destination')),
3564 ('a', 'all', None, _('bundle all changesets in the repository')),
3565 ('a', 'all', None, _('bundle all changesets in the repository')),
3565 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3566 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3566 ] + remoteopts,
3567 ] + remoteopts,
3567 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3568 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3568 "cat":
3569 "cat":
3569 (cat,
3570 (cat,
3570 [('o', 'output', '', _('print output to file with formatted name')),
3571 [('o', 'output', '', _('print output to file with formatted name')),
3571 ('r', 'rev', '', _('print the given revision')),
3572 ('r', 'rev', '', _('print the given revision')),
3572 ('', 'decode', None, _('apply any matching decode filter')),
3573 ('', 'decode', None, _('apply any matching decode filter')),
3573 ] + walkopts,
3574 ] + walkopts,
3574 _('[OPTION]... FILE...')),
3575 _('[OPTION]... FILE...')),
3575 "^clone":
3576 "^clone":
3576 (clone,
3577 (clone,
3577 [('U', 'noupdate', None,
3578 [('U', 'noupdate', None,
3578 _('the clone will include an empty working copy (only a repository)')),
3579 _('the clone will include an empty working copy (only a repository)')),
3579 ('u', 'updaterev', '',
3580 ('u', 'updaterev', '',
3580 _('revision, tag or branch to check out')),
3581 _('revision, tag or branch to check out')),
3581 ('r', 'rev', [],
3582 ('r', 'rev', [],
3582 _('include the specified changeset')),
3583 _('include the specified changeset')),
3583 ('b', 'branch', [],
3584 ('b', 'branch', [],
3584 _('clone only the specified branch')),
3585 _('clone only the specified branch')),
3585 ('', 'pull', None, _('use pull protocol to copy metadata')),
3586 ('', 'pull', None, _('use pull protocol to copy metadata')),
3586 ('', 'uncompressed', None,
3587 ('', 'uncompressed', None,
3587 _('use uncompressed transfer (fast over LAN)')),
3588 _('use uncompressed transfer (fast over LAN)')),
3588 ] + remoteopts,
3589 ] + remoteopts,
3589 _('[OPTION]... SOURCE [DEST]')),
3590 _('[OPTION]... SOURCE [DEST]')),
3590 "^commit|ci":
3591 "^commit|ci":
3591 (commit,
3592 (commit,
3592 [('A', 'addremove', None,
3593 [('A', 'addremove', None,
3593 _('mark new/missing files as added/removed before committing')),
3594 _('mark new/missing files as added/removed before committing')),
3594 ('', 'close-branch', None,
3595 ('', 'close-branch', None,
3595 _('mark a branch as closed, hiding it from the branch list')),
3596 _('mark a branch as closed, hiding it from the branch list')),
3596 ] + walkopts + commitopts + commitopts2,
3597 ] + walkopts + commitopts + commitopts2,
3597 _('[OPTION]... [FILE]...')),
3598 _('[OPTION]... [FILE]...')),
3598 "copy|cp":
3599 "copy|cp":
3599 (copy,
3600 (copy,
3600 [('A', 'after', None, _('record a copy that has already occurred')),
3601 [('A', 'after', None, _('record a copy that has already occurred')),
3601 ('f', 'force', None,
3602 ('f', 'force', None,
3602 _('forcibly copy over an existing managed file')),
3603 _('forcibly copy over an existing managed file')),
3603 ] + walkopts + dryrunopts,
3604 ] + walkopts + dryrunopts,
3604 _('[OPTION]... [SOURCE]... DEST')),
3605 _('[OPTION]... [SOURCE]... DEST')),
3605 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3606 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3606 "debugcheckstate": (debugcheckstate, [], ''),
3607 "debugcheckstate": (debugcheckstate, [], ''),
3607 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3608 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3608 "debugcomplete":
3609 "debugcomplete":
3609 (debugcomplete,
3610 (debugcomplete,
3610 [('o', 'options', None, _('show the command options'))],
3611 [('o', 'options', None, _('show the command options'))],
3611 _('[-o] CMD')),
3612 _('[-o] CMD')),
3612 "debugdate":
3613 "debugdate":
3613 (debugdate,
3614 (debugdate,
3614 [('e', 'extended', None, _('try extended date formats'))],
3615 [('e', 'extended', None, _('try extended date formats'))],
3615 _('[-e] DATE [RANGE]')),
3616 _('[-e] DATE [RANGE]')),
3616 "debugdata": (debugdata, [], _('FILE REV')),
3617 "debugdata": (debugdata, [], _('FILE REV')),
3617 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3618 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3618 "debugindex": (debugindex, [], _('FILE')),
3619 "debugindex": (debugindex, [], _('FILE')),
3619 "debugindexdot": (debugindexdot, [], _('FILE')),
3620 "debugindexdot": (debugindexdot, [], _('FILE')),
3620 "debuginstall": (debuginstall, [], ''),
3621 "debuginstall": (debuginstall, [], ''),
3621 "debugrebuildstate":
3622 "debugrebuildstate":
3622 (debugrebuildstate,
3623 (debugrebuildstate,
3623 [('r', 'rev', '', _('revision to rebuild to'))],
3624 [('r', 'rev', '', _('revision to rebuild to'))],
3624 _('[-r REV] [REV]')),
3625 _('[-r REV] [REV]')),
3625 "debugrename":
3626 "debugrename":
3626 (debugrename,
3627 (debugrename,
3627 [('r', 'rev', '', _('revision to debug'))],
3628 [('r', 'rev', '', _('revision to debug'))],
3628 _('[-r REV] FILE')),
3629 _('[-r REV] FILE')),
3629 "debugsetparents":
3630 "debugsetparents":
3630 (debugsetparents, [], _('REV1 [REV2]')),
3631 (debugsetparents, [], _('REV1 [REV2]')),
3631 "debugstate":
3632 "debugstate":
3632 (debugstate,
3633 (debugstate,
3633 [('', 'nodates', None, _('do not display the saved mtime'))],
3634 [('', 'nodates', None, _('do not display the saved mtime'))],
3634 _('[OPTION]...')),
3635 _('[OPTION]...')),
3635 "debugsub":
3636 "debugsub":
3636 (debugsub,
3637 (debugsub,
3637 [('r', 'rev', '', _('revision to check'))],
3638 [('r', 'rev', '', _('revision to check'))],
3638 _('[-r REV] [REV]')),
3639 _('[-r REV] [REV]')),
3639 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3640 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3640 "^diff":
3641 "^diff":
3641 (diff,
3642 (diff,
3642 [('r', 'rev', [], _('revision')),
3643 [('r', 'rev', [], _('revision')),
3643 ('c', 'change', '', _('change made by revision'))
3644 ('c', 'change', '', _('change made by revision'))
3644 ] + diffopts + diffopts2 + walkopts,
3645 ] + diffopts + diffopts2 + walkopts,
3645 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
3646 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
3646 "^export":
3647 "^export":
3647 (export,
3648 (export,
3648 [('o', 'output', '', _('print output to file with formatted name')),
3649 [('o', 'output', '', _('print output to file with formatted name')),
3649 ('', 'switch-parent', None, _('diff against the second parent')),
3650 ('', 'switch-parent', None, _('diff against the second parent')),
3650 ('r', 'rev', [], _('revisions to export')),
3651 ('r', 'rev', [], _('revisions to export')),
3651 ] + diffopts,
3652 ] + diffopts,
3652 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3653 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3653 "^forget":
3654 "^forget":
3654 (forget,
3655 (forget,
3655 [] + walkopts,
3656 [] + walkopts,
3656 _('[OPTION]... FILE...')),
3657 _('[OPTION]... FILE...')),
3657 "grep":
3658 "grep":
3658 (grep,
3659 (grep,
3659 [('0', 'print0', None, _('end fields with NUL')),
3660 [('0', 'print0', None, _('end fields with NUL')),
3660 ('', 'all', None, _('print all revisions that match')),
3661 ('', 'all', None, _('print all revisions that match')),
3661 ('f', 'follow', None,
3662 ('f', 'follow', None,
3662 _('follow changeset history,'
3663 _('follow changeset history,'
3663 ' or file history across copies and renames')),
3664 ' or file history across copies and renames')),
3664 ('i', 'ignore-case', None, _('ignore case when matching')),
3665 ('i', 'ignore-case', None, _('ignore case when matching')),
3665 ('l', 'files-with-matches', None,
3666 ('l', 'files-with-matches', None,
3666 _('print only filenames and revisions that match')),
3667 _('print only filenames and revisions that match')),
3667 ('n', 'line-number', None, _('print matching line numbers')),
3668 ('n', 'line-number', None, _('print matching line numbers')),
3668 ('r', 'rev', [], _('search in given revision range')),
3669 ('r', 'rev', [], _('search in given revision range')),
3669 ('u', 'user', None, _('list the author (long with -v)')),
3670 ('u', 'user', None, _('list the author (long with -v)')),
3670 ('d', 'date', None, _('list the date (short with -q)')),
3671 ('d', 'date', None, _('list the date (short with -q)')),
3671 ] + walkopts,
3672 ] + walkopts,
3672 _('[OPTION]... PATTERN [FILE]...')),
3673 _('[OPTION]... PATTERN [FILE]...')),
3673 "heads":
3674 "heads":
3674 (heads,
3675 (heads,
3675 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3676 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3676 ('t', 'topo', False, _('show topological heads only')),
3677 ('t', 'topo', False, _('show topological heads only')),
3677 ('a', 'active', False,
3678 ('a', 'active', False,
3678 _('show active branchheads only [DEPRECATED]')),
3679 _('show active branchheads only [DEPRECATED]')),
3679 ('c', 'closed', False,
3680 ('c', 'closed', False,
3680 _('show normal and closed branch heads')),
3681 _('show normal and closed branch heads')),
3681 ] + templateopts,
3682 ] + templateopts,
3682 _('[-ac] [-r STARTREV] [REV]...')),
3683 _('[-ac] [-r STARTREV] [REV]...')),
3683 "help": (help_, [], _('[TOPIC]')),
3684 "help": (help_, [], _('[TOPIC]')),
3684 "identify|id":
3685 "identify|id":
3685 (identify,
3686 (identify,
3686 [('r', 'rev', '', _('identify the specified revision')),
3687 [('r', 'rev', '', _('identify the specified revision')),
3687 ('n', 'num', None, _('show local revision number')),
3688 ('n', 'num', None, _('show local revision number')),
3688 ('i', 'id', None, _('show global revision id')),
3689 ('i', 'id', None, _('show global revision id')),
3689 ('b', 'branch', None, _('show branch')),
3690 ('b', 'branch', None, _('show branch')),
3690 ('t', 'tags', None, _('show tags'))],
3691 ('t', 'tags', None, _('show tags'))],
3691 _('[-nibt] [-r REV] [SOURCE]')),
3692 _('[-nibt] [-r REV] [SOURCE]')),
3692 "import|patch":
3693 "import|patch":
3693 (import_,
3694 (import_,
3694 [('p', 'strip', 1,
3695 [('p', 'strip', 1,
3695 _('directory strip option for patch. This has the same '
3696 _('directory strip option for patch. This has the same '
3696 'meaning as the corresponding patch option')),
3697 'meaning as the corresponding patch option')),
3697 ('b', 'base', '', _('base path')),
3698 ('b', 'base', '', _('base path')),
3698 ('f', 'force', None,
3699 ('f', 'force', None,
3699 _('skip check for outstanding uncommitted changes')),
3700 _('skip check for outstanding uncommitted changes')),
3700 ('', 'no-commit', None,
3701 ('', 'no-commit', None,
3701 _("don't commit, just update the working directory")),
3702 _("don't commit, just update the working directory")),
3702 ('', 'exact', None,
3703 ('', 'exact', None,
3703 _('apply patch to the nodes from which it was generated')),
3704 _('apply patch to the nodes from which it was generated')),
3704 ('', 'import-branch', None,
3705 ('', 'import-branch', None,
3705 _('use any branch information in patch (implied by --exact)'))] +
3706 _('use any branch information in patch (implied by --exact)'))] +
3706 commitopts + commitopts2 + similarityopts,
3707 commitopts + commitopts2 + similarityopts,
3707 _('[OPTION]... PATCH...')),
3708 _('[OPTION]... PATCH...')),
3708 "incoming|in":
3709 "incoming|in":
3709 (incoming,
3710 (incoming,
3710 [('f', 'force', None,
3711 [('f', 'force', None,
3711 _('run even if remote repository is unrelated')),
3712 _('run even if remote repository is unrelated')),
3712 ('n', 'newest-first', None, _('show newest record first')),
3713 ('n', 'newest-first', None, _('show newest record first')),
3713 ('', 'bundle', '', _('file to store the bundles into')),
3714 ('', 'bundle', '', _('file to store the bundles into')),
3714 ('r', 'rev', [],
3715 ('r', 'rev', [],
3715 _('a remote changeset intended to be added')),
3716 _('a remote changeset intended to be added')),
3716 ('b', 'branch', [],
3717 ('b', 'branch', [],
3717 _('a specific branch you would like to pull')),
3718 _('a specific branch you would like to pull')),
3718 ] + logopts + remoteopts,
3719 ] + logopts + remoteopts,
3719 _('[-p] [-n] [-M] [-f] [-r REV]...'
3720 _('[-p] [-n] [-M] [-f] [-r REV]...'
3720 ' [--bundle FILENAME] [SOURCE]')),
3721 ' [--bundle FILENAME] [SOURCE]')),
3721 "^init":
3722 "^init":
3722 (init,
3723 (init,
3723 remoteopts,
3724 remoteopts,
3724 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3725 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3725 "locate":
3726 "locate":
3726 (locate,
3727 (locate,
3727 [('r', 'rev', '', _('search the repository as it is in REV')),
3728 [('r', 'rev', '', _('search the repository as it is in REV')),
3728 ('0', 'print0', None,
3729 ('0', 'print0', None,
3729 _('end filenames with NUL, for use with xargs')),
3730 _('end filenames with NUL, for use with xargs')),
3730 ('f', 'fullpath', None,
3731 ('f', 'fullpath', None,
3731 _('print complete paths from the filesystem root')),
3732 _('print complete paths from the filesystem root')),
3732 ] + walkopts,
3733 ] + walkopts,
3733 _('[OPTION]... [PATTERN]...')),
3734 _('[OPTION]... [PATTERN]...')),
3734 "^log|history":
3735 "^log|history":
3735 (log,
3736 (log,
3736 [('f', 'follow', None,
3737 [('f', 'follow', None,
3737 _('follow changeset history,'
3738 _('follow changeset history,'
3738 ' or file history across copies and renames')),
3739 ' or file history across copies and renames')),
3739 ('', 'follow-first', None,
3740 ('', 'follow-first', None,
3740 _('only follow the first parent of merge changesets')),
3741 _('only follow the first parent of merge changesets')),
3741 ('d', 'date', '', _('show revisions matching date spec')),
3742 ('d', 'date', '', _('show revisions matching date spec')),
3742 ('C', 'copies', None, _('show copied files')),
3743 ('C', 'copies', None, _('show copied files')),
3743 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3744 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3744 ('r', 'rev', [], _('show the specified revision or range')),
3745 ('r', 'rev', [], _('show the specified revision or range')),
3745 ('', 'removed', None, _('include revisions where files were removed')),
3746 ('', 'removed', None, _('include revisions where files were removed')),
3746 ('m', 'only-merges', None, _('show only merges')),
3747 ('m', 'only-merges', None, _('show only merges')),
3747 ('u', 'user', [], _('revisions committed by user')),
3748 ('u', 'user', [], _('revisions committed by user')),
3748 ('', 'only-branch', [],
3749 ('', 'only-branch', [],
3749 _('show only changesets within the given named branch (DEPRECATED)')),
3750 _('show only changesets within the given named branch (DEPRECATED)')),
3750 ('b', 'branch', [],
3751 ('b', 'branch', [],
3751 _('show changesets within the given named branch')),
3752 _('show changesets within the given named branch')),
3752 ('P', 'prune', [],
3753 ('P', 'prune', [],
3753 _('do not display revision or any of its ancestors')),
3754 _('do not display revision or any of its ancestors')),
3754 ] + logopts + walkopts,
3755 ] + logopts + walkopts,
3755 _('[OPTION]... [FILE]')),
3756 _('[OPTION]... [FILE]')),
3756 "manifest":
3757 "manifest":
3757 (manifest,
3758 (manifest,
3758 [('r', 'rev', '', _('revision to display'))],
3759 [('r', 'rev', '', _('revision to display'))],
3759 _('[-r REV]')),
3760 _('[-r REV]')),
3760 "^merge":
3761 "^merge":
3761 (merge,
3762 (merge,
3762 [('f', 'force', None, _('force a merge with outstanding changes')),
3763 [('f', 'force', None, _('force a merge with outstanding changes')),
3763 ('r', 'rev', '', _('revision to merge')),
3764 ('r', 'rev', '', _('revision to merge')),
3764 ('P', 'preview', None,
3765 ('P', 'preview', None,
3765 _('review revisions to merge (no merge is performed)'))],
3766 _('review revisions to merge (no merge is performed)'))],
3766 _('[-P] [-f] [[-r] REV]')),
3767 _('[-P] [-f] [[-r] REV]')),
3767 "outgoing|out":
3768 "outgoing|out":
3768 (outgoing,
3769 (outgoing,
3769 [('f', 'force', None,
3770 [('f', 'force', None,
3770 _('run even when the destination is unrelated')),
3771 _('run even when the destination is unrelated')),
3771 ('r', 'rev', [],
3772 ('r', 'rev', [],
3772 _('a changeset intended to be included in the destination')),
3773 _('a changeset intended to be included in the destination')),
3773 ('n', 'newest-first', None, _('show newest record first')),
3774 ('n', 'newest-first', None, _('show newest record first')),
3774 ('b', 'branch', [],
3775 ('b', 'branch', [],
3775 _('a specific branch you would like to push')),
3776 _('a specific branch you would like to push')),
3776 ] + logopts + remoteopts,
3777 ] + logopts + remoteopts,
3777 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3778 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3778 "parents":
3779 "parents":
3779 (parents,
3780 (parents,
3780 [('r', 'rev', '', _('show parents of the specified revision')),
3781 [('r', 'rev', '', _('show parents of the specified revision')),
3781 ] + templateopts,
3782 ] + templateopts,
3782 _('[-r REV] [FILE]')),
3783 _('[-r REV] [FILE]')),
3783 "paths": (paths, [], _('[NAME]')),
3784 "paths": (paths, [], _('[NAME]')),
3784 "^pull":
3785 "^pull":
3785 (pull,
3786 (pull,
3786 [('u', 'update', None,
3787 [('u', 'update', None,
3787 _('update to new branch head if changesets were pulled')),
3788 _('update to new branch head if changesets were pulled')),
3788 ('f', 'force', None,
3789 ('f', 'force', None,
3789 _('run even when remote repository is unrelated')),
3790 _('run even when remote repository is unrelated')),
3790 ('r', 'rev', [],
3791 ('r', 'rev', [],
3791 _('a remote changeset intended to be added')),
3792 _('a remote changeset intended to be added')),
3792 ('b', 'branch', [],
3793 ('b', 'branch', [],
3793 _('a specific branch you would like to pull')),
3794 _('a specific branch you would like to pull')),
3794 ] + remoteopts,
3795 ] + remoteopts,
3795 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3796 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3796 "^push":
3797 "^push":
3797 (push,
3798 (push,
3798 [('f', 'force', None, _('force push')),
3799 [('f', 'force', None, _('force push')),
3799 ('r', 'rev', [],
3800 ('r', 'rev', [],
3800 _('a changeset intended to be included in the destination')),
3801 _('a changeset intended to be included in the destination')),
3801 ('b', 'branch', [],
3802 ('b', 'branch', [],
3802 _('a specific branch you would like to push')),
3803 _('a specific branch you would like to push')),
3803 ] + remoteopts,
3804 ] + remoteopts,
3804 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3805 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3805 "recover": (recover, []),
3806 "recover": (recover, []),
3806 "^remove|rm":
3807 "^remove|rm":
3807 (remove,
3808 (remove,
3808 [('A', 'after', None, _('record delete for missing files')),
3809 [('A', 'after', None, _('record delete for missing files')),
3809 ('f', 'force', None,
3810 ('f', 'force', None,
3810 _('remove (and delete) file even if added or modified')),
3811 _('remove (and delete) file even if added or modified')),
3811 ] + walkopts,
3812 ] + walkopts,
3812 _('[OPTION]... FILE...')),
3813 _('[OPTION]... FILE...')),
3813 "rename|mv":
3814 "rename|mv":
3814 (rename,
3815 (rename,
3815 [('A', 'after', None, _('record a rename that has already occurred')),
3816 [('A', 'after', None, _('record a rename that has already occurred')),
3816 ('f', 'force', None,
3817 ('f', 'force', None,
3817 _('forcibly copy over an existing managed file')),
3818 _('forcibly copy over an existing managed file')),
3818 ] + walkopts + dryrunopts,
3819 ] + walkopts + dryrunopts,
3819 _('[OPTION]... SOURCE... DEST')),
3820 _('[OPTION]... SOURCE... DEST')),
3820 "resolve":
3821 "resolve":
3821 (resolve,
3822 (resolve,
3822 [('a', 'all', None, _('select all unresolved files')),
3823 [('a', 'all', None, _('select all unresolved files')),
3823 ('l', 'list', None, _('list state of files needing merge')),
3824 ('l', 'list', None, _('list state of files needing merge')),
3824 ('m', 'mark', None, _('mark files as resolved')),
3825 ('m', 'mark', None, _('mark files as resolved')),
3825 ('u', 'unmark', None, _('unmark files as resolved')),
3826 ('u', 'unmark', None, _('unmark files as resolved')),
3826 ('n', 'no-status', None, _('hide status prefix'))]
3827 ('n', 'no-status', None, _('hide status prefix'))]
3827 + walkopts,
3828 + walkopts,
3828 _('[OPTION]... [FILE]...')),
3829 _('[OPTION]... [FILE]...')),
3829 "revert":
3830 "revert":
3830 (revert,
3831 (revert,
3831 [('a', 'all', None, _('revert all changes when no arguments given')),
3832 [('a', 'all', None, _('revert all changes when no arguments given')),
3832 ('d', 'date', '', _('tipmost revision matching date')),
3833 ('d', 'date', '', _('tipmost revision matching date')),
3833 ('r', 'rev', '', _('revert to the specified revision')),
3834 ('r', 'rev', '', _('revert to the specified revision')),
3834 ('', 'no-backup', None, _('do not save backup copies of files')),
3835 ('', 'no-backup', None, _('do not save backup copies of files')),
3835 ] + walkopts + dryrunopts,
3836 ] + walkopts + dryrunopts,
3836 _('[OPTION]... [-r REV] [NAME]...')),
3837 _('[OPTION]... [-r REV] [NAME]...')),
3837 "rollback": (rollback, dryrunopts),
3838 "rollback": (rollback, dryrunopts),
3838 "root": (root, []),
3839 "root": (root, []),
3839 "^serve":
3840 "^serve":
3840 (serve,
3841 (serve,
3841 [('A', 'accesslog', '', _('name of access log file to write to')),
3842 [('A', 'accesslog', '', _('name of access log file to write to')),
3842 ('d', 'daemon', None, _('run server in background')),
3843 ('d', 'daemon', None, _('run server in background')),
3843 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3844 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3844 ('E', 'errorlog', '', _('name of error log file to write to')),
3845 ('E', 'errorlog', '', _('name of error log file to write to')),
3845 # use string type, then we can check if something was passed
3846 # use string type, then we can check if something was passed
3846 ('p', 'port', '', _('port to listen on (default: 8000)')),
3847 ('p', 'port', '', _('port to listen on (default: 8000)')),
3847 ('a', 'address', '',
3848 ('a', 'address', '',
3848 _('address to listen on (default: all interfaces)')),
3849 _('address to listen on (default: all interfaces)')),
3849 ('', 'prefix', '',
3850 ('', 'prefix', '',
3850 _('prefix path to serve from (default: server root)')),
3851 _('prefix path to serve from (default: server root)')),
3851 ('n', 'name', '',
3852 ('n', 'name', '',
3852 _('name to show in web pages (default: working directory)')),
3853 _('name to show in web pages (default: working directory)')),
3853 ('', 'web-conf', '', _('name of the hgweb config file'
3854 ('', 'web-conf', '', _('name of the hgweb config file'
3854 ' (serve more than one repository)')),
3855 ' (serve more than one repository)')),
3855 ('', 'webdir-conf', '', _('name of the hgweb config file'
3856 ('', 'webdir-conf', '', _('name of the hgweb config file'
3856 ' (DEPRECATED)')),
3857 ' (DEPRECATED)')),
3857 ('', 'pid-file', '', _('name of file to write process ID to')),
3858 ('', 'pid-file', '', _('name of file to write process ID to')),
3858 ('', 'stdio', None, _('for remote clients')),
3859 ('', 'stdio', None, _('for remote clients')),
3859 ('t', 'templates', '', _('web templates to use')),
3860 ('t', 'templates', '', _('web templates to use')),
3860 ('', 'style', '', _('template style to use')),
3861 ('', 'style', '', _('template style to use')),
3861 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3862 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3862 ('', 'certificate', '', _('SSL certificate file'))],
3863 ('', 'certificate', '', _('SSL certificate file'))],
3863 _('[OPTION]...')),
3864 _('[OPTION]...')),
3864 "showconfig|debugconfig":
3865 "showconfig|debugconfig":
3865 (showconfig,
3866 (showconfig,
3866 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3867 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3867 _('[-u] [NAME]...')),
3868 _('[-u] [NAME]...')),
3868 "^summary|sum":
3869 "^summary|sum":
3869 (summary,
3870 (summary,
3870 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3871 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3871 "^status|st":
3872 "^status|st":
3872 (status,
3873 (status,
3873 [('A', 'all', None, _('show status of all files')),
3874 [('A', 'all', None, _('show status of all files')),
3874 ('m', 'modified', None, _('show only modified files')),
3875 ('m', 'modified', None, _('show only modified files')),
3875 ('a', 'added', None, _('show only added files')),
3876 ('a', 'added', None, _('show only added files')),
3876 ('r', 'removed', None, _('show only removed files')),
3877 ('r', 'removed', None, _('show only removed files')),
3877 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3878 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3878 ('c', 'clean', None, _('show only files without changes')),
3879 ('c', 'clean', None, _('show only files without changes')),
3879 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3880 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3880 ('i', 'ignored', None, _('show only ignored files')),
3881 ('i', 'ignored', None, _('show only ignored files')),
3881 ('n', 'no-status', None, _('hide status prefix')),
3882 ('n', 'no-status', None, _('hide status prefix')),
3882 ('C', 'copies', None, _('show source of copied files')),
3883 ('C', 'copies', None, _('show source of copied files')),
3883 ('0', 'print0', None,
3884 ('0', 'print0', None,
3884 _('end filenames with NUL, for use with xargs')),
3885 _('end filenames with NUL, for use with xargs')),
3885 ('', 'rev', [], _('show difference from revision')),
3886 ('', 'rev', [], _('show difference from revision')),
3886 ('', 'change', '', _('list the changed files of a revision')),
3887 ('', 'change', '', _('list the changed files of a revision')),
3887 ] + walkopts,
3888 ] + walkopts,
3888 _('[OPTION]... [FILE]...')),
3889 _('[OPTION]... [FILE]...')),
3889 "tag":
3890 "tag":
3890 (tag,
3891 (tag,
3891 [('f', 'force', None, _('replace existing tag')),
3892 [('f', 'force', None, _('replace existing tag')),
3892 ('l', 'local', None, _('make the tag local')),
3893 ('l', 'local', None, _('make the tag local')),
3893 ('r', 'rev', '', _('revision to tag')),
3894 ('r', 'rev', '', _('revision to tag')),
3894 ('', 'remove', None, _('remove a tag')),
3895 ('', 'remove', None, _('remove a tag')),
3895 # -l/--local is already there, commitopts cannot be used
3896 # -l/--local is already there, commitopts cannot be used
3896 ('m', 'message', '', _('use <text> as commit message')),
3897 ('m', 'message', '', _('use <text> as commit message')),
3897 ] + commitopts2,
3898 ] + commitopts2,
3898 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3899 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3899 "tags": (tags, [], ''),
3900 "tags": (tags, [], ''),
3900 "tip":
3901 "tip":
3901 (tip,
3902 (tip,
3902 [('p', 'patch', None, _('show patch')),
3903 [('p', 'patch', None, _('show patch')),
3903 ('g', 'git', None, _('use git extended diff format')),
3904 ('g', 'git', None, _('use git extended diff format')),
3904 ] + templateopts,
3905 ] + templateopts,
3905 _('[-p] [-g]')),
3906 _('[-p] [-g]')),
3906 "unbundle":
3907 "unbundle":
3907 (unbundle,
3908 (unbundle,
3908 [('u', 'update', None,
3909 [('u', 'update', None,
3909 _('update to new branch head if changesets were unbundled'))],
3910 _('update to new branch head if changesets were unbundled'))],
3910 _('[-u] FILE...')),
3911 _('[-u] FILE...')),
3911 "^update|up|checkout|co":
3912 "^update|up|checkout|co":
3912 (update,
3913 (update,
3913 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3914 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3914 ('c', 'check', None, _('check for uncommitted changes')),
3915 ('c', 'check', None, _('check for uncommitted changes')),
3915 ('d', 'date', '', _('tipmost revision matching date')),
3916 ('d', 'date', '', _('tipmost revision matching date')),
3916 ('r', 'rev', '', _('revision'))],
3917 ('r', 'rev', '', _('revision'))],
3917 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3918 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3918 "verify": (verify, []),
3919 "verify": (verify, []),
3919 "version": (version_, []),
3920 "version": (version_, []),
3920 }
3921 }
3921
3922
3922 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3923 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3923 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3924 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3924 optionalrepo = ("identify paths serve showconfig debugancestor")
3925 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,134 +1,144 b''
1 # statichttprepo.py - simple http repository class for mercurial
1 # statichttprepo.py - simple http repository class for mercurial
2 #
2 #
3 # This provides read-only repo access to repositories exported via static http
3 # This provides read-only repo access to repositories exported via static http
4 #
4 #
5 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from i18n import _
10 from i18n import _
11 import changelog, byterange, url, error
11 import changelog, byterange, url, error
12 import localrepo, manifest, util, store
12 import localrepo, manifest, util, store
13 import urllib, urllib2, errno
13 import urllib, urllib2, errno
14
14
15 class httprangereader(object):
15 class httprangereader(object):
16 def __init__(self, url, opener):
16 def __init__(self, url, opener):
17 # we assume opener has HTTPRangeHandler
17 # we assume opener has HTTPRangeHandler
18 self.url = url
18 self.url = url
19 self.pos = 0
19 self.pos = 0
20 self.opener = opener
20 self.opener = opener
21 self.name = url
21 def seek(self, pos):
22 def seek(self, pos):
22 self.pos = pos
23 self.pos = pos
23 def read(self, bytes=None):
24 def read(self, bytes=None):
24 req = urllib2.Request(self.url)
25 req = urllib2.Request(self.url)
25 end = ''
26 end = ''
26 if bytes:
27 if bytes:
27 end = self.pos + bytes - 1
28 end = self.pos + bytes - 1
28 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
29 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
29
30
30 try:
31 try:
31 f = self.opener.open(req)
32 f = self.opener.open(req)
32 data = f.read()
33 data = f.read()
33 if hasattr(f, 'getcode'):
34 if hasattr(f, 'getcode'):
34 # python 2.6+
35 # python 2.6+
35 code = f.getcode()
36 code = f.getcode()
36 elif hasattr(f, 'code'):
37 elif hasattr(f, 'code'):
37 # undocumented attribute, seems to be set in 2.4 and 2.5
38 # undocumented attribute, seems to be set in 2.4 and 2.5
38 code = f.code
39 code = f.code
39 else:
40 else:
40 # Don't know how to check, hope for the best.
41 # Don't know how to check, hope for the best.
41 code = 206
42 code = 206
42 except urllib2.HTTPError, inst:
43 except urllib2.HTTPError, inst:
43 num = inst.code == 404 and errno.ENOENT or None
44 num = inst.code == 404 and errno.ENOENT or None
44 raise IOError(num, inst)
45 raise IOError(num, inst)
45 except urllib2.URLError, inst:
46 except urllib2.URLError, inst:
46 raise IOError(None, inst.reason[1])
47 raise IOError(None, inst.reason[1])
47
48
48 if code == 200:
49 if code == 200:
49 # HTTPRangeHandler does nothing if remote does not support
50 # HTTPRangeHandler does nothing if remote does not support
50 # Range headers and returns the full entity. Let's slice it.
51 # Range headers and returns the full entity. Let's slice it.
51 if bytes:
52 if bytes:
52 data = data[self.pos:self.pos + bytes]
53 data = data[self.pos:self.pos + bytes]
53 else:
54 else:
54 data = data[self.pos:]
55 data = data[self.pos:]
55 elif bytes:
56 elif bytes:
56 data = data[:bytes]
57 data = data[:bytes]
57 self.pos += len(data)
58 self.pos += len(data)
58 return data
59 return data
60 def __iter__(self):
61 return iter(self.read().splitlines(1))
62 def close(self):
63 pass
59
64
60 def build_opener(ui, authinfo):
65 def build_opener(ui, authinfo):
61 # urllib cannot handle URLs with embedded user or passwd
66 # urllib cannot handle URLs with embedded user or passwd
62 urlopener = url.opener(ui, authinfo)
67 urlopener = url.opener(ui, authinfo)
63 urlopener.add_handler(byterange.HTTPRangeHandler())
68 urlopener.add_handler(byterange.HTTPRangeHandler())
64
69
65 def opener(base):
70 def opener(base):
66 """return a function that opens files over http"""
71 """return a function that opens files over http"""
67 p = base
72 p = base
68 def o(path, mode="r"):
73 def o(path, mode="r", atomictemp=None):
74 if 'a' in mode or 'w' in mode:
75 raise IOError('Permission denied')
69 f = "/".join((p, urllib.quote(path)))
76 f = "/".join((p, urllib.quote(path)))
70 return httprangereader(f, urlopener)
77 return httprangereader(f, urlopener)
71 return o
78 return o
72
79
73 return opener
80 return opener
74
81
75 class statichttprepository(localrepo.localrepository):
82 class statichttprepository(localrepo.localrepository):
76 def __init__(self, ui, path):
83 def __init__(self, ui, path):
77 self._url = path
84 self._url = path
78 self.ui = ui
85 self.ui = ui
79
86
87 self.root = path
80 self.path, authinfo = url.getauthinfo(path.rstrip('/') + "/.hg")
88 self.path, authinfo = url.getauthinfo(path.rstrip('/') + "/.hg")
81
89
82 opener = build_opener(ui, authinfo)
90 opener = build_opener(ui, authinfo)
83 self.opener = opener(self.path)
91 self.opener = opener(self.path)
84
92
85 # find requirements
93 # find requirements
86 try:
94 try:
87 requirements = self.opener("requires").read().splitlines()
95 requirements = self.opener("requires").read().splitlines()
88 except IOError, inst:
96 except IOError, inst:
89 if inst.errno != errno.ENOENT:
97 if inst.errno != errno.ENOENT:
90 raise
98 raise
91 # check if it is a non-empty old-style repository
99 # check if it is a non-empty old-style repository
92 try:
100 try:
93 self.opener("00changelog.i").read(1)
101 self.opener("00changelog.i").read(1)
94 except IOError, inst:
102 except IOError, inst:
95 if inst.errno != errno.ENOENT:
103 if inst.errno != errno.ENOENT:
96 raise
104 raise
97 # we do not care about empty old-style repositories here
105 # we do not care about empty old-style repositories here
98 msg = _("'%s' does not appear to be an hg repository") % path
106 msg = _("'%s' does not appear to be an hg repository") % path
99 raise error.RepoError(msg)
107 raise error.RepoError(msg)
100 requirements = []
108 requirements = []
101
109
102 # check them
110 # check them
103 for r in requirements:
111 for r in requirements:
104 if r not in self.supported:
112 if r not in self.supported:
105 raise error.RepoError(_("requirement '%s' not supported") % r)
113 raise error.RepoError(_("requirement '%s' not supported") % r)
106
114
107 # setup store
115 # setup store
108 def pjoin(a, b):
116 def pjoin(a, b):
109 return a + '/' + b
117 return a + '/' + b
110 self.store = store.store(requirements, self.path, opener, pjoin)
118 self.store = store.store(requirements, self.path, opener, pjoin)
111 self.spath = self.store.path
119 self.spath = self.store.path
112 self.sopener = self.store.opener
120 self.sopener = self.store.opener
113 self.sjoin = self.store.join
121 self.sjoin = self.store.join
114
122
115 self.manifest = manifest.manifest(self.sopener)
123 self.manifest = manifest.manifest(self.sopener)
116 self.changelog = changelog.changelog(self.sopener)
124 self.changelog = changelog.changelog(self.sopener)
117 self._tags = None
125 self._tags = None
118 self.nodetagscache = None
126 self.nodetagscache = None
127 self._branchcache = None
128 self._branchcachetip = None
119 self.encodepats = None
129 self.encodepats = None
120 self.decodepats = None
130 self.decodepats = None
121
131
122 def url(self):
132 def url(self):
123 return self._url
133 return self._url
124
134
125 def local(self):
135 def local(self):
126 return False
136 return False
127
137
128 def lock(self, wait=True):
138 def lock(self, wait=True):
129 raise util.Abort(_('cannot lock static-http repository'))
139 raise util.Abort(_('cannot lock static-http repository'))
130
140
131 def instance(ui, path, create):
141 def instance(ui, path, create):
132 if create:
142 if create:
133 raise util.Abort(_('cannot create new static-http repository'))
143 raise util.Abort(_('cannot create new static-http repository'))
134 return statichttprepository(ui, path[7:])
144 return statichttprepository(ui, path[7:])
@@ -1,374 +1,375 b''
1 # subrepo.py - sub-repository handling for Mercurial
1 # subrepo.py - sub-repository handling for Mercurial
2 #
2 #
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009-2010 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 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import errno, os, re, xml.dom.minidom, shutil
8 import errno, os, re, xml.dom.minidom, shutil
9 from i18n import _
9 from i18n import _
10 import config, util, node, error
10 import config, util, node, error
11 hg = None
11 hg = None
12
12
13 nullstate = ('', '', 'empty')
13 nullstate = ('', '', 'empty')
14
14
15 def state(ctx):
15 def state(ctx):
16 p = config.config()
16 p = config.config()
17 def read(f, sections=None, remap=None):
17 def read(f, sections=None, remap=None):
18 if f in ctx:
18 if f in ctx:
19 p.parse(f, ctx[f].data(), sections, remap, read)
19 p.parse(f, ctx[f].data(), sections, remap, read)
20 else:
20 else:
21 raise util.Abort(_("subrepo spec file %s not found") % f)
21 raise util.Abort(_("subrepo spec file %s not found") % f)
22
22
23 if '.hgsub' in ctx:
23 if '.hgsub' in ctx:
24 read('.hgsub')
24 read('.hgsub')
25
25
26 rev = {}
26 rev = {}
27 if '.hgsubstate' in ctx:
27 if '.hgsubstate' in ctx:
28 try:
28 try:
29 for l in ctx['.hgsubstate'].data().splitlines():
29 for l in ctx['.hgsubstate'].data().splitlines():
30 revision, path = l.split(" ", 1)
30 revision, path = l.split(" ", 1)
31 rev[path] = revision
31 rev[path] = revision
32 except IOError, err:
32 except IOError, err:
33 if err.errno != errno.ENOENT:
33 if err.errno != errno.ENOENT:
34 raise
34 raise
35
35
36 state = {}
36 state = {}
37 for path, src in p[''].items():
37 for path, src in p[''].items():
38 kind = 'hg'
38 kind = 'hg'
39 if src.startswith('['):
39 if src.startswith('['):
40 if ']' not in src:
40 if ']' not in src:
41 raise util.Abort(_('missing ] in subrepo source'))
41 raise util.Abort(_('missing ] in subrepo source'))
42 kind, src = src.split(']', 1)
42 kind, src = src.split(']', 1)
43 kind = kind[1:]
43 kind = kind[1:]
44 state[path] = (src.strip(), rev.get(path, ''), kind)
44 state[path] = (src.strip(), rev.get(path, ''), kind)
45
45
46 return state
46 return state
47
47
48 def writestate(repo, state):
48 def writestate(repo, state):
49 repo.wwrite('.hgsubstate',
49 repo.wwrite('.hgsubstate',
50 ''.join(['%s %s\n' % (state[s][1], s)
50 ''.join(['%s %s\n' % (state[s][1], s)
51 for s in sorted(state)]), '')
51 for s in sorted(state)]), '')
52
52
53 def submerge(repo, wctx, mctx, actx):
53 def submerge(repo, wctx, mctx, actx):
54 # working context, merging context, ancestor context
54 # working context, merging context, ancestor context
55 if mctx == actx: # backwards?
55 if mctx == actx: # backwards?
56 actx = wctx.p1()
56 actx = wctx.p1()
57 s1 = wctx.substate
57 s1 = wctx.substate
58 s2 = mctx.substate
58 s2 = mctx.substate
59 sa = actx.substate
59 sa = actx.substate
60 sm = {}
60 sm = {}
61
61
62 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
62 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
63
63
64 def debug(s, msg, r=""):
64 def debug(s, msg, r=""):
65 if r:
65 if r:
66 r = "%s:%s:%s" % r
66 r = "%s:%s:%s" % r
67 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
67 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
68
68
69 for s, l in s1.items():
69 for s, l in s1.items():
70 if wctx != actx and wctx.sub(s).dirty():
70 if wctx != actx and wctx.sub(s).dirty():
71 l = (l[0], l[1] + "+")
71 l = (l[0], l[1] + "+")
72 a = sa.get(s, nullstate)
72 a = sa.get(s, nullstate)
73 if s in s2:
73 if s in s2:
74 r = s2[s]
74 r = s2[s]
75 if l == r or r == a: # no change or local is newer
75 if l == r or r == a: # no change or local is newer
76 sm[s] = l
76 sm[s] = l
77 continue
77 continue
78 elif l == a: # other side changed
78 elif l == a: # other side changed
79 debug(s, "other changed, get", r)
79 debug(s, "other changed, get", r)
80 wctx.sub(s).get(r)
80 wctx.sub(s).get(r)
81 sm[s] = r
81 sm[s] = r
82 elif l[0] != r[0]: # sources differ
82 elif l[0] != r[0]: # sources differ
83 if repo.ui.promptchoice(
83 if repo.ui.promptchoice(
84 _(' subrepository sources for %s differ\n'
84 _(' subrepository sources for %s differ\n'
85 'use (l)ocal source (%s) or (r)emote source (%s)?')
85 'use (l)ocal source (%s) or (r)emote source (%s)?')
86 % (s, l[0], r[0]),
86 % (s, l[0], r[0]),
87 (_('&Local'), _('&Remote')), 0):
87 (_('&Local'), _('&Remote')), 0):
88 debug(s, "prompt changed, get", r)
88 debug(s, "prompt changed, get", r)
89 wctx.sub(s).get(r)
89 wctx.sub(s).get(r)
90 sm[s] = r
90 sm[s] = r
91 elif l[1] == a[1]: # local side is unchanged
91 elif l[1] == a[1]: # local side is unchanged
92 debug(s, "other side changed, get", r)
92 debug(s, "other side changed, get", r)
93 wctx.sub(s).get(r)
93 wctx.sub(s).get(r)
94 sm[s] = r
94 sm[s] = r
95 else:
95 else:
96 debug(s, "both sides changed, merge with", r)
96 debug(s, "both sides changed, merge with", r)
97 wctx.sub(s).merge(r)
97 wctx.sub(s).merge(r)
98 sm[s] = l
98 sm[s] = l
99 elif l == a: # remote removed, local unchanged
99 elif l == a: # remote removed, local unchanged
100 debug(s, "remote removed, remove")
100 debug(s, "remote removed, remove")
101 wctx.sub(s).remove()
101 wctx.sub(s).remove()
102 else:
102 else:
103 if repo.ui.promptchoice(
103 if repo.ui.promptchoice(
104 _(' local changed subrepository %s which remote removed\n'
104 _(' local changed subrepository %s which remote removed\n'
105 'use (c)hanged version or (d)elete?') % s,
105 'use (c)hanged version or (d)elete?') % s,
106 (_('&Changed'), _('&Delete')), 0):
106 (_('&Changed'), _('&Delete')), 0):
107 debug(s, "prompt remove")
107 debug(s, "prompt remove")
108 wctx.sub(s).remove()
108 wctx.sub(s).remove()
109
109
110 for s, r in s2.items():
110 for s, r in s2.items():
111 if s in s1:
111 if s in s1:
112 continue
112 continue
113 elif s not in sa:
113 elif s not in sa:
114 debug(s, "remote added, get", r)
114 debug(s, "remote added, get", r)
115 mctx.sub(s).get(r)
115 mctx.sub(s).get(r)
116 sm[s] = r
116 sm[s] = r
117 elif r != sa[s]:
117 elif r != sa[s]:
118 if repo.ui.promptchoice(
118 if repo.ui.promptchoice(
119 _(' remote changed subrepository %s which local removed\n'
119 _(' remote changed subrepository %s which local removed\n'
120 'use (c)hanged version or (d)elete?') % s,
120 'use (c)hanged version or (d)elete?') % s,
121 (_('&Changed'), _('&Delete')), 0) == 0:
121 (_('&Changed'), _('&Delete')), 0) == 0:
122 debug(s, "prompt recreate", r)
122 debug(s, "prompt recreate", r)
123 wctx.sub(s).get(r)
123 wctx.sub(s).get(r)
124 sm[s] = r
124 sm[s] = r
125
125
126 # record merged .hgsubstate
126 # record merged .hgsubstate
127 writestate(repo, sm)
127 writestate(repo, sm)
128
128
129 def _abssource(repo, push=False):
129 def _abssource(repo, push=False):
130 if hasattr(repo, '_subparent'):
130 if hasattr(repo, '_subparent'):
131 source = repo._subsource
131 source = repo._subsource
132 if source.startswith('/') or '://' in source:
132 if source.startswith('/') or '://' in source:
133 return source
133 return source
134 parent = _abssource(repo._subparent, push)
134 parent = _abssource(repo._subparent, push)
135 if '://' in parent:
135 if '://' in parent:
136 if parent[-1] == '/':
136 if parent[-1] == '/':
137 parent = parent[:-1]
137 parent = parent[:-1]
138 return parent + '/' + source
138 return parent + '/' + source
139 return os.path.join(parent, repo._subsource)
139 return os.path.join(parent, repo._subsource)
140 if push and repo.ui.config('paths', 'default-push'):
140 if push and repo.ui.config('paths', 'default-push'):
141 return repo.ui.config('paths', 'default-push', repo.root)
141 return repo.ui.config('paths', 'default-push', repo.root)
142 return repo.ui.config('paths', 'default', repo.root)
142 return repo.ui.config('paths', 'default', repo.root)
143
143
144 def subrepo(ctx, path):
144 def subrepo(ctx, path):
145 # subrepo inherently violates our import layering rules
145 # subrepo inherently violates our import layering rules
146 # because it wants to make repo objects from deep inside the stack
146 # because it wants to make repo objects from deep inside the stack
147 # so we manually delay the circular imports to not break
147 # so we manually delay the circular imports to not break
148 # scripts that don't use our demand-loading
148 # scripts that don't use our demand-loading
149 global hg
149 global hg
150 import hg as h
150 import hg as h
151 hg = h
151 hg = h
152
152
153 util.path_auditor(ctx._repo.root)(path)
153 util.path_auditor(ctx._repo.root)(path)
154 state = ctx.substate.get(path, nullstate)
154 state = ctx.substate.get(path, nullstate)
155 if state[2] not in types:
155 if state[2] not in types:
156 raise util.Abort(_('unknown subrepo type %s') % state[2])
156 raise util.Abort(_('unknown subrepo type %s') % state[2])
157 return types[state[2]](ctx, path, state[:2])
157 return types[state[2]](ctx, path, state[:2])
158
158
159 # subrepo classes need to implement the following methods:
159 # subrepo classes need to implement the following methods:
160 # __init__(self, ctx, path, state)
160 # __init__(self, ctx, path, state)
161 # dirty(self): returns true if the dirstate of the subrepo
161 # dirty(self): returns true if the dirstate of the subrepo
162 # does not match current stored state
162 # does not match current stored state
163 # commit(self, text, user, date): commit the current changes
163 # commit(self, text, user, date): commit the current changes
164 # to the subrepo with the given log message. Use given
164 # to the subrepo with the given log message. Use given
165 # user and date if possible. Return the new state of the subrepo.
165 # user and date if possible. Return the new state of the subrepo.
166 # remove(self): remove the subrepo (should verify the dirstate
166 # remove(self): remove the subrepo (should verify the dirstate
167 # is not dirty first)
167 # is not dirty first)
168 # get(self, state): run whatever commands are needed to put the
168 # get(self, state): run whatever commands are needed to put the
169 # subrepo into this state
169 # subrepo into this state
170 # merge(self, state): merge currently-saved state with the new state.
170 # merge(self, state): merge currently-saved state with the new state.
171 # push(self, force): perform whatever action is analagous to 'hg push'
171 # push(self, force): perform whatever action is analagous to 'hg push'
172 # This may be a no-op on some systems.
172 # This may be a no-op on some systems.
173
173
174 class hgsubrepo(object):
174 class hgsubrepo(object):
175 def __init__(self, ctx, path, state):
175 def __init__(self, ctx, path, state):
176 self._path = path
176 self._path = path
177 self._state = state
177 self._state = state
178 r = ctx._repo
178 r = ctx._repo
179 root = r.wjoin(path)
179 root = r.wjoin(path)
180 create = False
180 create = False
181 if not os.path.exists(os.path.join(root, '.hg')):
181 if not os.path.exists(os.path.join(root, '.hg')):
182 create = True
182 create = True
183 util.makedirs(root)
183 util.makedirs(root)
184 self._repo = hg.repository(r.ui, root, create=create)
184 self._repo = hg.repository(r.ui, root, create=create)
185 self._repo._subparent = r
185 self._repo._subparent = r
186 self._repo._subsource = state[0]
186 self._repo._subsource = state[0]
187
187
188 if create:
188 if create:
189 fp = self._repo.opener("hgrc", "w", text=True)
189 fp = self._repo.opener("hgrc", "w", text=True)
190 fp.write('[paths]\n')
190 fp.write('[paths]\n')
191
191
192 def addpathconfig(key, value):
192 def addpathconfig(key, value):
193 fp.write('%s = %s\n' % (key, value))
193 fp.write('%s = %s\n' % (key, value))
194 self._repo.ui.setconfig('paths', key, value)
194 self._repo.ui.setconfig('paths', key, value)
195
195
196 defpath = _abssource(self._repo)
196 defpath = _abssource(self._repo)
197 defpushpath = _abssource(self._repo, True)
197 defpushpath = _abssource(self._repo, True)
198 addpathconfig('default', defpath)
198 addpathconfig('default', defpath)
199 if defpath != defpushpath:
199 if defpath != defpushpath:
200 addpathconfig('default-push', defpushpath)
200 addpathconfig('default-push', defpushpath)
201 fp.close()
201 fp.close()
202
202
203 def dirty(self):
203 def dirty(self):
204 r = self._state[1]
204 r = self._state[1]
205 if r == '':
205 if r == '':
206 return True
206 return True
207 w = self._repo[None]
207 w = self._repo[None]
208 if w.p1() != self._repo[r]: # version checked out change
208 if w.p1() != self._repo[r]: # version checked out change
209 return True
209 return True
210 return w.dirty() # working directory changed
210 return w.dirty() # working directory changed
211
211
212 def commit(self, text, user, date):
212 def commit(self, text, user, date):
213 self._repo.ui.debug("committing subrepo %s\n" % self._path)
213 self._repo.ui.debug("committing subrepo %s\n" % self._path)
214 n = self._repo.commit(text, user, date)
214 n = self._repo.commit(text, user, date)
215 if not n:
215 if not n:
216 return self._repo['.'].hex() # different version checked out
216 return self._repo['.'].hex() # different version checked out
217 return node.hex(n)
217 return node.hex(n)
218
218
219 def remove(self):
219 def remove(self):
220 # we can't fully delete the repository as it may contain
220 # we can't fully delete the repository as it may contain
221 # local-only history
221 # local-only history
222 self._repo.ui.note(_('removing subrepo %s\n') % self._path)
222 self._repo.ui.note(_('removing subrepo %s\n') % self._path)
223 hg.clean(self._repo, node.nullid, False)
223 hg.clean(self._repo, node.nullid, False)
224
224
225 def _get(self, state):
225 def _get(self, state):
226 source, revision, kind = state
226 source, revision, kind = state
227 try:
227 try:
228 self._repo.lookup(revision)
228 self._repo.lookup(revision)
229 except error.RepoError:
229 except error.RepoError:
230 self._repo._subsource = source
230 self._repo._subsource = source
231 srcurl = _abssource(self._repo)
231 srcurl = _abssource(self._repo)
232 self._repo.ui.status(_('pulling subrepo %s from %s\n')
232 self._repo.ui.status(_('pulling subrepo %s from %s\n')
233 % (self._path, srcurl))
233 % (self._path, srcurl))
234 other = hg.repository(self._repo.ui, srcurl)
234 other = hg.repository(self._repo.ui, srcurl)
235 self._repo.pull(other)
235 self._repo.pull(other)
236
236
237 def get(self, state):
237 def get(self, state):
238 self._get(state)
238 self._get(state)
239 source, revision, kind = state
239 source, revision, kind = state
240 self._repo.ui.debug("getting subrepo %s\n" % self._path)
240 self._repo.ui.debug("getting subrepo %s\n" % self._path)
241 hg.clean(self._repo, revision, False)
241 hg.clean(self._repo, revision, False)
242
242
243 def merge(self, state):
243 def merge(self, state):
244 self._get(state)
244 self._get(state)
245 cur = self._repo['.']
245 cur = self._repo['.']
246 dst = self._repo[state[1]]
246 dst = self._repo[state[1]]
247 anc = dst.ancestor(cur)
247 anc = dst.ancestor(cur)
248 if anc == cur:
248 if anc == cur:
249 self._repo.ui.debug("updating subrepo %s\n" % self._path)
249 self._repo.ui.debug("updating subrepo %s\n" % self._path)
250 hg.update(self._repo, state[1])
250 hg.update(self._repo, state[1])
251 elif anc == dst:
251 elif anc == dst:
252 self._repo.ui.debug("skipping subrepo %s\n" % self._path)
252 self._repo.ui.debug("skipping subrepo %s\n" % self._path)
253 else:
253 else:
254 self._repo.ui.debug("merging subrepo %s\n" % self._path)
254 self._repo.ui.debug("merging subrepo %s\n" % self._path)
255 hg.merge(self._repo, state[1], remind=False)
255 hg.merge(self._repo, state[1], remind=False)
256
256
257 def push(self, force):
257 def push(self, force):
258 # push subrepos depth-first for coherent ordering
258 # push subrepos depth-first for coherent ordering
259 c = self._repo['']
259 c = self._repo['']
260 subs = c.substate # only repos that are committed
260 subs = c.substate # only repos that are committed
261 for s in sorted(subs):
261 for s in sorted(subs):
262 c.sub(s).push(force)
262 if not c.sub(s).push(force):
263 return False
263
264
264 self._repo.ui.status(_('pushing subrepo %s\n') % self._path)
265 self._repo.ui.status(_('pushing subrepo %s\n') % self._path)
265 dsturl = _abssource(self._repo, True)
266 dsturl = _abssource(self._repo, True)
266 other = hg.repository(self._repo.ui, dsturl)
267 other = hg.repository(self._repo.ui, dsturl)
267 self._repo.push(other, force)
268 return self._repo.push(other, force)
268
269
269 class svnsubrepo(object):
270 class svnsubrepo(object):
270 def __init__(self, ctx, path, state):
271 def __init__(self, ctx, path, state):
271 self._path = path
272 self._path = path
272 self._state = state
273 self._state = state
273 self._ctx = ctx
274 self._ctx = ctx
274 self._ui = ctx._repo.ui
275 self._ui = ctx._repo.ui
275
276
276 def _svncommand(self, commands):
277 def _svncommand(self, commands):
277 path = os.path.join(self._ctx._repo.origroot, self._path)
278 path = os.path.join(self._ctx._repo.origroot, self._path)
278 cmd = ['svn'] + commands + [path]
279 cmd = ['svn'] + commands + [path]
279 cmd = [util.shellquote(arg) for arg in cmd]
280 cmd = [util.shellquote(arg) for arg in cmd]
280 cmd = util.quotecommand(' '.join(cmd))
281 cmd = util.quotecommand(' '.join(cmd))
281 env = dict(os.environ)
282 env = dict(os.environ)
282 # Avoid localized output, preserve current locale for everything else.
283 # Avoid localized output, preserve current locale for everything else.
283 env['LC_MESSAGES'] = 'C'
284 env['LC_MESSAGES'] = 'C'
284 write, read, err = util.popen3(cmd, env=env, newlines=True)
285 write, read, err = util.popen3(cmd, env=env, newlines=True)
285 retdata = read.read()
286 retdata = read.read()
286 err = err.read().strip()
287 err = err.read().strip()
287 if err:
288 if err:
288 raise util.Abort(err)
289 raise util.Abort(err)
289 return retdata
290 return retdata
290
291
291 def _wcrev(self):
292 def _wcrev(self):
292 output = self._svncommand(['info', '--xml'])
293 output = self._svncommand(['info', '--xml'])
293 doc = xml.dom.minidom.parseString(output)
294 doc = xml.dom.minidom.parseString(output)
294 entries = doc.getElementsByTagName('entry')
295 entries = doc.getElementsByTagName('entry')
295 if not entries:
296 if not entries:
296 return 0
297 return 0
297 return int(entries[0].getAttribute('revision') or 0)
298 return int(entries[0].getAttribute('revision') or 0)
298
299
299 def _wcchanged(self):
300 def _wcchanged(self):
300 """Return (changes, extchanges) where changes is True
301 """Return (changes, extchanges) where changes is True
301 if the working directory was changed, and extchanges is
302 if the working directory was changed, and extchanges is
302 True if any of these changes concern an external entry.
303 True if any of these changes concern an external entry.
303 """
304 """
304 output = self._svncommand(['status', '--xml'])
305 output = self._svncommand(['status', '--xml'])
305 externals, changes = [], []
306 externals, changes = [], []
306 doc = xml.dom.minidom.parseString(output)
307 doc = xml.dom.minidom.parseString(output)
307 for e in doc.getElementsByTagName('entry'):
308 for e in doc.getElementsByTagName('entry'):
308 s = e.getElementsByTagName('wc-status')
309 s = e.getElementsByTagName('wc-status')
309 if not s:
310 if not s:
310 continue
311 continue
311 item = s[0].getAttribute('item')
312 item = s[0].getAttribute('item')
312 props = s[0].getAttribute('props')
313 props = s[0].getAttribute('props')
313 path = e.getAttribute('path')
314 path = e.getAttribute('path')
314 if item == 'external':
315 if item == 'external':
315 externals.append(path)
316 externals.append(path)
316 if (item not in ('', 'normal', 'unversioned', 'external')
317 if (item not in ('', 'normal', 'unversioned', 'external')
317 or props not in ('', 'none')):
318 or props not in ('', 'none')):
318 changes.append(path)
319 changes.append(path)
319 for path in changes:
320 for path in changes:
320 for ext in externals:
321 for ext in externals:
321 if path == ext or path.startswith(ext + os.sep):
322 if path == ext or path.startswith(ext + os.sep):
322 return True, True
323 return True, True
323 return bool(changes), False
324 return bool(changes), False
324
325
325 def dirty(self):
326 def dirty(self):
326 if self._wcrev() == self._state[1] and not self._wcchanged()[0]:
327 if self._wcrev() == self._state[1] and not self._wcchanged()[0]:
327 return False
328 return False
328 return True
329 return True
329
330
330 def commit(self, text, user, date):
331 def commit(self, text, user, date):
331 # user and date are out of our hands since svn is centralized
332 # user and date are out of our hands since svn is centralized
332 changed, extchanged = self._wcchanged()
333 changed, extchanged = self._wcchanged()
333 if not changed:
334 if not changed:
334 return self._wcrev()
335 return self._wcrev()
335 if extchanged:
336 if extchanged:
336 # Do not try to commit externals
337 # Do not try to commit externals
337 raise util.Abort(_('cannot commit svn externals'))
338 raise util.Abort(_('cannot commit svn externals'))
338 commitinfo = self._svncommand(['commit', '-m', text])
339 commitinfo = self._svncommand(['commit', '-m', text])
339 self._ui.status(commitinfo)
340 self._ui.status(commitinfo)
340 newrev = re.search('Committed revision ([\d]+).', commitinfo)
341 newrev = re.search('Committed revision ([\d]+).', commitinfo)
341 if not newrev:
342 if not newrev:
342 raise util.Abort(commitinfo.splitlines()[-1])
343 raise util.Abort(commitinfo.splitlines()[-1])
343 newrev = newrev.groups()[0]
344 newrev = newrev.groups()[0]
344 self._ui.status(self._svncommand(['update', '-r', newrev]))
345 self._ui.status(self._svncommand(['update', '-r', newrev]))
345 return newrev
346 return newrev
346
347
347 def remove(self):
348 def remove(self):
348 if self.dirty():
349 if self.dirty():
349 self._ui.warn(_('not removing repo %s because '
350 self._ui.warn(_('not removing repo %s because '
350 'it has changes.\n' % self._path))
351 'it has changes.\n' % self._path))
351 return
352 return
352 self._ui.note(_('removing subrepo %s\n') % self._path)
353 self._ui.note(_('removing subrepo %s\n') % self._path)
353 shutil.rmtree(self._ctx.repo.join(self._path))
354 shutil.rmtree(self._ctx.repo.join(self._path))
354
355
355 def get(self, state):
356 def get(self, state):
356 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
357 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
357 if not re.search('Checked out revision [\d]+.', status):
358 if not re.search('Checked out revision [\d]+.', status):
358 raise util.Abort(status.splitlines()[-1])
359 raise util.Abort(status.splitlines()[-1])
359 self._ui.status(status)
360 self._ui.status(status)
360
361
361 def merge(self, state):
362 def merge(self, state):
362 old = int(self._state[1])
363 old = int(self._state[1])
363 new = int(state[1])
364 new = int(state[1])
364 if new > old:
365 if new > old:
365 self.get(state)
366 self.get(state)
366
367
367 def push(self, force):
368 def push(self, force):
368 # nothing for svn
369 # nothing for svn
369 pass
370 pass
370
371
371 types = {
372 types = {
372 'hg': hgsubrepo,
373 'hg': hgsubrepo,
373 'svn': svnsubrepo,
374 'svn': svnsubrepo,
374 }
375 }
@@ -1,339 +1,341 b''
1 # tags.py - read tag info from local repository
1 # tags.py - read tag info from local repository
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009 Matt Mackall <mpm@selenic.com>
4 # Copyright 2009 Greg Ward <greg@gerg.ca>
4 # Copyright 2009 Greg Ward <greg@gerg.ca>
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 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 # Currently this module only deals with reading and caching tags.
9 # Currently this module only deals with reading and caching tags.
10 # Eventually, it could take care of updating (adding/removing/moving)
10 # Eventually, it could take care of updating (adding/removing/moving)
11 # tags too.
11 # tags too.
12
12
13 from node import nullid, bin, hex, short
13 from node import nullid, bin, hex, short
14 from i18n import _
14 from i18n import _
15 import encoding
15 import encoding
16 import error
16 import error
17
17
18 def _debugalways(ui, *msg):
18 def _debugalways(ui, *msg):
19 ui.write(*msg)
19 ui.write(*msg)
20
20
21 def _debugconditional(ui, *msg):
21 def _debugconditional(ui, *msg):
22 ui.debug(*msg)
22 ui.debug(*msg)
23
23
24 def _debugnever(ui, *msg):
24 def _debugnever(ui, *msg):
25 pass
25 pass
26
26
27 _debug = _debugalways
27 _debug = _debugalways
28 _debug = _debugnever
28 _debug = _debugnever
29
29
30 def findglobaltags1(ui, repo, alltags, tagtypes):
30 def findglobaltags1(ui, repo, alltags, tagtypes):
31 '''Find global tags in repo by reading .hgtags from every head that
31 '''Find global tags in repo by reading .hgtags from every head that
32 has a distinct version of it. Updates the dicts alltags, tagtypes
32 has a distinct version of it. Updates the dicts alltags, tagtypes
33 in place: alltags maps tag name to (node, hist) pair (see _readtags()
33 in place: alltags maps tag name to (node, hist) pair (see _readtags()
34 below), and tagtypes maps tag name to tag type ('global' in this
34 below), and tagtypes maps tag name to tag type ('global' in this
35 case).'''
35 case).'''
36
36
37 seen = set()
37 seen = set()
38 fctx = None
38 fctx = None
39 ctxs = [] # list of filectx
39 ctxs = [] # list of filectx
40 for node in repo.heads():
40 for node in repo.heads():
41 try:
41 try:
42 fnode = repo[node].filenode('.hgtags')
42 fnode = repo[node].filenode('.hgtags')
43 except error.LookupError:
43 except error.LookupError:
44 continue
44 continue
45 if fnode not in seen:
45 if fnode not in seen:
46 seen.add(fnode)
46 seen.add(fnode)
47 if not fctx:
47 if not fctx:
48 fctx = repo.filectx('.hgtags', fileid=fnode)
48 fctx = repo.filectx('.hgtags', fileid=fnode)
49 else:
49 else:
50 fctx = fctx.filectx(fnode)
50 fctx = fctx.filectx(fnode)
51 ctxs.append(fctx)
51 ctxs.append(fctx)
52
52
53 # read the tags file from each head, ending with the tip
53 # read the tags file from each head, ending with the tip
54 for fctx in reversed(ctxs):
54 for fctx in reversed(ctxs):
55 filetags = _readtags(
55 filetags = _readtags(
56 ui, repo, fctx.data().splitlines(), fctx)
56 ui, repo, fctx.data().splitlines(), fctx)
57 _updatetags(filetags, "global", alltags, tagtypes)
57 _updatetags(filetags, "global", alltags, tagtypes)
58
58
59 def findglobaltags2(ui, repo, alltags, tagtypes):
59 def findglobaltags2(ui, repo, alltags, tagtypes):
60 '''Same as findglobaltags1(), but with caching.'''
60 '''Same as findglobaltags1(), but with caching.'''
61 # This is so we can be lazy and assume alltags contains only global
61 # This is so we can be lazy and assume alltags contains only global
62 # tags when we pass it to _writetagcache().
62 # tags when we pass it to _writetagcache().
63 assert len(alltags) == len(tagtypes) == 0, \
63 assert len(alltags) == len(tagtypes) == 0, \
64 "findglobaltags() should be called first"
64 "findglobaltags() should be called first"
65
65
66 (heads, tagfnode, cachetags, shouldwrite) = _readtagcache(ui, repo)
66 (heads, tagfnode, cachetags, shouldwrite) = _readtagcache(ui, repo)
67 if cachetags is not None:
67 if cachetags is not None:
68 assert not shouldwrite
68 assert not shouldwrite
69 # XXX is this really 100% correct? are there oddball special
69 # XXX is this really 100% correct? are there oddball special
70 # cases where a global tag should outrank a local tag but won't,
70 # cases where a global tag should outrank a local tag but won't,
71 # because cachetags does not contain rank info?
71 # because cachetags does not contain rank info?
72 _updatetags(cachetags, 'global', alltags, tagtypes)
72 _updatetags(cachetags, 'global', alltags, tagtypes)
73 return
73 return
74
74
75 _debug(ui, "reading tags from %d head(s): %s\n"
75 _debug(ui, "reading tags from %d head(s): %s\n"
76 % (len(heads), map(short, reversed(heads))))
76 % (len(heads), map(short, reversed(heads))))
77 seen = set() # set of fnode
77 seen = set() # set of fnode
78 fctx = None
78 fctx = None
79 for head in reversed(heads): # oldest to newest
79 for head in reversed(heads): # oldest to newest
80 assert head in repo.changelog.nodemap, \
80 assert head in repo.changelog.nodemap, \
81 "tag cache returned bogus head %s" % short(head)
81 "tag cache returned bogus head %s" % short(head)
82
82
83 fnode = tagfnode.get(head)
83 fnode = tagfnode.get(head)
84 if fnode and fnode not in seen:
84 if fnode and fnode not in seen:
85 seen.add(fnode)
85 seen.add(fnode)
86 if not fctx:
86 if not fctx:
87 fctx = repo.filectx('.hgtags', fileid=fnode)
87 fctx = repo.filectx('.hgtags', fileid=fnode)
88 else:
88 else:
89 fctx = fctx.filectx(fnode)
89 fctx = fctx.filectx(fnode)
90
90
91 filetags = _readtags(ui, repo, fctx.data().splitlines(), fctx)
91 filetags = _readtags(ui, repo, fctx.data().splitlines(), fctx)
92 _updatetags(filetags, 'global', alltags, tagtypes)
92 _updatetags(filetags, 'global', alltags, tagtypes)
93
93
94 # and update the cache (if necessary)
94 # and update the cache (if necessary)
95 if shouldwrite:
95 if shouldwrite:
96 _writetagcache(ui, repo, heads, tagfnode, alltags)
96 _writetagcache(ui, repo, heads, tagfnode, alltags)
97
97
98 # Set this to findglobaltags1 to disable tag caching.
98 # Set this to findglobaltags1 to disable tag caching.
99 findglobaltags = findglobaltags2
99 findglobaltags = findglobaltags2
100
100
101 def readlocaltags(ui, repo, alltags, tagtypes):
101 def readlocaltags(ui, repo, alltags, tagtypes):
102 '''Read local tags in repo. Update alltags and tagtypes.'''
102 '''Read local tags in repo. Update alltags and tagtypes.'''
103 try:
103 try:
104 # localtags is in the local encoding; re-encode to UTF-8 on
104 # localtags is in the local encoding; re-encode to UTF-8 on
105 # input for consistency with the rest of this module.
105 # input for consistency with the rest of this module.
106 data = repo.opener("localtags").read()
106 data = repo.opener("localtags").read()
107 filetags = _readtags(
107 filetags = _readtags(
108 ui, repo, data.splitlines(), "localtags",
108 ui, repo, data.splitlines(), "localtags",
109 recode=encoding.fromlocal)
109 recode=encoding.fromlocal)
110 _updatetags(filetags, "local", alltags, tagtypes)
110 _updatetags(filetags, "local", alltags, tagtypes)
111 except IOError:
111 except IOError:
112 pass
112 pass
113
113
114 def _readtags(ui, repo, lines, fn, recode=None):
114 def _readtags(ui, repo, lines, fn, recode=None):
115 '''Read tag definitions from a file (or any source of lines).
115 '''Read tag definitions from a file (or any source of lines).
116 Return a mapping from tag name to (node, hist): node is the node id
116 Return a mapping from tag name to (node, hist): node is the node id
117 from the last line read for that name, and hist is the list of node
117 from the last line read for that name, and hist is the list of node
118 ids previously associated with it (in file order). All node ids are
118 ids previously associated with it (in file order). All node ids are
119 binary, not hex.'''
119 binary, not hex.'''
120
120
121 filetags = {} # map tag name to (node, hist)
121 filetags = {} # map tag name to (node, hist)
122 count = 0
122 count = 0
123
123
124 def warn(msg):
124 def warn(msg):
125 ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
125 ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
126
126
127 for line in lines:
127 for line in lines:
128 count += 1
128 count += 1
129 if not line:
129 if not line:
130 continue
130 continue
131 try:
131 try:
132 (nodehex, name) = line.split(" ", 1)
132 (nodehex, name) = line.split(" ", 1)
133 except ValueError:
133 except ValueError:
134 warn(_("cannot parse entry"))
134 warn(_("cannot parse entry"))
135 continue
135 continue
136 name = name.strip()
136 name = name.strip()
137 if recode:
137 if recode:
138 name = recode(name)
138 name = recode(name)
139 try:
139 try:
140 nodebin = bin(nodehex)
140 nodebin = bin(nodehex)
141 except TypeError:
141 except TypeError:
142 warn(_("node '%s' is not well formed") % nodehex)
142 warn(_("node '%s' is not well formed") % nodehex)
143 continue
143 continue
144 if nodebin not in repo.changelog.nodemap:
144 if nodebin not in repo.changelog.nodemap:
145 # silently ignore as pull -r might cause this
145 # silently ignore as pull -r might cause this
146 continue
146 continue
147
147
148 # update filetags
148 # update filetags
149 hist = []
149 hist = []
150 if name in filetags:
150 if name in filetags:
151 n, hist = filetags[name]
151 n, hist = filetags[name]
152 hist.append(n)
152 hist.append(n)
153 filetags[name] = (nodebin, hist)
153 filetags[name] = (nodebin, hist)
154 return filetags
154 return filetags
155
155
156 def _updatetags(filetags, tagtype, alltags, tagtypes):
156 def _updatetags(filetags, tagtype, alltags, tagtypes):
157 '''Incorporate the tag info read from one file into the two
157 '''Incorporate the tag info read from one file into the two
158 dictionaries, alltags and tagtypes, that contain all tag
158 dictionaries, alltags and tagtypes, that contain all tag
159 info (global across all heads plus local).'''
159 info (global across all heads plus local).'''
160
160
161 for name, nodehist in filetags.iteritems():
161 for name, nodehist in filetags.iteritems():
162 if name not in alltags:
162 if name not in alltags:
163 alltags[name] = nodehist
163 alltags[name] = nodehist
164 tagtypes[name] = tagtype
164 tagtypes[name] = tagtype
165 continue
165 continue
166
166
167 # we prefer alltags[name] if:
167 # we prefer alltags[name] if:
168 # it supercedes us OR
168 # it supercedes us OR
169 # mutual supercedes and it has a higher rank
169 # mutual supercedes and it has a higher rank
170 # otherwise we win because we're tip-most
170 # otherwise we win because we're tip-most
171 anode, ahist = nodehist
171 anode, ahist = nodehist
172 bnode, bhist = alltags[name]
172 bnode, bhist = alltags[name]
173 if (bnode != anode and anode in bhist and
173 if (bnode != anode and anode in bhist and
174 (bnode not in ahist or len(bhist) > len(ahist))):
174 (bnode not in ahist or len(bhist) > len(ahist))):
175 anode = bnode
175 anode = bnode
176 ahist.extend([n for n in bhist if n not in ahist])
176 ahist.extend([n for n in bhist if n not in ahist])
177 alltags[name] = anode, ahist
177 alltags[name] = anode, ahist
178 tagtypes[name] = tagtype
178 tagtypes[name] = tagtype
179
179
180
180
181 # The tag cache only stores info about heads, not the tag contents
181 # The tag cache only stores info about heads, not the tag contents
182 # from each head. I.e. it doesn't try to squeeze out the maximum
182 # from each head. I.e. it doesn't try to squeeze out the maximum
183 # performance, but is simpler has a better chance of actually
183 # performance, but is simpler has a better chance of actually
184 # working correctly. And this gives the biggest performance win: it
184 # working correctly. And this gives the biggest performance win: it
185 # avoids looking up .hgtags in the manifest for every head, and it
185 # avoids looking up .hgtags in the manifest for every head, and it
186 # can avoid calling heads() at all if there have been no changes to
186 # can avoid calling heads() at all if there have been no changes to
187 # the repo.
187 # the repo.
188
188
189 def _readtagcache(ui, repo):
189 def _readtagcache(ui, repo):
190 '''Read the tag cache and return a tuple (heads, fnodes, cachetags,
190 '''Read the tag cache and return a tuple (heads, fnodes, cachetags,
191 shouldwrite). If the cache is completely up-to-date, cachetags is a
191 shouldwrite). If the cache is completely up-to-date, cachetags is a
192 dict of the form returned by _readtags(); otherwise, it is None and
192 dict of the form returned by _readtags(); otherwise, it is None and
193 heads and fnodes are set. In that case, heads is the list of all
193 heads and fnodes are set. In that case, heads is the list of all
194 heads currently in the repository (ordered from tip to oldest) and
194 heads currently in the repository (ordered from tip to oldest) and
195 fnodes is a mapping from head to .hgtags filenode. If those two are
195 fnodes is a mapping from head to .hgtags filenode. If those two are
196 set, caller is responsible for reading tag info from each head.'''
196 set, caller is responsible for reading tag info from each head.'''
197
197
198 try:
198 try:
199 cachefile = repo.opener('tags.cache', 'r')
199 cachefile = repo.opener('tags.cache', 'r')
200 # force reading the file for static-http
201 cachelines = iter(cachefile)
200 _debug(ui, 'reading tag cache from %s\n' % cachefile.name)
202 _debug(ui, 'reading tag cache from %s\n' % cachefile.name)
201 except IOError:
203 except IOError:
202 cachefile = None
204 cachefile = None
203
205
204 # The cache file consists of lines like
206 # The cache file consists of lines like
205 # <headrev> <headnode> [<tagnode>]
207 # <headrev> <headnode> [<tagnode>]
206 # where <headrev> and <headnode> redundantly identify a repository
208 # where <headrev> and <headnode> redundantly identify a repository
207 # head from the time the cache was written, and <tagnode> is the
209 # head from the time the cache was written, and <tagnode> is the
208 # filenode of .hgtags on that head. Heads with no .hgtags file will
210 # filenode of .hgtags on that head. Heads with no .hgtags file will
209 # have no <tagnode>. The cache is ordered from tip to oldest (which
211 # have no <tagnode>. The cache is ordered from tip to oldest (which
210 # is part of why <headrev> is there: a quick visual check is all
212 # is part of why <headrev> is there: a quick visual check is all
211 # that's required to ensure correct order).
213 # that's required to ensure correct order).
212 #
214 #
213 # This information is enough to let us avoid the most expensive part
215 # This information is enough to let us avoid the most expensive part
214 # of finding global tags, which is looking up <tagnode> in the
216 # of finding global tags, which is looking up <tagnode> in the
215 # manifest for each head.
217 # manifest for each head.
216 cacherevs = [] # list of headrev
218 cacherevs = [] # list of headrev
217 cacheheads = [] # list of headnode
219 cacheheads = [] # list of headnode
218 cachefnode = {} # map headnode to filenode
220 cachefnode = {} # map headnode to filenode
219 if cachefile:
221 if cachefile:
220 for line in cachefile:
222 for line in cachelines:
221 if line == "\n":
223 if line == "\n":
222 break
224 break
223 line = line.rstrip().split()
225 line = line.rstrip().split()
224 cacherevs.append(int(line[0]))
226 cacherevs.append(int(line[0]))
225 headnode = bin(line[1])
227 headnode = bin(line[1])
226 cacheheads.append(headnode)
228 cacheheads.append(headnode)
227 if len(line) == 3:
229 if len(line) == 3:
228 fnode = bin(line[2])
230 fnode = bin(line[2])
229 cachefnode[headnode] = fnode
231 cachefnode[headnode] = fnode
230
232
231 tipnode = repo.changelog.tip()
233 tipnode = repo.changelog.tip()
232 tiprev = len(repo.changelog) - 1
234 tiprev = len(repo.changelog) - 1
233
235
234 # Case 1 (common): tip is the same, so nothing has changed.
236 # Case 1 (common): tip is the same, so nothing has changed.
235 # (Unchanged tip trivially means no changesets have been added.
237 # (Unchanged tip trivially means no changesets have been added.
236 # But, thanks to localrepository.destroyed(), it also means none
238 # But, thanks to localrepository.destroyed(), it also means none
237 # have been destroyed by strip or rollback.)
239 # have been destroyed by strip or rollback.)
238 if cacheheads and cacheheads[0] == tipnode and cacherevs[0] == tiprev:
240 if cacheheads and cacheheads[0] == tipnode and cacherevs[0] == tiprev:
239 _debug(ui, "tag cache: tip unchanged\n")
241 _debug(ui, "tag cache: tip unchanged\n")
240 tags = _readtags(ui, repo, cachefile, cachefile.name)
242 tags = _readtags(ui, repo, cachelines, cachefile.name)
241 cachefile.close()
243 cachefile.close()
242 return (None, None, tags, False)
244 return (None, None, tags, False)
243 if cachefile:
245 if cachefile:
244 cachefile.close() # ignore rest of file
246 cachefile.close() # ignore rest of file
245
247
246 repoheads = repo.heads()
248 repoheads = repo.heads()
247 # Case 2 (uncommon): empty repo; get out quickly and don't bother
249 # Case 2 (uncommon): empty repo; get out quickly and don't bother
248 # writing an empty cache.
250 # writing an empty cache.
249 if repoheads == [nullid]:
251 if repoheads == [nullid]:
250 return ([], {}, {}, False)
252 return ([], {}, {}, False)
251
253
252 # Case 3 (uncommon): cache file missing or empty.
254 # Case 3 (uncommon): cache file missing or empty.
253 if not cacheheads:
255 if not cacheheads:
254 _debug(ui, 'tag cache: cache file missing or empty\n')
256 _debug(ui, 'tag cache: cache file missing or empty\n')
255
257
256 # Case 4 (uncommon): tip rev decreased. This should only happen
258 # Case 4 (uncommon): tip rev decreased. This should only happen
257 # when we're called from localrepository.destroyed(). Refresh the
259 # when we're called from localrepository.destroyed(). Refresh the
258 # cache so future invocations will not see disappeared heads in the
260 # cache so future invocations will not see disappeared heads in the
259 # cache.
261 # cache.
260 elif cacheheads and tiprev < cacherevs[0]:
262 elif cacheheads and tiprev < cacherevs[0]:
261 _debug(ui,
263 _debug(ui,
262 'tag cache: tip rev decremented (from %d to %d), '
264 'tag cache: tip rev decremented (from %d to %d), '
263 'so we must be destroying nodes\n'
265 'so we must be destroying nodes\n'
264 % (cacherevs[0], tiprev))
266 % (cacherevs[0], tiprev))
265
267
266 # Case 5 (common): tip has changed, so we've added/replaced heads.
268 # Case 5 (common): tip has changed, so we've added/replaced heads.
267 else:
269 else:
268 _debug(ui,
270 _debug(ui,
269 'tag cache: tip has changed (%d:%s); must find new heads\n'
271 'tag cache: tip has changed (%d:%s); must find new heads\n'
270 % (tiprev, short(tipnode)))
272 % (tiprev, short(tipnode)))
271
273
272 # Luckily, the code to handle cases 3, 4, 5 is the same. So the
274 # Luckily, the code to handle cases 3, 4, 5 is the same. So the
273 # above if/elif/else can disappear once we're confident this thing
275 # above if/elif/else can disappear once we're confident this thing
274 # actually works and we don't need the debug output.
276 # actually works and we don't need the debug output.
275
277
276 # N.B. in case 4 (nodes destroyed), "new head" really means "newly
278 # N.B. in case 4 (nodes destroyed), "new head" really means "newly
277 # exposed".
279 # exposed".
278 newheads = [head
280 newheads = [head
279 for head in repoheads
281 for head in repoheads
280 if head not in set(cacheheads)]
282 if head not in set(cacheheads)]
281 _debug(ui, 'tag cache: found %d head(s) not in cache: %s\n'
283 _debug(ui, 'tag cache: found %d head(s) not in cache: %s\n'
282 % (len(newheads), map(short, newheads)))
284 % (len(newheads), map(short, newheads)))
283
285
284 # Now we have to lookup the .hgtags filenode for every new head.
286 # Now we have to lookup the .hgtags filenode for every new head.
285 # This is the most expensive part of finding tags, so performance
287 # This is the most expensive part of finding tags, so performance
286 # depends primarily on the size of newheads. Worst case: no cache
288 # depends primarily on the size of newheads. Worst case: no cache
287 # file, so newheads == repoheads.
289 # file, so newheads == repoheads.
288 for head in newheads:
290 for head in newheads:
289 cctx = repo[head]
291 cctx = repo[head]
290 try:
292 try:
291 fnode = cctx.filenode('.hgtags')
293 fnode = cctx.filenode('.hgtags')
292 cachefnode[head] = fnode
294 cachefnode[head] = fnode
293 except error.LookupError:
295 except error.LookupError:
294 # no .hgtags file on this head
296 # no .hgtags file on this head
295 pass
297 pass
296
298
297 # Caller has to iterate over all heads, but can use the filenodes in
299 # Caller has to iterate over all heads, but can use the filenodes in
298 # cachefnode to get to each .hgtags revision quickly.
300 # cachefnode to get to each .hgtags revision quickly.
299 return (repoheads, cachefnode, None, True)
301 return (repoheads, cachefnode, None, True)
300
302
301 def _writetagcache(ui, repo, heads, tagfnode, cachetags):
303 def _writetagcache(ui, repo, heads, tagfnode, cachetags):
302
304
303 try:
305 try:
304 cachefile = repo.opener('tags.cache', 'w', atomictemp=True)
306 cachefile = repo.opener('tags.cache', 'w', atomictemp=True)
305 except (OSError, IOError):
307 except (OSError, IOError):
306 return
308 return
307 _debug(ui, 'writing cache file %s\n' % cachefile.name)
309 _debug(ui, 'writing cache file %s\n' % cachefile.name)
308
310
309 realheads = repo.heads() # for sanity checks below
311 realheads = repo.heads() # for sanity checks below
310 for head in heads:
312 for head in heads:
311 # temporary sanity checks; these can probably be removed
313 # temporary sanity checks; these can probably be removed
312 # once this code has been in crew for a few weeks
314 # once this code has been in crew for a few weeks
313 assert head in repo.changelog.nodemap, \
315 assert head in repo.changelog.nodemap, \
314 'trying to write non-existent node %s to tag cache' % short(head)
316 'trying to write non-existent node %s to tag cache' % short(head)
315 assert head in realheads, \
317 assert head in realheads, \
316 'trying to write non-head %s to tag cache' % short(head)
318 'trying to write non-head %s to tag cache' % short(head)
317 assert head != nullid, \
319 assert head != nullid, \
318 'trying to write nullid to tag cache'
320 'trying to write nullid to tag cache'
319
321
320 # This can't fail because of the first assert above. When/if we
322 # This can't fail because of the first assert above. When/if we
321 # remove that assert, we might want to catch LookupError here
323 # remove that assert, we might want to catch LookupError here
322 # and downgrade it to a warning.
324 # and downgrade it to a warning.
323 rev = repo.changelog.rev(head)
325 rev = repo.changelog.rev(head)
324
326
325 fnode = tagfnode.get(head)
327 fnode = tagfnode.get(head)
326 if fnode:
328 if fnode:
327 cachefile.write('%d %s %s\n' % (rev, hex(head), hex(fnode)))
329 cachefile.write('%d %s %s\n' % (rev, hex(head), hex(fnode)))
328 else:
330 else:
329 cachefile.write('%d %s\n' % (rev, hex(head)))
331 cachefile.write('%d %s\n' % (rev, hex(head)))
330
332
331 # Tag names in the cache are in UTF-8 -- which is the whole reason
333 # Tag names in the cache are in UTF-8 -- which is the whole reason
332 # we keep them in UTF-8 throughout this module. If we converted
334 # we keep them in UTF-8 throughout this module. If we converted
333 # them local encoding on input, we would lose info writing them to
335 # them local encoding on input, we would lose info writing them to
334 # the cache.
336 # the cache.
335 cachefile.write('\n')
337 cachefile.write('\n')
336 for (name, (node, hist)) in cachetags.iteritems():
338 for (name, (node, hist)) in cachetags.iteritems():
337 cachefile.write("%s %s\n" % (hex(node), name))
339 cachefile.write("%s %s\n" % (hex(node), name))
338
340
339 cachefile.rename()
341 cachefile.rename()
@@ -1,180 +1,181 b''
1 #!/bin/sh
1 #!/bin/sh
2 # Test basic extension support
2 # Test basic extension support
3
3
4 "$TESTDIR/hghave" no-outer-repo || exit 80
4 "$TESTDIR/hghave" no-outer-repo || exit 80
5
5
6 cat > foobar.py <<EOF
6 cat > foobar.py <<EOF
7 import os
7 import os
8 from mercurial import commands
8 from mercurial import commands
9
9
10 def uisetup(ui):
10 def uisetup(ui):
11 ui.write("uisetup called\\n")
11 ui.write("uisetup called\\n")
12
12
13 def reposetup(ui, repo):
13 def reposetup(ui, repo):
14 ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
14 ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
15 ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
15 ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
16
16
17 def foo(ui, *args, **kwargs):
17 def foo(ui, *args, **kwargs):
18 ui.write("Foo\\n")
18 ui.write("Foo\\n")
19
19
20 def bar(ui, *args, **kwargs):
20 def bar(ui, *args, **kwargs):
21 ui.write("Bar\\n")
21 ui.write("Bar\\n")
22
22
23 cmdtable = {
23 cmdtable = {
24 "foo": (foo, [], "hg foo"),
24 "foo": (foo, [], "hg foo"),
25 "bar": (bar, [], "hg bar"),
25 "bar": (bar, [], "hg bar"),
26 }
26 }
27
27
28 commands.norepo += ' bar'
28 commands.norepo += ' bar'
29 EOF
29 EOF
30 abspath=`pwd`/foobar.py
30 abspath=`pwd`/foobar.py
31
31
32 mkdir barfoo
32 mkdir barfoo
33 cp foobar.py barfoo/__init__.py
33 cp foobar.py barfoo/__init__.py
34 barfoopath=`pwd`/barfoo
34 barfoopath=`pwd`/barfoo
35
35
36 hg init a
36 hg init a
37 cd a
37 cd a
38 echo foo > file
38 echo foo > file
39 hg add file
39 hg add file
40 hg commit -m 'add file'
40 hg commit -m 'add file'
41
41
42 echo '[extensions]' >> $HGRCPATH
42 echo '[extensions]' >> $HGRCPATH
43 echo "foobar = $abspath" >> $HGRCPATH
43 echo "foobar = $abspath" >> $HGRCPATH
44 hg foo
44 hg foo
45
45
46 cd ..
46 cd ..
47 hg clone a b
47 hg clone a b
48
48
49 hg bar
49 hg bar
50 echo 'foobar = !' >> $HGRCPATH
50 echo 'foobar = !' >> $HGRCPATH
51
51
52 echo '% module/__init__.py-style'
52 echo '% module/__init__.py-style'
53 echo "barfoo = $barfoopath" >> $HGRCPATH
53 echo "barfoo = $barfoopath" >> $HGRCPATH
54 cd a
54 cd a
55 hg foo
55 hg foo
56 echo 'barfoo = !' >> $HGRCPATH
56 echo 'barfoo = !' >> $HGRCPATH
57
57
58 # check that extensions are loaded in phases
58 # check that extensions are loaded in phases
59 cat > foo.py <<EOF
59 cat > foo.py <<EOF
60 import os
60 import os
61 name = os.path.basename(__file__).rsplit('.', 1)[0]
61 name = os.path.basename(__file__).rsplit('.', 1)[0]
62 print "1) %s imported" % name
62 print "1) %s imported" % name
63 def uisetup(ui):
63 def uisetup(ui):
64 print "2) %s uisetup" % name
64 print "2) %s uisetup" % name
65 def extsetup():
65 def extsetup():
66 print "3) %s extsetup" % name
66 print "3) %s extsetup" % name
67 def reposetup(ui, repo):
67 def reposetup(ui, repo):
68 print "4) %s reposetup" % name
68 print "4) %s reposetup" % name
69 EOF
69 EOF
70
70
71 cp foo.py bar.py
71 cp foo.py bar.py
72 echo 'foo = foo.py' >> $HGRCPATH
72 echo 'foo = foo.py' >> $HGRCPATH
73 echo 'bar = bar.py' >> $HGRCPATH
73 echo 'bar = bar.py' >> $HGRCPATH
74
74
75 # command with no output, we just want to see the extensions loaded
75 # command with no output, we just want to see the extensions loaded
76 hg paths
76 hg paths
77
77
78 # check hgweb's load order
78 # check hgweb's load order
79 echo '% hgweb.cgi'
79 echo '% hgweb.cgi'
80 cat > hgweb.cgi <<EOF
80 cat > hgweb.cgi <<EOF
81 #!/usr/bin/env python
81 #!/usr/bin/env python
82 from mercurial import demandimport; demandimport.enable()
82 from mercurial import demandimport; demandimport.enable()
83 from mercurial.hgweb import hgweb
83 from mercurial.hgweb import hgweb
84 from mercurial.hgweb import wsgicgi
84 from mercurial.hgweb import wsgicgi
85
85
86 application = hgweb('.', 'test repo')
86 application = hgweb('.', 'test repo')
87 wsgicgi.launch(application)
87 wsgicgi.launch(application)
88 EOF
88 EOF
89 SCRIPT_NAME='/' SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
89 SCRIPT_NAME='/' SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
90 | grep '^[0-9]) ' # ignores HTML output
90 | grep '^[0-9]) ' # ignores HTML output
91
91
92 echo 'foo = !' >> $HGRCPATH
92 echo 'foo = !' >> $HGRCPATH
93 echo 'bar = !' >> $HGRCPATH
93 echo 'bar = !' >> $HGRCPATH
94
94
95 cd ..
95 cd ..
96 cat > empty.py <<EOF
96 cat > empty.py <<EOF
97 '''empty cmdtable
97 '''empty cmdtable
98 '''
98 '''
99 cmdtable = {}
99 cmdtable = {}
100 EOF
100 EOF
101 emptypath=`pwd`/empty.py
101 emptypath=`pwd`/empty.py
102 echo "empty = $emptypath" >> $HGRCPATH
102 echo "empty = $emptypath" >> $HGRCPATH
103 hg help empty
103 hg help empty
104 echo 'empty = !' >> $HGRCPATH
104 echo 'empty = !' >> $HGRCPATH
105
105
106 cat > debugextension.py <<EOF
106 cat > debugextension.py <<EOF
107 '''only debugcommands
107 '''only debugcommands
108 '''
108 '''
109 def debugfoobar(ui, repo, *args, **opts):
109 def debugfoobar(ui, repo, *args, **opts):
110 "yet another debug command"
110 "yet another debug command"
111 pass
111 pass
112
112
113 def foo(ui, repo, *args, **opts):
113 def foo(ui, repo, *args, **opts):
114 """yet another foo command
114 """yet another foo command
115
115
116 This command has been DEPRECATED since forever.
116 This command has been DEPRECATED since forever.
117 """
117 """
118 pass
118 pass
119
119
120 cmdtable = {
120 cmdtable = {
121 "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
121 "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
122 "foo": (foo, (), "hg foo")
122 "foo": (foo, (), "hg foo")
123 }
123 }
124 EOF
124 EOF
125 debugpath=`pwd`/debugextension.py
125 debugpath=`pwd`/debugextension.py
126 echo "debugextension = $debugpath" >> $HGRCPATH
126 echo "debugextension = $debugpath" >> $HGRCPATH
127 echo "% hg help"
127 echo "% hg help"
128 hg help debugextension
128 hg help debugextension
129 echo "% hg help --verbose"
129 echo "% hg help --verbose"
130 hg --verbose help debugextension
130 hg --verbose help debugextension
131 echo "% hg help --debug"
131 echo "% hg help --debug"
132 hg --debug help debugextension
132 hg --debug help debugextension
133 echo 'debugextension = !' >> $HGRCPATH
133 echo 'debugextension = !' >> $HGRCPATH
134
134
135 echo % issue811
135 echo % issue811
136 debugpath=`pwd`/debugissue811.py
136 debugpath=`pwd`/debugissue811.py
137 cat > debugissue811.py <<EOF
137 cat > debugissue811.py <<EOF
138 '''show all loaded extensions
138 '''show all loaded extensions
139 '''
139 '''
140 from mercurial import extensions, commands
140 from mercurial import extensions, commands
141
141
142 def debugextensions(ui):
142 def debugextensions(ui):
143 "yet another debug command"
143 "yet another debug command"
144 ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
144 ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
145
145
146 cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
146 cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
147 commands.norepo += " debugextensions"
147 commands.norepo += " debugextensions"
148 EOF
148 EOF
149 echo "debugissue811 = $debugpath" >> $HGRCPATH
149 echo "debugissue811 = $debugpath" >> $HGRCPATH
150 echo "mq=" >> $HGRCPATH
150 echo "mq=" >> $HGRCPATH
151 echo "hgext.mq=" >> $HGRCPATH
151 echo "hgext.mq=" >> $HGRCPATH
152 echo "hgext/mq=" >> $HGRCPATH
152 echo "hgext/mq=" >> $HGRCPATH
153
153
154 echo % show extensions
154 echo % show extensions
155 hg debugextensions
155 hg debugextensions
156
156
157 echo '% disabled extension commands'
157 echo '% disabled extension commands'
158 HGRCPATH=
158 HGRCPATH=
159 export HGRCPATH
159 hg help email
160 hg help email
160 hg qdel
161 hg qdel
161 hg churn
162 hg churn
162 echo '% disabled extensions'
163 echo '% disabled extensions'
163 hg help churn
164 hg help churn
164 hg help patchbomb
165 hg help patchbomb
165 echo '% broken disabled extension and command'
166 echo '% broken disabled extension and command'
166 mkdir hgext
167 mkdir hgext
167 echo > hgext/__init__.py
168 echo > hgext/__init__.py
168 cat > hgext/broken.py <<EOF
169 cat > hgext/broken.py <<EOF
169 "broken extension'
170 "broken extension'
170 EOF
171 EOF
171 cat > path.py <<EOF
172 cat > path.py <<EOF
172 import os, sys
173 import os, sys
173 sys.path.insert(0, os.environ['HGEXTPATH'])
174 sys.path.insert(0, os.environ['HGEXTPATH'])
174 EOF
175 EOF
175 HGEXTPATH=`pwd`
176 HGEXTPATH=`pwd`
176 export HGEXTPATH
177 export HGEXTPATH
177 hg --config extensions.path=./path.py help broken
178 hg --config extensions.path=./path.py help broken
178 hg --config extensions.path=./path.py help foo > /dev/null
179 hg --config extensions.path=./path.py help foo > /dev/null
179
180
180 exit 0
181 exit 0
@@ -1,43 +1,43 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 echo "% setup"
3 echo "% setup"
4
4
5 # create a little extension that has 3 side-effects:
5 # create a little extension that has 3 side-effects:
6 # 1) ensure changelog data is not inlined
6 # 1) ensure changelog data is not inlined
7 # 2) make revlog to use lazyparser
7 # 2) make revlog to use lazyparser
8 # 3) test that repo.lookup() works
8 # 3) test that repo.lookup() works
9 # 1 and 2 are preconditions for the bug; 3 is the bug.
9 # 1 and 2 are preconditions for the bug; 3 is the bug.
10 cat > commitwrapper.py <<EOF
10 cat > commitwrapper.py <<EOF
11 from mercurial import extensions, node, revlog
11 from mercurial import extensions, node, revlog
12
12
13 def reposetup(ui, repo):
13 def reposetup(ui, repo):
14 def wrapcommit(orig, *args, **kwargs):
14 def wrapcommit(orig, *args, **kwargs):
15 result = orig(*args, **kwargs)
15 result = orig(*args, **kwargs)
16 tip1 = node.short(repo.changelog.tip())
16 tip1 = node.short(repo.changelog.tip())
17 tip2 = node.short(repo.lookup(tip1))
17 tip2 = node.short(repo.lookup(tip1))
18 assert tip1 == tip2
18 assert tip1 == tip2
19 ui.write('new tip: %s\n' % tip1)
19 ui.write('new tip: %s\n' % tip1)
20 return result
20 return result
21
21
22 extensions.wrapfunction(repo, 'commit', wrapcommit)
22 extensions.wrapfunction(repo, 'commit', wrapcommit)
23
23
24 def extsetup(ui):
24 def extsetup(ui):
25 revlog._maxinline = 8 # split out 00changelog.d early
25 revlog._maxinline = 8 # split out 00changelog.d early
26 revlog._prereadsize = 8 # use revlog.lazyparser
26 revlog._prereadsize = 8 # use revlog.lazyparser
27 EOF
27 EOF
28
28
29 cat >> $HGRCPATH <<EOF
29 cat >> $HGRCPATH <<EOF
30 [extensions]
30 [extensions]
31 commitwrapper = $PWD/commitwrapper.py
31 commitwrapper = `pwd`/commitwrapper.py
32 EOF
32 EOF
33
33
34 hg init repo1
34 hg init repo1
35 cd repo1
35 cd repo1
36 echo a > a
36 echo a > a
37 hg commit -A -m'add a with a long commit message to make the changelog a bit bigger'
37 hg commit -A -m'add a with a long commit message to make the changelog a bit bigger'
38
38
39 echo ""
39 echo ""
40 echo "% test that new changesets are visible to repo.lookup()"
40 echo "% test that new changesets are visible to repo.lookup()"
41 echo a >> a
41 echo a >> a
42 hg commit -m'one more commit to demonstrate the bug'
42 hg commit -m'one more commit to demonstrate the bug'
43 hg tip
43 hg tip
@@ -1,79 +1,79 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 # Test issue 529 - mq aborts when merging patch deleting files
3 # Test issue 529 - mq aborts when merging patch deleting files
4
4
5 checkundo()
5 checkundo()
6 {
6 {
7 if [ -f .hg/store/undo ]; then
7 if [ -f .hg/store/undo ]; then
8 echo ".hg/store/undo still exists after $1"
8 echo ".hg/store/undo still exists after $1"
9 fi
9 fi
10 }
10 }
11
11
12 echo "[extensions]" >> $HGRCPATH
12 echo "[extensions]" >> $HGRCPATH
13 echo "mq =" >> $HGRCPATH
13 echo "mq =" >> $HGRCPATH
14 echo "[mq]" >> $HGRCPATH
14 echo "[mq]" >> $HGRCPATH
15 echo "git = keep" >> $HGRCPATH
15 echo "git = keep" >> $HGRCPATH
16
16
17 # Commit two dummy files in "init" changeset
17 # Commit two dummy files in "init" changeset
18 hg init t
18 hg init t
19 cd t
19 cd t
20 echo a > a
20 echo a > a
21 echo b > b
21 echo b > b
22 hg ci -Am init
22 hg ci -Am init
23 hg tag -l init
23 hg tag -l init
24
24
25 # Create a patch removing a
25 # Create a patch removing a
26 hg qnew rm_a
26 hg qnew rm_a
27 hg rm a
27 hg rm a
28 hg qrefresh -m "rm a"
28 hg qrefresh -m "rm a"
29
29
30 # Save the patch queue so we can merge it later
30 # Save the patch queue so we can merge it later
31 hg qsave -c -e 2>&1 | grep -v ^copy
31 hg qsave -c -e 2>&1 | grep -v '^copy'
32 checkundo qsave
32 checkundo qsave
33
33
34 # Update b and commit in an "update" changeset
34 # Update b and commit in an "update" changeset
35 hg up -C init
35 hg up -C init
36 echo b >> b
36 echo b >> b
37 hg st
37 hg st
38 hg ci -m update
38 hg ci -m update
39
39
40 # Here, qpush used to abort with :
40 # Here, qpush used to abort with :
41 # The system cannot find the file specified => a
41 # The system cannot find the file specified => a
42 hg manifest
42 hg manifest
43 hg qpush -a -m 2>&1 | grep -v ^merging
43 hg qpush -a -m 2>&1 | grep -v '^merging'
44 checkundo 'qpush -m'
44 checkundo 'qpush -m'
45 hg manifest
45 hg manifest
46
46
47 # ensure status is correct after merge
47 # ensure status is correct after merge
48 hg qpop -a
48 hg qpop -a
49 cd ..
49 cd ..
50
50
51 # Classic MQ merge sequence *with an explicit named queue*
51 # Classic MQ merge sequence *with an explicit named queue*
52 echo
52 echo
53 echo % init t2
53 echo % init t2
54 hg init t2
54 hg init t2
55 cd t2
55 cd t2
56 echo '[diff]' > .hg/hgrc
56 echo '[diff]' > .hg/hgrc
57 echo 'nodates = 1' >> .hg/hgrc
57 echo 'nodates = 1' >> .hg/hgrc
58 echo a > a
58 echo a > a
59 hg ci -Am init
59 hg ci -Am init
60 echo b > a
60 echo b > a
61 hg ci -m changea
61 hg ci -m changea
62 hg up -C 0
62 hg up -C 0
63 hg cp a aa
63 hg cp a aa
64 echo c >> a
64 echo c >> a
65 hg qnew --git -f -e patcha
65 hg qnew --git -f -e patcha
66 echo d >> a
66 echo d >> a
67 hg qnew -d '0 0' -f -e patcha2
67 hg qnew -d '0 0' -f -e patcha2
68 echo % create the reference queue
68 echo % create the reference queue
69 hg qsave -c -e -n refqueue 2> /dev/null
69 hg qsave -c -e -n refqueue 2> /dev/null
70 hg up -C 1
70 hg up -C 1
71 echo % merge
71 echo % merge
72 HGMERGE=internal:other hg qpush -a -m -n refqueue 2>&1 | \
72 HGMERGE=internal:other hg qpush -a -m -n refqueue 2>&1 | \
73 sed 's/merging with queue at.*refqueue/merging with queue at refqueue/'
73 sed 's/merging with queue at.*refqueue/merging with queue at refqueue/'
74 echo % check patcha is still a git patch
74 echo % check patcha is still a git patch
75 cat .hg/patches/patcha
75 cat .hg/patches/patcha
76 echo % check patcha2 is still a regular patch
76 echo % check patcha2 is still a regular patch
77 grep git .hg/patches/patcha2 && echo 'git patch found!'
77 grep git .hg/patches/patcha2 && echo 'git patch found!'
78 cd ..
78 cd ..
79
79
@@ -1,649 +1,649 b''
1 % help
1 % help
2 mq extension - manage a stack of patches
2 mq extension - manage a stack of patches
3
3
4 This extension lets you work with a stack of patches in a Mercurial
4 This extension lets you work with a stack of patches in a Mercurial
5 repository. It manages two stacks of patches - all known patches, and applied
5 repository. It manages two stacks of patches - all known patches, and applied
6 patches (subset of known patches).
6 patches (subset of known patches).
7
7
8 Known patches are represented as patch files in the .hg/patches directory.
8 Known patches are represented as patch files in the .hg/patches directory.
9 Applied patches are both patch files and changesets.
9 Applied patches are both patch files and changesets.
10
10
11 Common tasks (use "hg help command" for more details):
11 Common tasks (use "hg help command" for more details):
12
12
13 create new patch qnew
13 create new patch qnew
14 import existing patch qimport
14 import existing patch qimport
15
15
16 print patch series qseries
16 print patch series qseries
17 print applied patches qapplied
17 print applied patches qapplied
18
18
19 add known patch to applied stack qpush
19 add known patch to applied stack qpush
20 remove patch from applied stack qpop
20 remove patch from applied stack qpop
21 refresh contents of top applied patch qrefresh
21 refresh contents of top applied patch qrefresh
22
22
23 By default, mq will automatically use git patches when required to avoid
23 By default, mq will automatically use git patches when required to avoid
24 losing file mode changes, copy records, binary files or empty files creations
24 losing file mode changes, copy records, binary files or empty files creations
25 or deletions. This behaviour can be configured with:
25 or deletions. This behaviour can be configured with:
26
26
27 [mq]
27 [mq]
28 git = auto/keep/yes/no
28 git = auto/keep/yes/no
29
29
30 If set to 'keep', mq will obey the [diff] section configuration while
30 If set to 'keep', mq will obey the [diff] section configuration while
31 preserving existing git patches upon qrefresh. If set to 'yes' or 'no', mq
31 preserving existing git patches upon qrefresh. If set to 'yes' or 'no', mq
32 will override the [diff] section and always generate git or regular patches,
32 will override the [diff] section and always generate git or regular patches,
33 possibly losing data in the second case.
33 possibly losing data in the second case.
34
34
35 list of commands:
35 list of commands:
36
36
37 qapplied print the patches already applied
37 qapplied print the patches already applied
38 qclone clone main and patch repository at same time
38 qclone clone main and patch repository at same time
39 qdelete remove patches from queue
39 qdelete remove patches from queue
40 qdiff diff of the current patch and subsequent modifications
40 qdiff diff of the current patch and subsequent modifications
41 qfinish move applied patches into repository history
41 qfinish move applied patches into repository history
42 qfold fold the named patches into the current patch
42 qfold fold the named patches into the current patch
43 qgoto push or pop patches until named patch is at top of stack
43 qgoto push or pop patches until named patch is at top of stack
44 qguard set or print guards for a patch
44 qguard set or print guards for a patch
45 qheader print the header of the topmost or specified patch
45 qheader print the header of the topmost or specified patch
46 qimport import a patch
46 qimport import a patch
47 qnew create a new patch
47 qnew create a new patch
48 qnext print the name of the next patch
48 qnext print the name of the next patch
49 qpop pop the current patch off the stack
49 qpop pop the current patch off the stack
50 qprev print the name of the previous patch
50 qprev print the name of the previous patch
51 qpush push the next patch onto the stack
51 qpush push the next patch onto the stack
52 qrefresh update the current patch
52 qrefresh update the current patch
53 qrename rename a patch
53 qrename rename a patch
54 qselect set or print guarded patches to push
54 qselect set or print guarded patches to push
55 qseries print the entire series file
55 qseries print the entire series file
56 qtop print the name of the current patch
56 qtop print the name of the current patch
57 qunapplied print the patches not yet applied
57 qunapplied print the patches not yet applied
58 strip strip a revision and all its descendants from the repository
58 strip strip a changeset and all its descendants from the repository
59
59
60 use "hg -v help mq" to show aliases and global options
60 use "hg -v help mq" to show aliases and global options
61 adding a
61 adding a
62 updating to branch default
62 updating to branch default
63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 adding b/z
64 adding b/z
65 % qinit
65 % qinit
66 % -R qinit
66 % -R qinit
67 % qinit -c
67 % qinit -c
68 A .hgignore
68 A .hgignore
69 A series
69 A series
70 % qinit; qinit -c
70 % qinit; qinit -c
71 .hgignore:
71 .hgignore:
72 ^\.hg
72 ^\.hg
73 ^\.mq
73 ^\.mq
74 syntax: glob
74 syntax: glob
75 status
75 status
76 guards
76 guards
77 series:
77 series:
78 abort: repository already exists!
78 abort: repository already exists!
79 % qinit; <stuff>; qinit -c
79 % qinit; <stuff>; qinit -c
80 adding .hg/patches/A
80 adding .hg/patches/A
81 adding .hg/patches/B
81 adding .hg/patches/B
82 A .hgignore
82 A .hgignore
83 A A
83 A A
84 A B
84 A B
85 A series
85 A series
86 .hgignore:
86 .hgignore:
87 status
87 status
88 bleh
88 bleh
89 series:
89 series:
90 A
90 A
91 B
91 B
92 % init --mq without repo
92 % init --mq without repo
93 abort: There is no Mercurial repository here (.hg not found)
93 abort: There is no Mercurial repository here (.hg not found)
94 % init --mq with repo path
94 % init --mq with repo path
95 ok
95 ok
96 % init --mq with nonexistent directory
96 % init --mq with nonexistent directory
97 abort: repository nonexistentdir not found!
97 abort: repository nonexistentdir not found!
98 % init --mq with bundle (non "local")
98 % init --mq with bundle (non "local")
99 abort: only a local queue repository may be initialized
99 abort: only a local queue repository may be initialized
100 % qrefresh
100 % qrefresh
101 foo bar
101 foo bar
102
102
103 diff -r xa
103 diff -r xa
104 --- a/a
104 --- a/a
105 +++ b/a
105 +++ b/a
106 @@ -1,1 +1,2 @@
106 @@ -1,1 +1,2 @@
107 a
107 a
108 +a
108 +a
109 % empty qrefresh
109 % empty qrefresh
110 revision:
110 revision:
111 patch:
111 patch:
112 foo bar
112 foo bar
113
113
114 working dir diff:
114 working dir diff:
115 --- a/a
115 --- a/a
116 +++ b/a
116 +++ b/a
117 @@ -1,1 +1,2 @@
117 @@ -1,1 +1,2 @@
118 a
118 a
119 +a
119 +a
120 % qpop
120 % qpop
121 popping test.patch
121 popping test.patch
122 patch queue now empty
122 patch queue now empty
123 % qpush with dump of tag cache
123 % qpush with dump of tag cache
124 .hg/tags.cache (pre qpush):
124 .hg/tags.cache (pre qpush):
125 1
125 1
126
126
127 applying test.patch
127 applying test.patch
128 now at: test.patch
128 now at: test.patch
129 .hg/tags.cache (post qpush):
129 .hg/tags.cache (post qpush):
130 2
130 2
131
131
132 % pop/push outside repo
132 % pop/push outside repo
133 popping test.patch
133 popping test.patch
134 patch queue now empty
134 patch queue now empty
135 applying test.patch
135 applying test.patch
136 now at: test.patch
136 now at: test.patch
137 % qrefresh in subdir
137 % qrefresh in subdir
138 % pop/push -a in subdir
138 % pop/push -a in subdir
139 popping test2.patch
139 popping test2.patch
140 popping test.patch
140 popping test.patch
141 patch queue now empty
141 patch queue now empty
142 applying test.patch
142 applying test.patch
143 applying test2.patch
143 applying test2.patch
144 now at: test2.patch
144 now at: test2.patch
145 % qseries
145 % qseries
146 test.patch
146 test.patch
147 test2.patch
147 test2.patch
148 0 A test.patch: f...
148 0 A test.patch: f...
149 1 A test2.patch:
149 1 A test2.patch:
150 popping test2.patch
150 popping test2.patch
151 now at: test.patch
151 now at: test.patch
152 0 A test.patch: foo bar
152 0 A test.patch: foo bar
153 1 U test2.patch:
153 1 U test2.patch:
154 applying test2.patch
154 applying test2.patch
155 now at: test2.patch
155 now at: test2.patch
156 % qapplied
156 % qapplied
157 test.patch
157 test.patch
158 test2.patch
158 test2.patch
159 % qtop
159 % qtop
160 test2.patch
160 test2.patch
161 % prev
161 % prev
162 test.patch
162 test.patch
163 % next
163 % next
164 all patches applied
164 all patches applied
165 popping test2.patch
165 popping test2.patch
166 now at: test.patch
166 now at: test.patch
167 % commit should fail
167 % commit should fail
168 abort: cannot commit over an applied mq patch
168 abort: cannot commit over an applied mq patch
169 % push should fail
169 % push should fail
170 pushing to ../../k
170 pushing to ../../k
171 abort: source has mq patches applied
171 abort: source has mq patches applied
172 % import should fail
172 % import should fail
173 abort: cannot import over an applied patch
173 abort: cannot import over an applied patch
174 % import --no-commit should succeed
174 % import --no-commit should succeed
175 applying ../../import.diff
175 applying ../../import.diff
176 M a
176 M a
177 % qunapplied
177 % qunapplied
178 test2.patch
178 test2.patch
179 % qpush/qpop with index
179 % qpush/qpop with index
180 applying test2.patch
180 applying test2.patch
181 now at: test2.patch
181 now at: test2.patch
182 popping test2.patch
182 popping test2.patch
183 popping test1b.patch
183 popping test1b.patch
184 now at: test.patch
184 now at: test.patch
185 applying test1b.patch
185 applying test1b.patch
186 now at: test1b.patch
186 now at: test1b.patch
187 applying test2.patch
187 applying test2.patch
188 now at: test2.patch
188 now at: test2.patch
189 popping test2.patch
189 popping test2.patch
190 now at: test1b.patch
190 now at: test1b.patch
191 popping test1b.patch
191 popping test1b.patch
192 now at: test.patch
192 now at: test.patch
193 applying test1b.patch
193 applying test1b.patch
194 applying test2.patch
194 applying test2.patch
195 now at: test2.patch
195 now at: test2.patch
196 % qpush --move
196 % qpush --move
197 popping test2.patch
197 popping test2.patch
198 popping test1b.patch
198 popping test1b.patch
199 popping test.patch
199 popping test.patch
200 patch queue now empty
200 patch queue now empty
201 applying test2.patch
201 applying test2.patch
202 now at: test2.patch
202 now at: test2.patch
203 applying test1b.patch
203 applying test1b.patch
204 now at: test1b.patch
204 now at: test1b.patch
205 applying test.patch
205 applying test.patch
206 now at: test.patch
206 now at: test.patch
207 0 A test2.patch
207 0 A test2.patch
208 1 A test1b.patch
208 1 A test1b.patch
209 2 A test.patch
209 2 A test.patch
210 popping test.patch
210 popping test.patch
211 popping test1b.patch
211 popping test1b.patch
212 popping test2.patch
212 popping test2.patch
213 patch queue now empty
213 patch queue now empty
214 applying test.patch
214 applying test.patch
215 now at: test.patch
215 now at: test.patch
216 applying test1b.patch
216 applying test1b.patch
217 now at: test1b.patch
217 now at: test1b.patch
218 abort: patch bogus not in series
218 abort: patch bogus not in series
219 abort: cannot push to a previous patch: test.patch
219 abort: cannot push to a previous patch: test.patch
220 applying test2.patch
220 applying test2.patch
221 now at: test2.patch
221 now at: test2.patch
222 % pop, qapplied, qunapplied
222 % pop, qapplied, qunapplied
223 0 A test.patch
223 0 A test.patch
224 1 A test1b.patch
224 1 A test1b.patch
225 2 A test2.patch
225 2 A test2.patch
226 % qapplied -1 test.patch
226 % qapplied -1 test.patch
227 only one patch applied
227 only one patch applied
228 % qapplied -1 test1b.patch
228 % qapplied -1 test1b.patch
229 test.patch
229 test.patch
230 % qapplied -1 test2.patch
230 % qapplied -1 test2.patch
231 test1b.patch
231 test1b.patch
232 % qapplied -1
232 % qapplied -1
233 test1b.patch
233 test1b.patch
234 % qapplied
234 % qapplied
235 test.patch
235 test.patch
236 test1b.patch
236 test1b.patch
237 test2.patch
237 test2.patch
238 % qapplied test1b.patch
238 % qapplied test1b.patch
239 test.patch
239 test.patch
240 test1b.patch
240 test1b.patch
241 % qunapplied -1
241 % qunapplied -1
242 all patches applied
242 all patches applied
243 % qunapplied
243 % qunapplied
244 % popping
244 % popping
245 popping test2.patch
245 popping test2.patch
246 now at: test1b.patch
246 now at: test1b.patch
247 % qunapplied -1
247 % qunapplied -1
248 test2.patch
248 test2.patch
249 % qunapplied
249 % qunapplied
250 test2.patch
250 test2.patch
251 % qunapplied test2.patch
251 % qunapplied test2.patch
252 % qunapplied -1 test2.patch
252 % qunapplied -1 test2.patch
253 all patches applied
253 all patches applied
254 % popping -a
254 % popping -a
255 popping test1b.patch
255 popping test1b.patch
256 popping test.patch
256 popping test.patch
257 patch queue now empty
257 patch queue now empty
258 % qapplied
258 % qapplied
259 % qapplied -1
259 % qapplied -1
260 no patches applied
260 no patches applied
261 applying test.patch
261 applying test.patch
262 now at: test.patch
262 now at: test.patch
263 % push should succeed
263 % push should succeed
264 popping test.patch
264 popping test.patch
265 patch queue now empty
265 patch queue now empty
266 pushing to ../../k
266 pushing to ../../k
267 searching for changes
267 searching for changes
268 adding changesets
268 adding changesets
269 adding manifests
269 adding manifests
270 adding file changes
270 adding file changes
271 added 1 changesets with 1 changes to 1 files
271 added 1 changesets with 1 changes to 1 files
272 % qpush/qpop error codes
272 % qpush/qpop error codes
273 applying test.patch
273 applying test.patch
274 applying test1b.patch
274 applying test1b.patch
275 applying test2.patch
275 applying test2.patch
276 now at: test2.patch
276 now at: test2.patch
277 % pops all patches and succeeds
277 % pops all patches and succeeds
278 popping test2.patch
278 popping test2.patch
279 popping test1b.patch
279 popping test1b.patch
280 popping test.patch
280 popping test.patch
281 patch queue now empty
281 patch queue now empty
282 qpop -a succeeds
282 qpop -a succeeds
283 % does nothing and succeeds
283 % does nothing and succeeds
284 no patches applied
284 no patches applied
285 qpop -a succeeds
285 qpop -a succeeds
286 % fails - nothing else to pop
286 % fails - nothing else to pop
287 no patches applied
287 no patches applied
288 qpop fails
288 qpop fails
289 % pushes a patch and succeeds
289 % pushes a patch and succeeds
290 applying test.patch
290 applying test.patch
291 now at: test.patch
291 now at: test.patch
292 qpush succeeds
292 qpush succeeds
293 % pops a patch and succeeds
293 % pops a patch and succeeds
294 popping test.patch
294 popping test.patch
295 patch queue now empty
295 patch queue now empty
296 qpop succeeds
296 qpop succeeds
297 % pushes up to test1b.patch and succeeds
297 % pushes up to test1b.patch and succeeds
298 applying test.patch
298 applying test.patch
299 applying test1b.patch
299 applying test1b.patch
300 now at: test1b.patch
300 now at: test1b.patch
301 qpush test1b.patch succeeds
301 qpush test1b.patch succeeds
302 % does nothing and succeeds
302 % does nothing and succeeds
303 qpush: test1b.patch is already at the top
303 qpush: test1b.patch is already at the top
304 qpush test1b.patch succeeds
304 qpush test1b.patch succeeds
305 % does nothing and succeeds
305 % does nothing and succeeds
306 qpop: test1b.patch is already at the top
306 qpop: test1b.patch is already at the top
307 qpop test1b.patch succeeds
307 qpop test1b.patch succeeds
308 % fails - can't push to this patch
308 % fails - can't push to this patch
309 abort: cannot push to a previous patch: test.patch
309 abort: cannot push to a previous patch: test.patch
310 qpush test.patch fails
310 qpush test.patch fails
311 % fails - can't pop to this patch
311 % fails - can't pop to this patch
312 abort: patch test2.patch is not applied
312 abort: patch test2.patch is not applied
313 qpop test2.patch fails
313 qpop test2.patch fails
314 % pops up to test.patch and succeeds
314 % pops up to test.patch and succeeds
315 popping test1b.patch
315 popping test1b.patch
316 now at: test.patch
316 now at: test.patch
317 qpop test.patch succeeds
317 qpop test.patch succeeds
318 % pushes all patches and succeeds
318 % pushes all patches and succeeds
319 applying test1b.patch
319 applying test1b.patch
320 applying test2.patch
320 applying test2.patch
321 now at: test2.patch
321 now at: test2.patch
322 qpush -a succeeds
322 qpush -a succeeds
323 % does nothing and succeeds
323 % does nothing and succeeds
324 all patches are currently applied
324 all patches are currently applied
325 qpush -a succeeds
325 qpush -a succeeds
326 % fails - nothing else to push
326 % fails - nothing else to push
327 patch series already fully applied
327 patch series already fully applied
328 qpush fails
328 qpush fails
329 % does nothing and succeeds
329 % does nothing and succeeds
330 qpush: test2.patch is already at the top
330 qpush: test2.patch is already at the top
331 qpush test2.patch succeeds
331 qpush test2.patch succeeds
332 % strip
332 % strip
333 adding x
333 adding x
334 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
334 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
335 saving bundle to
335 saving bundle to
336 adding changesets
336 adding changesets
337 adding manifests
337 adding manifests
338 adding file changes
338 adding file changes
339 added 1 changesets with 1 changes to 1 files
339 added 1 changesets with 1 changes to 1 files
340 (run 'hg update' to get a working copy)
340 (run 'hg update' to get a working copy)
341 % strip with local changes, should complain
341 % strip with local changes, should complain
342 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
342 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 abort: local changes found
343 abort: local changes found
344 % --force strip with local changes
344 % --force strip with local changes
345 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
345 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
346 saving bundle to
346 saving bundle to
347 % cd b; hg qrefresh
347 % cd b; hg qrefresh
348 adding a
348 adding a
349 foo
349 foo
350
350
351 diff -r cb9a9f314b8b a
351 diff -r cb9a9f314b8b a
352 --- a/a
352 --- a/a
353 +++ b/a
353 +++ b/a
354 @@ -1,1 +1,2 @@
354 @@ -1,1 +1,2 @@
355 a
355 a
356 +a
356 +a
357 diff -r cb9a9f314b8b b/f
357 diff -r cb9a9f314b8b b/f
358 --- /dev/null
358 --- /dev/null
359 +++ b/b/f
359 +++ b/b/f
360 @@ -0,0 +1,1 @@
360 @@ -0,0 +1,1 @@
361 +f
361 +f
362 % hg qrefresh .
362 % hg qrefresh .
363 foo
363 foo
364
364
365 diff -r cb9a9f314b8b b/f
365 diff -r cb9a9f314b8b b/f
366 --- /dev/null
366 --- /dev/null
367 +++ b/b/f
367 +++ b/b/f
368 @@ -0,0 +1,1 @@
368 @@ -0,0 +1,1 @@
369 +f
369 +f
370 M a
370 M a
371 % qpush failure
371 % qpush failure
372 popping bar
372 popping bar
373 popping foo
373 popping foo
374 patch queue now empty
374 patch queue now empty
375 applying foo
375 applying foo
376 applying bar
376 applying bar
377 file foo already exists
377 file foo already exists
378 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
378 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
379 patch failed, unable to continue (try -v)
379 patch failed, unable to continue (try -v)
380 patch failed, rejects left in working dir
380 patch failed, rejects left in working dir
381 errors during apply, please fix and refresh bar
381 errors during apply, please fix and refresh bar
382 ? foo
382 ? foo
383 ? foo.rej
383 ? foo.rej
384 % mq tags
384 % mq tags
385 0 qparent
385 0 qparent
386 1 foo qbase
386 1 foo qbase
387 2 bar qtip tip
387 2 bar qtip tip
388 % bad node in status
388 % bad node in status
389 popping bar
389 popping bar
390 now at: foo
390 now at: foo
391 changeset: 0:cb9a9f314b8b
391 changeset: 0:cb9a9f314b8b
392 mq status file refers to unknown node
392 mq status file refers to unknown node
393 tag: tip
393 tag: tip
394 user: test
394 user: test
395 date: Thu Jan 01 00:00:00 1970 +0000
395 date: Thu Jan 01 00:00:00 1970 +0000
396 summary: a
396 summary: a
397
397
398 mq status file refers to unknown node
398 mq status file refers to unknown node
399 default 0:cb9a9f314b8b
399 default 0:cb9a9f314b8b
400 abort: trying to pop unknown node
400 abort: trying to pop unknown node
401 new file
401 new file
402
402
403 diff --git a/new b/new
403 diff --git a/new b/new
404 new file mode 100755
404 new file mode 100755
405 --- /dev/null
405 --- /dev/null
406 +++ b/new
406 +++ b/new
407 @@ -0,0 +1,1 @@
407 @@ -0,0 +1,1 @@
408 +foo
408 +foo
409 copy file
409 copy file
410
410
411 diff --git a/new b/copy
411 diff --git a/new b/copy
412 copy from new
412 copy from new
413 copy to copy
413 copy to copy
414 popping copy
414 popping copy
415 now at: new
415 now at: new
416 applying copy
416 applying copy
417 now at: copy
417 now at: copy
418 diff --git a/new b/copy
418 diff --git a/new b/copy
419 copy from new
419 copy from new
420 copy to copy
420 copy to copy
421 diff --git a/new b/copy
421 diff --git a/new b/copy
422 copy from new
422 copy from new
423 copy to copy
423 copy to copy
424 % test file addition in slow path
424 % test file addition in slow path
425 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
425 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
426 created new head
426 created new head
427 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
427 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
428 adding branch
428 adding branch
429 adding changesets
429 adding changesets
430 adding manifests
430 adding manifests
431 adding file changes
431 adding file changes
432 added 1 changesets with 1 changes to 1 files
432 added 1 changesets with 1 changes to 1 files
433 diff --git a/bar b/bar
433 diff --git a/bar b/bar
434 new file mode 100644
434 new file mode 100644
435 --- /dev/null
435 --- /dev/null
436 +++ b/bar
436 +++ b/bar
437 @@ -0,0 +1,1 @@
437 @@ -0,0 +1,1 @@
438 +bar
438 +bar
439 diff --git a/foo b/baz
439 diff --git a/foo b/baz
440 rename from foo
440 rename from foo
441 rename to baz
441 rename to baz
442 2 baz (foo)
442 2 baz (foo)
443 diff --git a/bar b/bar
443 diff --git a/bar b/bar
444 new file mode 100644
444 new file mode 100644
445 --- /dev/null
445 --- /dev/null
446 +++ b/bar
446 +++ b/bar
447 @@ -0,0 +1,1 @@
447 @@ -0,0 +1,1 @@
448 +bar
448 +bar
449 diff --git a/foo b/baz
449 diff --git a/foo b/baz
450 rename from foo
450 rename from foo
451 rename to baz
451 rename to baz
452 2 baz (foo)
452 2 baz (foo)
453 diff --git a/bar b/bar
453 diff --git a/bar b/bar
454 diff --git a/foo b/baz
454 diff --git a/foo b/baz
455 % test file move chains in the slow path
455 % test file move chains in the slow path
456 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
456 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
457 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
457 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
458 adding branch
458 adding branch
459 adding changesets
459 adding changesets
460 adding manifests
460 adding manifests
461 adding file changes
461 adding file changes
462 added 1 changesets with 1 changes to 1 files
462 added 1 changesets with 1 changes to 1 files
463 diff --git a/foo b/bleh
463 diff --git a/foo b/bleh
464 rename from foo
464 rename from foo
465 rename to bleh
465 rename to bleh
466 diff --git a/quux b/quux
466 diff --git a/quux b/quux
467 new file mode 100644
467 new file mode 100644
468 --- /dev/null
468 --- /dev/null
469 +++ b/quux
469 +++ b/quux
470 @@ -0,0 +1,1 @@
470 @@ -0,0 +1,1 @@
471 +bar
471 +bar
472 3 bleh (foo)
472 3 bleh (foo)
473 diff --git a/foo b/barney
473 diff --git a/foo b/barney
474 rename from foo
474 rename from foo
475 rename to barney
475 rename to barney
476 diff --git a/fred b/fred
476 diff --git a/fred b/fred
477 new file mode 100644
477 new file mode 100644
478 --- /dev/null
478 --- /dev/null
479 +++ b/fred
479 +++ b/fred
480 @@ -0,0 +1,1 @@
480 @@ -0,0 +1,1 @@
481 +bar
481 +bar
482 3 barney (foo)
482 3 barney (foo)
483 % refresh omitting an added file
483 % refresh omitting an added file
484 C newfile
484 C newfile
485 A newfile
485 A newfile
486 popping baz
486 popping baz
487 now at: bar
487 now at: bar
488 % create a git patch
488 % create a git patch
489 diff --git a/alexander b/alexander
489 diff --git a/alexander b/alexander
490 % create a git binary patch
490 % create a git binary patch
491 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
491 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
492 diff --git a/bucephalus b/bucephalus
492 diff --git a/bucephalus b/bucephalus
493 % check binary patches can be popped and pushed
493 % check binary patches can be popped and pushed
494 popping addbucephalus
494 popping addbucephalus
495 now at: addalexander
495 now at: addalexander
496 applying addbucephalus
496 applying addbucephalus
497 now at: addbucephalus
497 now at: addbucephalus
498 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
498 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
499 % strip again
499 % strip again
500 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
500 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
501 created new head
501 created new head
502 merging foo
502 merging foo
503 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
503 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
504 (branch merge, don't forget to commit)
504 (branch merge, don't forget to commit)
505 changeset: 3:99615015637b
505 changeset: 3:99615015637b
506 tag: tip
506 tag: tip
507 parent: 2:20cbbe65cff7
507 parent: 2:20cbbe65cff7
508 parent: 1:d2871fc282d4
508 parent: 1:d2871fc282d4
509 user: test
509 user: test
510 date: Thu Jan 01 00:00:00 1970 +0000
510 date: Thu Jan 01 00:00:00 1970 +0000
511 summary: merge
511 summary: merge
512
512
513 changeset: 2:20cbbe65cff7
513 changeset: 2:20cbbe65cff7
514 parent: 0:53245c60e682
514 parent: 0:53245c60e682
515 user: test
515 user: test
516 date: Thu Jan 01 00:00:00 1970 +0000
516 date: Thu Jan 01 00:00:00 1970 +0000
517 summary: change foo 2
517 summary: change foo 2
518
518
519 changeset: 1:d2871fc282d4
519 changeset: 1:d2871fc282d4
520 user: test
520 user: test
521 date: Thu Jan 01 00:00:00 1970 +0000
521 date: Thu Jan 01 00:00:00 1970 +0000
522 summary: change foo 1
522 summary: change foo 1
523
523
524 changeset: 0:53245c60e682
524 changeset: 0:53245c60e682
525 user: test
525 user: test
526 date: Thu Jan 01 00:00:00 1970 +0000
526 date: Thu Jan 01 00:00:00 1970 +0000
527 summary: add foo
527 summary: add foo
528
528
529 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
529 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
530 saving bundle to
530 saving bundle to
531 saving bundle to
531 saving bundle to
532 adding branch
532 adding branch
533 adding changesets
533 adding changesets
534 adding manifests
534 adding manifests
535 adding file changes
535 adding file changes
536 added 1 changesets with 1 changes to 1 files
536 added 1 changesets with 1 changes to 1 files
537 changeset: 1:20cbbe65cff7
537 changeset: 1:20cbbe65cff7
538 tag: tip
538 tag: tip
539 user: test
539 user: test
540 date: Thu Jan 01 00:00:00 1970 +0000
540 date: Thu Jan 01 00:00:00 1970 +0000
541 summary: change foo 2
541 summary: change foo 2
542
542
543 changeset: 0:53245c60e682
543 changeset: 0:53245c60e682
544 user: test
544 user: test
545 date: Thu Jan 01 00:00:00 1970 +0000
545 date: Thu Jan 01 00:00:00 1970 +0000
546 summary: add foo
546 summary: add foo
547
547
548 % qclone
548 % qclone
549 abort: versioned patch repository not found (see init --mq)
549 abort: versioned patch repository not found (see init --mq)
550 adding .hg/patches/patch1
550 adding .hg/patches/patch1
551 main repo:
551 main repo:
552 rev 1: change foo
552 rev 1: change foo
553 rev 0: add foo
553 rev 0: add foo
554 patch repo:
554 patch repo:
555 rev 0: checkpoint
555 rev 0: checkpoint
556 updating to branch default
556 updating to branch default
557 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
557 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
558 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
558 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
559 main repo:
559 main repo:
560 rev 0: add foo
560 rev 0: add foo
561 patch repo:
561 patch repo:
562 rev 0: checkpoint
562 rev 0: checkpoint
563 popping patch1
563 popping patch1
564 patch queue now empty
564 patch queue now empty
565 main repo:
565 main repo:
566 rev 0: add foo
566 rev 0: add foo
567 patch repo:
567 patch repo:
568 rev 0: checkpoint
568 rev 0: checkpoint
569 updating to branch default
569 updating to branch default
570 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
570 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
572 main repo:
572 main repo:
573 rev 0: add foo
573 rev 0: add foo
574 patch repo:
574 patch repo:
575 rev 0: checkpoint
575 rev 0: checkpoint
576 % test applying on an empty file (issue 1033)
576 % test applying on an empty file (issue 1033)
577 adding a
577 adding a
578 popping changea
578 popping changea
579 patch queue now empty
579 patch queue now empty
580 applying changea
580 applying changea
581 now at: changea
581 now at: changea
582 % test qpush with --force, issue1087
582 % test qpush with --force, issue1087
583 adding bye.txt
583 adding bye.txt
584 adding hello.txt
584 adding hello.txt
585 popping empty
585 popping empty
586 patch queue now empty
586 patch queue now empty
587 % qpush should fail, local changes
587 % qpush should fail, local changes
588 abort: local changes found, refresh first
588 abort: local changes found, refresh first
589 % apply force, should not discard changes with empty patch
589 % apply force, should not discard changes with empty patch
590 applying empty
590 applying empty
591 patch empty is empty
591 patch empty is empty
592 now at: empty
592 now at: empty
593 diff -r bf5fc3f07a0a hello.txt
593 diff -r bf5fc3f07a0a hello.txt
594 --- a/hello.txt
594 --- a/hello.txt
595 +++ b/hello.txt
595 +++ b/hello.txt
596 @@ -1,1 +1,2 @@
596 @@ -1,1 +1,2 @@
597 hello
597 hello
598 +world
598 +world
599 diff -r 9ecee4f634e3 hello.txt
599 diff -r 9ecee4f634e3 hello.txt
600 --- a/hello.txt
600 --- a/hello.txt
601 +++ b/hello.txt
601 +++ b/hello.txt
602 @@ -1,1 +1,2 @@
602 @@ -1,1 +1,2 @@
603 hello
603 hello
604 +world
604 +world
605 changeset: 1:bf5fc3f07a0a
605 changeset: 1:bf5fc3f07a0a
606 tag: empty
606 tag: empty
607 tag: qbase
607 tag: qbase
608 tag: qtip
608 tag: qtip
609 tag: tip
609 tag: tip
610 user: test
610 user: test
611 date: Thu Jan 01 00:00:00 1970 +0000
611 date: Thu Jan 01 00:00:00 1970 +0000
612 summary: imported patch empty
612 summary: imported patch empty
613
613
614
614
615 popping empty
615 popping empty
616 patch queue now empty
616 patch queue now empty
617 % qpush should fail, local changes
617 % qpush should fail, local changes
618 abort: local changes found, refresh first
618 abort: local changes found, refresh first
619 % apply force, should discard changes in hello, but not bye
619 % apply force, should discard changes in hello, but not bye
620 applying empty
620 applying empty
621 now at: empty
621 now at: empty
622 M bye.txt
622 M bye.txt
623 diff -r ba252371dbc1 bye.txt
623 diff -r ba252371dbc1 bye.txt
624 --- a/bye.txt
624 --- a/bye.txt
625 +++ b/bye.txt
625 +++ b/bye.txt
626 @@ -1,1 +1,2 @@
626 @@ -1,1 +1,2 @@
627 bye
627 bye
628 +universe
628 +universe
629 diff -r 9ecee4f634e3 bye.txt
629 diff -r 9ecee4f634e3 bye.txt
630 --- a/bye.txt
630 --- a/bye.txt
631 +++ b/bye.txt
631 +++ b/bye.txt
632 @@ -1,1 +1,2 @@
632 @@ -1,1 +1,2 @@
633 bye
633 bye
634 +universe
634 +universe
635 diff -r 9ecee4f634e3 hello.txt
635 diff -r 9ecee4f634e3 hello.txt
636 --- a/hello.txt
636 --- a/hello.txt
637 +++ b/hello.txt
637 +++ b/hello.txt
638 @@ -1,1 +1,3 @@
638 @@ -1,1 +1,3 @@
639 hello
639 hello
640 +world
640 +world
641 +universe
641 +universe
642 % test popping revisions not in working dir ancestry
642 % test popping revisions not in working dir ancestry
643 0 A empty
643 0 A empty
644 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
644 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
645 popping empty
645 popping empty
646 patch queue now empty
646 patch queue now empty
647 % test popping must remove files added in subdirectories first
647 % test popping must remove files added in subdirectories first
648 popping rename-dir
648 popping rename-dir
649 patch queue now empty
649 patch queue now empty
@@ -1,87 +1,93 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 cp "$TESTDIR"/printenv.py .
3 cp "$TESTDIR"/printenv.py .
4
4
5 hg clone http://localhost:$HGPORT/ copy
5 hg clone http://localhost:$HGPORT/ copy
6 echo $?
6 echo $?
7 test -d copy || echo copy: No such file or directory
7 test -d copy || echo copy: No such file or directory
8
8
9 # This server doesn't do range requests so it's basically only good for
9 # This server doesn't do range requests so it's basically only good for
10 # one pull
10 # one pull
11 cat > dumb.py <<EOF
11 cat > dumb.py <<EOF
12 import BaseHTTPServer, SimpleHTTPServer, os, signal
12 import BaseHTTPServer, SimpleHTTPServer, os, signal
13
13
14 def run(server_class=BaseHTTPServer.HTTPServer,
14 def run(server_class=BaseHTTPServer.HTTPServer,
15 handler_class=SimpleHTTPServer.SimpleHTTPRequestHandler):
15 handler_class=SimpleHTTPServer.SimpleHTTPRequestHandler):
16 server_address = ('localhost', int(os.environ['HGPORT']))
16 server_address = ('localhost', int(os.environ['HGPORT']))
17 httpd = server_class(server_address, handler_class)
17 httpd = server_class(server_address, handler_class)
18 httpd.serve_forever()
18 httpd.serve_forever()
19
19
20 signal.signal(signal.SIGTERM, lambda x: sys.exit(0))
20 signal.signal(signal.SIGTERM, lambda x: sys.exit(0))
21 run()
21 run()
22 EOF
22 EOF
23
23
24 python dumb.py 2>/dev/null &
24 python dumb.py 2>/dev/null &
25 echo $! >> $DAEMON_PIDS
25 echo $! >> $DAEMON_PIDS
26
26
27 mkdir remote
27 mkdir remote
28 cd remote
28 cd remote
29 hg init
29 hg init
30 echo foo > bar
30 echo foo > bar
31 hg add bar
31 hg add bar
32 hg commit -m"test" -d "1000000 0"
32 hg commit -m"test" -d "1000000 0"
33 hg tip
33 hg tip
34
34
35 cd ..
35 cd ..
36
36
37 hg clone static-http://localhost:$HGPORT/remote local | sed -e "s,:$HGPORT/,:\$HGPORT/,"
37 hg clone static-http://localhost:$HGPORT/remote local | sed -e "s,:$HGPORT/,:\$HGPORT/,"
38
38
39 cd local
39 cd local
40 hg verify
40 hg verify
41 cat bar
41 cat bar
42
42
43 cd ../remote
43 cd ../remote
44 echo baz > quux
44 echo baz > quux
45 hg commit -A -mtest2 -d '100000000 0'
45 hg commit -A -mtest2 -d '100000000 0'
46 # check for HTTP opener failures when cachefile does not exist
47 rm .hg/*.cache
46
48
47 cd ../local
49 cd ../local
48 echo '[hooks]' >> .hg/hgrc
50 echo '[hooks]' >> .hg/hgrc
49 echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
51 echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
50 hg pull | sed -e "s,:$HGPORT/,:\$HGPORT/,"
52 hg pull | sed -e "s,:$HGPORT/,:\$HGPORT/,"
51
53
52 echo '% trying to push'
54 echo '% trying to push'
53 hg update
55 hg update
54 echo more foo >> bar
56 echo more foo >> bar
55 hg commit -m"test" -d "100000000 0"
57 hg commit -m"test" -d "100000000 0"
56 hg push | sed -e "s,:$HGPORT/,:\$HGPORT/,"
58 hg push | sed -e "s,:$HGPORT/,:\$HGPORT/,"
57
59
60 echo '% trying clone -r'
61 cd ..
62 hg clone -r donotexist static-http://localhost:$HGPORT/remote local0 | sed -e "s,:$HGPORT/,:\$HGPORT/,"
63 hg clone -r 0 static-http://localhost:$HGPORT/remote local0 | sed -e "s,:$HGPORT/,:\$HGPORT/,"
64
58 echo '% test with "/" URI (issue 747)'
65 echo '% test with "/" URI (issue 747)'
59 cd ..
60 hg init
66 hg init
61 echo a > a
67 echo a > a
62 hg add a
68 hg add a
63 hg ci -ma
69 hg ci -ma
64
70
65 hg clone static-http://localhost:$HGPORT/ local2 | sed -e "s,:$HGPORT/,:\$HGPORT/,"
71 hg clone static-http://localhost:$HGPORT/ local2 | sed -e "s,:$HGPORT/,:\$HGPORT/,"
66
72
67 cd local2
73 cd local2
68 hg verify
74 hg verify
69 cat a
75 cat a
70 hg paths | sed -e "s,:$HGPORT/,:\$HGPORT/,"
76 hg paths | sed -e "s,:$HGPORT/,:\$HGPORT/,"
71
77
72 echo '% test with empty repo (issue965)'
78 echo '% test with empty repo (issue965)'
73 cd ..
79 cd ..
74 hg init remotempty
80 hg init remotempty
75
81
76 hg clone static-http://localhost:$HGPORT/remotempty local3 | sed -e "s,:$HGPORT/,:\$HGPORT/,"
82 hg clone static-http://localhost:$HGPORT/remotempty local3 | sed -e "s,:$HGPORT/,:\$HGPORT/,"
77
83
78 cd local3
84 cd local3
79 hg verify
85 hg verify
80 hg paths | sed -e "s,:$HGPORT/,:\$HGPORT/,"
86 hg paths | sed -e "s,:$HGPORT/,:\$HGPORT/,"
81
87
82 echo '% test with non-repo'
88 echo '% test with non-repo'
83 cd ..
89 cd ..
84 mkdir notarepo
90 mkdir notarepo
85 hg clone static-http://localhost:$HGPORT/notarepo local3 2>&1 | sed -e "s,:$HGPORT/,:\$HGPORT/,"
91 hg clone static-http://localhost:$HGPORT/notarepo local3 2>&1 | sed -e "s,:$HGPORT/,:\$HGPORT/,"
86
92
87 kill $!
93 kill $!
@@ -1,62 +1,71 b''
1 abort: error: Connection refused
1 abort: error: Connection refused
2 255
2 255
3 copy: No such file or directory
3 copy: No such file or directory
4 changeset: 0:53e17d176ae6
4 changeset: 0:53e17d176ae6
5 tag: tip
5 tag: tip
6 user: test
6 user: test
7 date: Mon Jan 12 13:46:40 1970 +0000
7 date: Mon Jan 12 13:46:40 1970 +0000
8 summary: test
8 summary: test
9
9
10 requesting all changes
10 requesting all changes
11 adding changesets
11 adding changesets
12 adding manifests
12 adding manifests
13 adding file changes
13 adding file changes
14 added 1 changesets with 1 changes to 1 files
14 added 1 changesets with 1 changes to 1 files
15 updating to branch default
15 updating to branch default
16 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 checking changesets
17 checking changesets
18 checking manifests
18 checking manifests
19 crosschecking files in changesets and manifests
19 crosschecking files in changesets and manifests
20 checking files
20 checking files
21 1 files, 1 changesets, 1 total revisions
21 1 files, 1 changesets, 1 total revisions
22 foo
22 foo
23 adding quux
23 adding quux
24 changegroup hook: HG_NODE=34401e0e9971e9720b613d9089ffa9a6eefb3d2d HG_SOURCE=pull HG_URL=http://localhost:$HGPORT/remote
24 changegroup hook: HG_NODE=34401e0e9971e9720b613d9089ffa9a6eefb3d2d HG_SOURCE=pull HG_URL=http://localhost:$HGPORT/remote
25 pulling from static-http://localhost:$HGPORT/remote
25 pulling from static-http://localhost:$HGPORT/remote
26 searching for changes
26 searching for changes
27 adding changesets
27 adding changesets
28 adding manifests
28 adding manifests
29 adding file changes
29 adding file changes
30 added 1 changesets with 1 changes to 1 files
30 added 1 changesets with 1 changes to 1 files
31 (run 'hg update' to get a working copy)
31 (run 'hg update' to get a working copy)
32 % trying to push
32 % trying to push
33 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 abort: cannot lock static-http repository
34 abort: cannot lock static-http repository
35 pushing to static-http://localhost:$HGPORT/remote
35 pushing to static-http://localhost:$HGPORT/remote
36 % trying clone -r
37 abort: unknown revision 'donotexist'!
38 requesting all changes
39 adding changesets
40 adding manifests
41 adding file changes
42 added 1 changesets with 1 changes to 1 files
43 updating to branch default
44 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 % test with "/" URI (issue 747)
45 % test with "/" URI (issue 747)
37 requesting all changes
46 requesting all changes
38 adding changesets
47 adding changesets
39 adding manifests
48 adding manifests
40 adding file changes
49 adding file changes
41 added 1 changesets with 1 changes to 1 files
50 added 1 changesets with 1 changes to 1 files
42 updating to branch default
51 updating to branch default
43 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 checking changesets
53 checking changesets
45 checking manifests
54 checking manifests
46 crosschecking files in changesets and manifests
55 crosschecking files in changesets and manifests
47 checking files
56 checking files
48 1 files, 1 changesets, 1 total revisions
57 1 files, 1 changesets, 1 total revisions
49 a
58 a
50 default = static-http://localhost:$HGPORT/
59 default = static-http://localhost:$HGPORT/
51 % test with empty repo (issue965)
60 % test with empty repo (issue965)
52 no changes found
61 no changes found
53 updating to branch default
62 updating to branch default
54 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
63 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 checking changesets
64 checking changesets
56 checking manifests
65 checking manifests
57 crosschecking files in changesets and manifests
66 crosschecking files in changesets and manifests
58 checking files
67 checking files
59 0 files, 0 changesets, 0 total revisions
68 0 files, 0 changesets, 0 total revisions
60 default = static-http://localhost:$HGPORT/remotempty
69 default = static-http://localhost:$HGPORT/remotempty
61 % test with non-repo
70 % test with non-repo
62 abort: 'http://localhost:$HGPORT/notarepo' does not appear to be an hg repository!
71 abort: 'http://localhost:$HGPORT/notarepo' does not appear to be an hg repository!
@@ -1,211 +1,231 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 rm -rf sub
3 rm -rf sub
4 mkdir sub
4 mkdir sub
5 cd sub
5 cd sub
6 hg init t
6 hg init t
7 cd t
7 cd t
8
8
9 echo % first revision, no sub
9 echo % first revision, no sub
10 echo a > a
10 echo a > a
11 hg ci -Am0
11 hg ci -Am0
12
12
13 echo % add first sub
13 echo % add first sub
14 echo s = s > .hgsub
14 echo s = s > .hgsub
15 hg add .hgsub
15 hg add .hgsub
16 hg init s
16 hg init s
17 echo a > s/a
17 echo a > s/a
18 hg -R s ci -Ams0
18 hg -R s ci -Ams0
19 hg ci -m1
19 hg ci -m1
20
20
21 echo % add sub sub
21 echo % add sub sub
22 echo ss = ss > s/.hgsub
22 echo ss = ss > s/.hgsub
23 hg init s/ss
23 hg init s/ss
24 echo a > s/ss/a
24 echo a > s/ss/a
25 hg -R s add s/.hgsub
25 hg -R s add s/.hgsub
26 hg -R s/ss add s/ss/a
26 hg -R s/ss add s/ss/a
27 hg ci -m2
27 hg ci -m2
28
28
29 echo % bump sub rev
29 echo % bump sub rev
30 echo b > s/a
30 echo b > s/a
31 hg -R s ci -ms1
31 hg -R s ci -ms1
32 hg ci -m3
32 hg ci -m3
33
33
34 echo % leave sub dirty
34 echo % leave sub dirty
35 echo c > s/a
35 echo c > s/a
36 hg ci -m4
36 hg ci -m4
37 hg tip -R s
37 hg tip -R s
38
38
39 echo % check caching
39 echo % check caching
40 hg co 0
40 hg co 0
41 hg debugsub
41 hg debugsub
42 echo % restore
42 echo % restore
43 hg co
43 hg co
44 hg debugsub
44 hg debugsub
45
45
46 echo % new branch for merge tests
46 echo % new branch for merge tests
47 hg co 1
47 hg co 1
48 echo t = t >> .hgsub
48 echo t = t >> .hgsub
49 hg init t
49 hg init t
50 echo t > t/t
50 echo t > t/t
51 hg -R t add t
51 hg -R t add t
52 echo % 5
52 echo % 5
53 hg ci -m5 # add sub
53 hg ci -m5 # add sub
54 echo t2 > t/t
54 echo t2 > t/t
55 echo % 6
55 echo % 6
56 hg st -R s
56 hg st -R s
57 hg ci -m6 # change sub
57 hg ci -m6 # change sub
58 hg debugsub
58 hg debugsub
59 echo t3 > t/t
59 echo t3 > t/t
60 echo % 7
60 echo % 7
61 hg ci -m7 # change sub again for conflict test
61 hg ci -m7 # change sub again for conflict test
62 hg rm .hgsub
62 hg rm .hgsub
63 echo % 8
63 echo % 8
64 hg ci -m8 # remove sub
64 hg ci -m8 # remove sub
65
65
66 echo % merge tests
66 echo % merge tests
67 hg co -C 3
67 hg co -C 3
68 hg merge 5 # test adding
68 hg merge 5 # test adding
69 hg debugsub
69 hg debugsub
70 hg ci -m9
70 hg ci -m9
71 hg merge 6 --debug # test change
71 hg merge 6 --debug # test change
72 hg debugsub
72 hg debugsub
73 echo conflict > t/t
73 echo conflict > t/t
74 hg ci -m10
74 hg ci -m10
75 HGMERGE=internal:merge hg merge --debug 7 # test conflict
75 HGMERGE=internal:merge hg merge --debug 7 # test conflict
76 echo % should conflict
76 echo % should conflict
77 cat t/t
77 cat t/t
78
78
79 echo % clone
79 echo % clone
80 cd ..
80 cd ..
81 hg clone t tc | sed 's|from .*/sub|from .../sub|g'
81 hg clone t tc | sed 's|from .*/sub|from .../sub|g'
82 cd tc
82 cd tc
83 hg debugsub
83 hg debugsub
84
84
85 echo % push
85 echo % push
86 echo bah > t/t
86 echo bah > t/t
87 hg ci -m11
87 hg ci -m11
88 hg push | sed 's/ .*sub/ ...sub/g'
88 hg push | sed 's/ .*sub/ ...sub/g'
89
89
90 echo % push -f
90 echo % push -f
91 echo bah > s/a
91 echo bah > s/a
92 hg ci -m12
92 hg ci -m12
93 hg push | sed 's/ .*sub/ ...sub/g'
93 hg push | sed 's/ .*sub/ ...sub/g'
94 hg push -f | sed 's/ .*sub/ ...sub/g'
94 hg push -f | sed 's/ .*sub/ ...sub/g'
95
95
96 echo % update
96 echo % update
97 cd ../t
97 cd ../t
98 hg up -C # discard our earlier merge
98 hg up -C # discard our earlier merge
99 echo blah > t/t
99 echo blah > t/t
100 hg ci -m13
100 hg ci -m13
101
101
102 echo % pull
102 echo % pull
103 cd ../tc
103 cd ../tc
104 hg pull | sed 's/ .*sub/ ...sub/g'
104 hg pull | sed 's/ .*sub/ ...sub/g'
105 # should pull t
105 # should pull t
106 hg up | sed 's|from .*/sub|from .../sub|g'
106 hg up | sed 's|from .*/sub|from .../sub|g'
107 cat t/t
107 cat t/t
108
108
109 echo % bogus subrepo path aborts
109 echo % bogus subrepo path aborts
110 echo 'bogus=[boguspath' >> .hgsub
110 echo 'bogus=[boguspath' >> .hgsub
111 hg ci -m 'bogus subrepo path'
111 hg ci -m 'bogus subrepo path'
112
112
113 echo % issue 1986
113 echo % issue 1986
114 cd ..
114 cd ..
115 rm -rf sub
115 rm -rf sub
116 hg init main
116 hg init main
117 cd main
117 cd main
118
118
119 hg init s # subrepo layout
119 hg init s # subrepo layout
120 cd s #
120 cd s #
121 echo a > a # o 5 br
121 echo a > a # o 5 br
122 hg ci -Am1 # /|
122 hg ci -Am1 # /|
123 hg branch br # o | 4 default
123 hg branch br # o | 4 default
124 echo a >> a # | |
124 echo a >> a # | |
125 hg ci -m1 # | o 3 br
125 hg ci -m1 # | o 3 br
126 hg up default # |/|
126 hg up default # |/|
127 echo b > b # o | 2 default
127 echo b > b # o | 2 default
128 hg ci -Am1 # | |
128 hg ci -Am1 # | |
129 hg up br # | o 1 br
129 hg up br # | o 1 br
130 hg merge tip # |/
130 hg merge tip # |/
131 hg ci -m1 # o 0 default
131 hg ci -m1 # o 0 default
132 hg up 2
132 hg up 2
133 echo c > c
133 echo c > c
134 hg ci -Am1
134 hg ci -Am1
135 hg up 3
135 hg up 3
136 hg merge 4
136 hg merge 4
137 hg ci -m1
137 hg ci -m1
138
138
139 cd .. # main repo layout:
139 cd .. # main repo layout:
140 echo 's = s' > .hgsub #
140 echo 's = s' > .hgsub #
141 hg -R s up 2 # * <-- try to merge default into br again
141 hg -R s up 2 # * <-- try to merge default into br again
142 hg ci -Am1 # .`|
142 hg ci -Am1 # .`|
143 hg branch br # . o 5 br --> substate = 5
143 hg branch br # . o 5 br --> substate = 5
144 echo b > b # . |
144 echo b > b # . |
145 hg -R s up 3 # o | 4 default --> substate = 4
145 hg -R s up 3 # o | 4 default --> substate = 4
146 hg ci -Am1 # | |
146 hg ci -Am1 # | |
147 hg up default # | o 3 br --> substate = 2
147 hg up default # | o 3 br --> substate = 2
148 echo c > c # |/|
148 echo c > c # |/|
149 hg ci -Am1 # o | 2 default --> substate = 2
149 hg ci -Am1 # o | 2 default --> substate = 2
150 hg up 1 # | |
150 hg up 1 # | |
151 hg merge 2 # | o 1 br --> substate = 3
151 hg merge 2 # | o 1 br --> substate = 3
152 hg ci -m1 # |/
152 hg ci -m1 # |/
153 hg up 2 # o 0 default --> substate = 2
153 hg up 2 # o 0 default --> substate = 2
154 hg -R s up 4
154 hg -R s up 4
155 echo d > d
155 echo d > d
156 hg ci -Am1
156 hg ci -Am1
157 hg up 3
157 hg up 3
158 hg -R s up 5
158 hg -R s up 5
159 echo e > e
159 echo e > e
160 hg ci -Am1
160 hg ci -Am1
161
161
162 hg up 5
162 hg up 5
163 hg merge 4 # try to merge default into br again
163 hg merge 4 # try to merge default into br again
164 cd ..
164 cd ..
165
165
166 echo % test subrepo delete from .hgsubstate
166 echo % test subrepo delete from .hgsubstate
167 hg init testdelete
167 hg init testdelete
168 mkdir testdelete/nested testdelete/nested2
168 mkdir testdelete/nested testdelete/nested2
169 hg init testdelete/nested
169 hg init testdelete/nested
170 hg init testdelete/nested2
170 hg init testdelete/nested2
171 echo test > testdelete/nested/foo
171 echo test > testdelete/nested/foo
172 echo test > testdelete/nested2/foo
172 echo test > testdelete/nested2/foo
173 hg -R testdelete/nested add
173 hg -R testdelete/nested add
174 hg -R testdelete/nested2 add
174 hg -R testdelete/nested2 add
175 hg -R testdelete/nested ci -m test
175 hg -R testdelete/nested ci -m test
176 hg -R testdelete/nested2 ci -m test
176 hg -R testdelete/nested2 ci -m test
177 echo nested = nested > testdelete/.hgsub
177 echo nested = nested > testdelete/.hgsub
178 echo nested2 = nested2 >> testdelete/.hgsub
178 echo nested2 = nested2 >> testdelete/.hgsub
179 hg -R testdelete add
179 hg -R testdelete add
180 hg -R testdelete ci -m "nested 1 & 2 added"
180 hg -R testdelete ci -m "nested 1 & 2 added"
181 echo nested = nested > testdelete/.hgsub
181 echo nested = nested > testdelete/.hgsub
182 hg -R testdelete ci -m "nested 2 deleted"
182 hg -R testdelete ci -m "nested 2 deleted"
183 cat testdelete/.hgsubstate | sed "s:.* ::"
183 cat testdelete/.hgsubstate | sed "s:.* ::"
184 hg -R testdelete remove testdelete/.hgsub
184 hg -R testdelete remove testdelete/.hgsub
185 hg -R testdelete ci -m ".hgsub deleted"
185 hg -R testdelete ci -m ".hgsub deleted"
186 cat testdelete/.hgsubstate
186 cat testdelete/.hgsubstate
187
187
188 echo % test repository cloning
188 echo % test repository cloning
189 mkdir mercurial mercurial2
189 mkdir mercurial mercurial2
190 hg init nested_absolute
190 hg init nested_absolute
191 echo test > nested_absolute/foo
191 echo test > nested_absolute/foo
192 hg -R nested_absolute add
192 hg -R nested_absolute add
193 hg -R nested_absolute ci -mtest
193 hg -R nested_absolute ci -mtest
194 cd mercurial
194 cd mercurial
195 hg init nested_relative
195 hg init nested_relative
196 echo test2 > nested_relative/foo2
196 echo test2 > nested_relative/foo2
197 hg -R nested_relative add
197 hg -R nested_relative add
198 hg -R nested_relative ci -mtest2
198 hg -R nested_relative ci -mtest2
199 hg init main
199 hg init main
200 echo "nested_relative = ../nested_relative" > main/.hgsub
200 echo "nested_relative = ../nested_relative" > main/.hgsub
201 echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
201 echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
202 hg -R main add
202 hg -R main add
203 hg -R main ci -m "add subrepos"
203 hg -R main ci -m "add subrepos"
204 cd ..
204 cd ..
205 hg clone mercurial/main mercurial2/main
205 hg clone mercurial/main mercurial2/main
206 cat mercurial2/main/nested_absolute/.hg/hgrc \
206 cat mercurial2/main/nested_absolute/.hg/hgrc \
207 mercurial2/main/nested_relative/.hg/hgrc \
207 mercurial2/main/nested_relative/.hg/hgrc \
208 | "$TESTDIR/filtertmp.py"
208 | "$TESTDIR/filtertmp.py"
209 rm -rf mercurial mercurial2
209 rm -rf mercurial mercurial2
210
210
211 echo % issue 1977
212 hg init repo
213 hg init repo/s
214 echo a > repo/s/a
215 hg -R repo/s ci -Am0
216 echo s = s > repo/.hgsub
217 hg -R repo ci -Am1
218 hg clone repo repo2
219 hg -q -R repo2 pull -u
220 echo 1 > repo2/s/a
221 hg -R repo2/s ci -m2
222 hg -q -R repo2/s push
223 hg -R repo2/s up -C 0
224 echo 2 > repo2/s/a
225 hg -R repo2/s ci -m3
226 hg -R repo2 ci -m3
227 hg -q -R repo2 push
228 hg -R repo update
229 rm -rf repo2 repo
230
211 exit 0
231 exit 0
@@ -1,268 +1,280 b''
1 % first revision, no sub
1 % first revision, no sub
2 adding a
2 adding a
3 % add first sub
3 % add first sub
4 adding a
4 adding a
5 committing subrepository s
5 committing subrepository s
6 % add sub sub
6 % add sub sub
7 committing subrepository s
7 committing subrepository s
8 committing subrepository ss
8 committing subrepository ss
9 % bump sub rev
9 % bump sub rev
10 committing subrepository s
10 committing subrepository s
11 % leave sub dirty
11 % leave sub dirty
12 committing subrepository s
12 committing subrepository s
13 changeset: 3:1c833a7a9e3a
13 changeset: 3:1c833a7a9e3a
14 tag: tip
14 tag: tip
15 user: test
15 user: test
16 date: Thu Jan 01 00:00:00 1970 +0000
16 date: Thu Jan 01 00:00:00 1970 +0000
17 summary: 4
17 summary: 4
18
18
19 % check caching
19 % check caching
20 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
20 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
21 % restore
21 % restore
22 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 path s
23 path s
24 source s
24 source s
25 revision 1c833a7a9e3a4445c711aaf0f012379cd0d4034e
25 revision 1c833a7a9e3a4445c711aaf0f012379cd0d4034e
26 % new branch for merge tests
26 % new branch for merge tests
27 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
27 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 adding t/t
28 adding t/t
29 % 5
29 % 5
30 committing subrepository t
30 committing subrepository t
31 created new head
31 created new head
32 % 6
32 % 6
33 committing subrepository t
33 committing subrepository t
34 path s
34 path s
35 source s
35 source s
36 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
36 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
37 path t
37 path t
38 source t
38 source t
39 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
39 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
40 % 7
40 % 7
41 committing subrepository t
41 committing subrepository t
42 % 8
42 % 8
43 % merge tests
43 % merge tests
44 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 (branch merge, don't forget to commit)
46 (branch merge, don't forget to commit)
47 path s
47 path s
48 source s
48 source s
49 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
49 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
50 path t
50 path t
51 source t
51 source t
52 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
52 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
53 created new head
53 created new head
54 searching for copies back to rev 2
54 searching for copies back to rev 2
55 resolving manifests
55 resolving manifests
56 overwrite None partial False
56 overwrite None partial False
57 ancestor 1f14a2e2d3ec local f0d2028bf86d+ remote 1831e14459c4
57 ancestor 1f14a2e2d3ec local f0d2028bf86d+ remote 1831e14459c4
58 .hgsubstate: versions differ -> m
58 .hgsubstate: versions differ -> m
59 update: .hgsubstate 1/1 files (100.00%)
59 update: .hgsubstate 1/1 files (100.00%)
60 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
60 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
61 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
61 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
62 getting subrepo t
62 getting subrepo t
63 resolving manifests
63 resolving manifests
64 overwrite True partial False
64 overwrite True partial False
65 ancestor 60ca1237c194+ local 60ca1237c194+ remote 6747d179aa9a
65 ancestor 60ca1237c194+ local 60ca1237c194+ remote 6747d179aa9a
66 t: remote is newer -> g
66 t: remote is newer -> g
67 update: t 1/1 files (100.00%)
67 update: t 1/1 files (100.00%)
68 getting t
68 getting t
69 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 (branch merge, don't forget to commit)
70 (branch merge, don't forget to commit)
71 path s
71 path s
72 source s
72 source s
73 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
73 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
74 path t
74 path t
75 source t
75 source t
76 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
76 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
77 committing subrepository t
77 committing subrepository t
78 searching for copies back to rev 2
78 searching for copies back to rev 2
79 resolving manifests
79 resolving manifests
80 overwrite None partial False
80 overwrite None partial False
81 ancestor 1831e14459c4 local e45c8b14af55+ remote f94576341bcf
81 ancestor 1831e14459c4 local e45c8b14af55+ remote f94576341bcf
82 .hgsubstate: versions differ -> m
82 .hgsubstate: versions differ -> m
83 update: .hgsubstate 1/1 files (100.00%)
83 update: .hgsubstate 1/1 files (100.00%)
84 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
84 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
85 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
85 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
86 merging subrepo t
86 merging subrepo t
87 searching for copies back to rev 2
87 searching for copies back to rev 2
88 resolving manifests
88 resolving manifests
89 overwrite None partial False
89 overwrite None partial False
90 ancestor 6747d179aa9a local 20a0db6fbf6c+ remote 7af322bc1198
90 ancestor 6747d179aa9a local 20a0db6fbf6c+ remote 7af322bc1198
91 t: versions differ -> m
91 t: versions differ -> m
92 preserving t for resolve of t
92 preserving t for resolve of t
93 update: t 1/1 files (100.00%)
93 update: t 1/1 files (100.00%)
94 picked tool 'internal:merge' for t (binary False symlink False)
94 picked tool 'internal:merge' for t (binary False symlink False)
95 merging t
95 merging t
96 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
96 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
97 warning: conflicts during merge.
97 warning: conflicts during merge.
98 merging t failed!
98 merging t failed!
99 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
99 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
100 use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon
100 use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon
101 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 (branch merge, don't forget to commit)
102 (branch merge, don't forget to commit)
103 % should conflict
103 % should conflict
104 <<<<<<< local
104 <<<<<<< local
105 conflict
105 conflict
106 =======
106 =======
107 t3
107 t3
108 >>>>>>> other
108 >>>>>>> other
109 % clone
109 % clone
110 updating to branch default
110 updating to branch default
111 pulling subrepo s from .../sub/t/s
111 pulling subrepo s from .../sub/t/s
112 requesting all changes
112 requesting all changes
113 adding changesets
113 adding changesets
114 adding manifests
114 adding manifests
115 adding file changes
115 adding file changes
116 added 4 changesets with 5 changes to 3 files
116 added 4 changesets with 5 changes to 3 files
117 pulling subrepo ss from .../sub/t/s/ss
117 pulling subrepo ss from .../sub/t/s/ss
118 requesting all changes
118 requesting all changes
119 adding changesets
119 adding changesets
120 adding manifests
120 adding manifests
121 adding file changes
121 adding file changes
122 added 1 changesets with 1 changes to 1 files
122 added 1 changesets with 1 changes to 1 files
123 pulling subrepo t from .../sub/t/t
123 pulling subrepo t from .../sub/t/t
124 requesting all changes
124 requesting all changes
125 adding changesets
125 adding changesets
126 adding manifests
126 adding manifests
127 adding file changes
127 adding file changes
128 added 4 changesets with 4 changes to 1 files (+1 heads)
128 added 4 changesets with 4 changes to 1 files (+1 heads)
129 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
130 path s
130 path s
131 source s
131 source s
132 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
132 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
133 path t
133 path t
134 source t
134 source t
135 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
135 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
136 % push
136 % push
137 committing subrepository t
137 committing subrepository t
138 pushing ...sub/t
138 pushing ...sub/t
139 pushing ...subrepo ss
139 pushing ...subrepo ss
140 searching for changes
140 searching for changes
141 no changes found
141 no changes found
142 pushing ...subrepo s
142 pushing ...subrepo s
143 searching for changes
143 searching for changes
144 no changes found
144 no changes found
145 pushing ...subrepo t
145 pushing ...subrepo t
146 searching for changes
146 searching for changes
147 adding changesets
147 adding changesets
148 adding manifests
148 adding manifests
149 adding file changes
149 adding file changes
150 added 1 changesets with 1 changes to 1 files
150 added 1 changesets with 1 changes to 1 files
151 searching for changes
151 searching for changes
152 adding changesets
152 adding changesets
153 adding manifests
153 adding manifests
154 adding file changes
154 adding file changes
155 added 1 changesets with 1 changes to 1 files
155 added 1 changesets with 1 changes to 1 files
156 % push -f
156 % push -f
157 committing subrepository s
157 committing subrepository s
158 abort: push creates new remote heads on branch 'default'!
158 abort: push creates new remote heads on branch 'default'!
159 pushing ...sub/t
159 pushing ...sub/t
160 pushing ...subrepo ss
160 pushing ...subrepo ss
161 searching for changes
161 searching for changes
162 no changes found
162 no changes found
163 pushing ...subrepo s
163 pushing ...subrepo s
164 searching for changes
164 searching for changes
165 (did you forget to merge? use push -f to force)
165 (did you forget to merge? use push -f to force)
166 pushing ...subrepo t
167 searching for changes
168 no changes found
169 searching for changes
170 adding changesets
171 adding manifests
172 adding file changes
173 added 1 changesets with 1 changes to 1 files
174 pushing ...sub/t
166 pushing ...sub/t
175 pushing ...subrepo ss
167 pushing ...subrepo ss
176 searching for changes
168 searching for changes
177 no changes found
169 no changes found
178 pushing ...subrepo s
170 pushing ...subrepo s
179 searching for changes
171 searching for changes
180 adding changesets
172 adding changesets
181 adding manifests
173 adding manifests
182 adding file changes
174 adding file changes
183 added 1 changesets with 1 changes to 1 files (+1 heads)
175 added 1 changesets with 1 changes to 1 files (+1 heads)
184 pushing ...subrepo t
176 pushing ...subrepo t
185 searching for changes
177 searching for changes
186 no changes found
178 no changes found
187 searching for changes
179 searching for changes
188 no changes found
180 adding changesets
181 adding manifests
182 adding file changes
183 added 1 changesets with 1 changes to 1 files
189 % update
184 % update
190 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
185 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 committing subrepository t
186 committing subrepository t
192 % pull
187 % pull
193 pulling ...sub/t
188 pulling ...sub/t
194 searching for changes
189 searching for changes
195 adding changesets
190 adding changesets
196 adding manifests
191 adding manifests
197 adding file changes
192 adding file changes
198 added 1 changesets with 1 changes to 1 files
193 added 1 changesets with 1 changes to 1 files
199 (run 'hg update' to get a working copy)
194 (run 'hg update' to get a working copy)
200 pulling subrepo t from .../sub/t/t
195 pulling subrepo t from .../sub/t/t
201 searching for changes
196 searching for changes
202 adding changesets
197 adding changesets
203 adding manifests
198 adding manifests
204 adding file changes
199 adding file changes
205 added 1 changesets with 1 changes to 1 files
200 added 1 changesets with 1 changes to 1 files
206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 blah
202 blah
208 % bogus subrepo path aborts
203 % bogus subrepo path aborts
209 abort: missing ] in subrepo source
204 abort: missing ] in subrepo source
210 % issue 1986
205 % issue 1986
211 adding a
206 adding a
212 marked working directory as branch br
207 marked working directory as branch br
213 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
208 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
214 adding b
209 adding b
215 created new head
210 created new head
216 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
211 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
217 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
218 (branch merge, don't forget to commit)
213 (branch merge, don't forget to commit)
219 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
214 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 adding c
215 adding c
221 created new head
216 created new head
222 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
217 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
223 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
218 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 (branch merge, don't forget to commit)
219 (branch merge, don't forget to commit)
225 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
220 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
226 adding .hgsub
221 adding .hgsub
227 committing subrepository s
222 committing subrepository s
228 marked working directory as branch br
223 marked working directory as branch br
229 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 adding b
225 adding b
231 committing subrepository s
226 committing subrepository s
232 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
227 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
233 adding c
228 adding c
234 created new head
229 created new head
235 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
230 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
236 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 (branch merge, don't forget to commit)
232 (branch merge, don't forget to commit)
238 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
233 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
240 adding d
235 adding d
241 committing subrepository s
236 committing subrepository s
242 created new head
237 created new head
243 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
238 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
244 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 adding e
240 adding e
246 committing subrepository s
241 committing subrepository s
247 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
243 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 (branch merge, don't forget to commit)
244 (branch merge, don't forget to commit)
250 % test subrepo delete from .hgsubstate
245 % test subrepo delete from .hgsubstate
251 adding testdelete/nested/foo
246 adding testdelete/nested/foo
252 adding testdelete/nested2/foo
247 adding testdelete/nested2/foo
253 adding testdelete/.hgsub
248 adding testdelete/.hgsub
254 committing subrepository nested2
249 committing subrepository nested2
255 committing subrepository nested
250 committing subrepository nested
256 nested
251 nested
257 % test repository cloning
252 % test repository cloning
258 adding nested_absolute/foo
253 adding nested_absolute/foo
259 adding nested_relative/foo2
254 adding nested_relative/foo2
260 adding main/.hgsub
255 adding main/.hgsub
261 committing subrepository nested_relative
256 committing subrepository nested_relative
262 committing subrepository nested_absolute
257 committing subrepository nested_absolute
263 updating to branch default
258 updating to branch default
264 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
259 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
265 [paths]
260 [paths]
266 default = $HGTMP/test-subrepo/sub/mercurial/nested_absolute
261 default = $HGTMP/test-subrepo/sub/mercurial/nested_absolute
267 [paths]
262 [paths]
268 default = $HGTMP/test-subrepo/sub/mercurial/main/../nested_relative
263 default = $HGTMP/test-subrepo/sub/mercurial/main/../nested_relative
264 % issue 1977
265 adding a
266 adding .hgsub
267 committing subrepository s
268 updating to branch default
269 pulling subrepo s
270 requesting all changes
271 adding changesets
272 adding manifests
273 adding file changes
274 added 1 changesets with 1 changes to 1 files
275 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 created new head
278 committing subrepository s
279 abort: push creates new remote heads on branch 'default'!
280 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now