##// END OF EJS Templates
mq: qdiff with the same diff options than qrefresh (issue1350)...
Patrick Mezard -
r10191:99d285ac default
parent child Browse files
Show More
@@ -1,2737 +1,2739 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details)::
17 Common tasks (use "hg help command" for more details)::
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25
25
26 add known patch to applied stack qpush
26 add known patch to applied stack qpush
27 remove patch from applied stack qpop
27 remove patch from applied stack qpop
28 refresh contents of top applied patch qrefresh
28 refresh contents of top applied patch qrefresh
29
29
30 By default, mq will automatically use git patches when required to
30 By default, mq will automatically use git patches when required to
31 avoid losing file mode changes, copy records, binary files or empty
31 avoid losing file mode changes, copy records, binary files or empty
32 files creations or deletions. This behaviour can be configured with::
32 files creations or deletions. This behaviour can be configured with::
33
33
34 [mq]
34 [mq]
35 git = auto/keep/yes/no
35 git = auto/keep/yes/no
36
36
37 If set to 'keep', mq will obey the [diff] section configuration while
37 If set to 'keep', mq will obey the [diff] section configuration while
38 preserving existing git patches upon qrefresh. If set to 'yes' or
38 preserving existing git patches upon qrefresh. If set to 'yes' or
39 'no', mq will override the [diff] section and always generate git or
39 'no', mq will override the [diff] section and always generate git or
40 regular patches, possibly losing data in the second case.
40 regular patches, possibly losing data in the second case.
41 '''
41 '''
42
42
43 from mercurial.i18n import _
43 from mercurial.i18n import _
44 from mercurial.node import bin, hex, short, nullid, nullrev
44 from mercurial.node import bin, hex, short, nullid, nullrev
45 from mercurial.lock import release
45 from mercurial.lock import release
46 from mercurial import commands, cmdutil, hg, patch, util
46 from mercurial import commands, cmdutil, hg, patch, util
47 from mercurial import repair, extensions, url, error
47 from mercurial import repair, extensions, url, error
48 import os, sys, re, errno
48 import os, sys, re, errno
49
49
50 commands.norepo += " qclone"
50 commands.norepo += " qclone"
51
51
52 # Patch names looks like unix-file names.
52 # Patch names looks like unix-file names.
53 # They must be joinable with queue directory and result in the patch path.
53 # They must be joinable with queue directory and result in the patch path.
54 normname = util.normpath
54 normname = util.normpath
55
55
56 class statusentry(object):
56 class statusentry(object):
57 def __init__(self, rev, name=None):
57 def __init__(self, rev, name=None):
58 if not name:
58 if not name:
59 fields = rev.split(':', 1)
59 fields = rev.split(':', 1)
60 if len(fields) == 2:
60 if len(fields) == 2:
61 self.rev, self.name = fields
61 self.rev, self.name = fields
62 else:
62 else:
63 self.rev, self.name = None, None
63 self.rev, self.name = None, None
64 else:
64 else:
65 self.rev, self.name = rev, name
65 self.rev, self.name = rev, name
66
66
67 def __str__(self):
67 def __str__(self):
68 return self.rev + ':' + self.name
68 return self.rev + ':' + self.name
69
69
70 class patchheader(object):
70 class patchheader(object):
71 def __init__(self, pf):
71 def __init__(self, pf):
72 def eatdiff(lines):
72 def eatdiff(lines):
73 while lines:
73 while lines:
74 l = lines[-1]
74 l = lines[-1]
75 if (l.startswith("diff -") or
75 if (l.startswith("diff -") or
76 l.startswith("Index:") or
76 l.startswith("Index:") or
77 l.startswith("===========")):
77 l.startswith("===========")):
78 del lines[-1]
78 del lines[-1]
79 else:
79 else:
80 break
80 break
81 def eatempty(lines):
81 def eatempty(lines):
82 while lines:
82 while lines:
83 l = lines[-1]
83 l = lines[-1]
84 if re.match('\s*$', l):
84 if re.match('\s*$', l):
85 del lines[-1]
85 del lines[-1]
86 else:
86 else:
87 break
87 break
88
88
89 message = []
89 message = []
90 comments = []
90 comments = []
91 user = None
91 user = None
92 date = None
92 date = None
93 format = None
93 format = None
94 subject = None
94 subject = None
95 diffstart = 0
95 diffstart = 0
96
96
97 for line in file(pf):
97 for line in file(pf):
98 line = line.rstrip()
98 line = line.rstrip()
99 if line.startswith('diff --git'):
99 if line.startswith('diff --git'):
100 diffstart = 2
100 diffstart = 2
101 break
101 break
102 if diffstart:
102 if diffstart:
103 if line.startswith('+++ '):
103 if line.startswith('+++ '):
104 diffstart = 2
104 diffstart = 2
105 break
105 break
106 if line.startswith("--- "):
106 if line.startswith("--- "):
107 diffstart = 1
107 diffstart = 1
108 continue
108 continue
109 elif format == "hgpatch":
109 elif format == "hgpatch":
110 # parse values when importing the result of an hg export
110 # parse values when importing the result of an hg export
111 if line.startswith("# User "):
111 if line.startswith("# User "):
112 user = line[7:]
112 user = line[7:]
113 elif line.startswith("# Date "):
113 elif line.startswith("# Date "):
114 date = line[7:]
114 date = line[7:]
115 elif not line.startswith("# ") and line:
115 elif not line.startswith("# ") and line:
116 message.append(line)
116 message.append(line)
117 format = None
117 format = None
118 elif line == '# HG changeset patch':
118 elif line == '# HG changeset patch':
119 message = []
119 message = []
120 format = "hgpatch"
120 format = "hgpatch"
121 elif (format != "tagdone" and (line.startswith("Subject: ") or
121 elif (format != "tagdone" and (line.startswith("Subject: ") or
122 line.startswith("subject: "))):
122 line.startswith("subject: "))):
123 subject = line[9:]
123 subject = line[9:]
124 format = "tag"
124 format = "tag"
125 elif (format != "tagdone" and (line.startswith("From: ") or
125 elif (format != "tagdone" and (line.startswith("From: ") or
126 line.startswith("from: "))):
126 line.startswith("from: "))):
127 user = line[6:]
127 user = line[6:]
128 format = "tag"
128 format = "tag"
129 elif format == "tag" and line == "":
129 elif format == "tag" and line == "":
130 # when looking for tags (subject: from: etc) they
130 # when looking for tags (subject: from: etc) they
131 # end once you find a blank line in the source
131 # end once you find a blank line in the source
132 format = "tagdone"
132 format = "tagdone"
133 elif message or line:
133 elif message or line:
134 message.append(line)
134 message.append(line)
135 comments.append(line)
135 comments.append(line)
136
136
137 eatdiff(message)
137 eatdiff(message)
138 eatdiff(comments)
138 eatdiff(comments)
139 eatempty(message)
139 eatempty(message)
140 eatempty(comments)
140 eatempty(comments)
141
141
142 # make sure message isn't empty
142 # make sure message isn't empty
143 if format and format.startswith("tag") and subject:
143 if format and format.startswith("tag") and subject:
144 message.insert(0, "")
144 message.insert(0, "")
145 message.insert(0, subject)
145 message.insert(0, subject)
146
146
147 self.message = message
147 self.message = message
148 self.comments = comments
148 self.comments = comments
149 self.user = user
149 self.user = user
150 self.date = date
150 self.date = date
151 self.haspatch = diffstart > 1
151 self.haspatch = diffstart > 1
152
152
153 def setuser(self, user):
153 def setuser(self, user):
154 if not self.updateheader(['From: ', '# User '], user):
154 if not self.updateheader(['From: ', '# User '], user):
155 try:
155 try:
156 patchheaderat = self.comments.index('# HG changeset patch')
156 patchheaderat = self.comments.index('# HG changeset patch')
157 self.comments.insert(patchheaderat + 1, '# User ' + user)
157 self.comments.insert(patchheaderat + 1, '# User ' + user)
158 except ValueError:
158 except ValueError:
159 if self._hasheader(['Date: ']):
159 if self._hasheader(['Date: ']):
160 self.comments = ['From: ' + user] + self.comments
160 self.comments = ['From: ' + user] + self.comments
161 else:
161 else:
162 tmp = ['# HG changeset patch', '# User ' + user, '']
162 tmp = ['# HG changeset patch', '# User ' + user, '']
163 self.comments = tmp + self.comments
163 self.comments = tmp + self.comments
164 self.user = user
164 self.user = user
165
165
166 def setdate(self, date):
166 def setdate(self, date):
167 if not self.updateheader(['Date: ', '# Date '], date):
167 if not self.updateheader(['Date: ', '# Date '], date):
168 try:
168 try:
169 patchheaderat = self.comments.index('# HG changeset patch')
169 patchheaderat = self.comments.index('# HG changeset patch')
170 self.comments.insert(patchheaderat + 1, '# Date ' + date)
170 self.comments.insert(patchheaderat + 1, '# Date ' + date)
171 except ValueError:
171 except ValueError:
172 if self._hasheader(['From: ']):
172 if self._hasheader(['From: ']):
173 self.comments = ['Date: ' + date] + self.comments
173 self.comments = ['Date: ' + date] + self.comments
174 else:
174 else:
175 tmp = ['# HG changeset patch', '# Date ' + date, '']
175 tmp = ['# HG changeset patch', '# Date ' + date, '']
176 self.comments = tmp + self.comments
176 self.comments = tmp + self.comments
177 self.date = date
177 self.date = date
178
178
179 def setmessage(self, message):
179 def setmessage(self, message):
180 if self.comments:
180 if self.comments:
181 self._delmsg()
181 self._delmsg()
182 self.message = [message]
182 self.message = [message]
183 self.comments += self.message
183 self.comments += self.message
184
184
185 def updateheader(self, prefixes, new):
185 def updateheader(self, prefixes, new):
186 '''Update all references to a field in the patch header.
186 '''Update all references to a field in the patch header.
187 Return whether the field is present.'''
187 Return whether the field is present.'''
188 res = False
188 res = False
189 for prefix in prefixes:
189 for prefix in prefixes:
190 for i in xrange(len(self.comments)):
190 for i in xrange(len(self.comments)):
191 if self.comments[i].startswith(prefix):
191 if self.comments[i].startswith(prefix):
192 self.comments[i] = prefix + new
192 self.comments[i] = prefix + new
193 res = True
193 res = True
194 break
194 break
195 return res
195 return res
196
196
197 def _hasheader(self, prefixes):
197 def _hasheader(self, prefixes):
198 '''Check if a header starts with any of the given prefixes.'''
198 '''Check if a header starts with any of the given prefixes.'''
199 for prefix in prefixes:
199 for prefix in prefixes:
200 for comment in self.comments:
200 for comment in self.comments:
201 if comment.startswith(prefix):
201 if comment.startswith(prefix):
202 return True
202 return True
203 return False
203 return False
204
204
205 def __str__(self):
205 def __str__(self):
206 if not self.comments:
206 if not self.comments:
207 return ''
207 return ''
208 return '\n'.join(self.comments) + '\n\n'
208 return '\n'.join(self.comments) + '\n\n'
209
209
210 def _delmsg(self):
210 def _delmsg(self):
211 '''Remove existing message, keeping the rest of the comments fields.
211 '''Remove existing message, keeping the rest of the comments fields.
212 If comments contains 'subject: ', message will prepend
212 If comments contains 'subject: ', message will prepend
213 the field and a blank line.'''
213 the field and a blank line.'''
214 if self.message:
214 if self.message:
215 subj = 'subject: ' + self.message[0].lower()
215 subj = 'subject: ' + self.message[0].lower()
216 for i in xrange(len(self.comments)):
216 for i in xrange(len(self.comments)):
217 if subj == self.comments[i].lower():
217 if subj == self.comments[i].lower():
218 del self.comments[i]
218 del self.comments[i]
219 self.message = self.message[2:]
219 self.message = self.message[2:]
220 break
220 break
221 ci = 0
221 ci = 0
222 for mi in self.message:
222 for mi in self.message:
223 while mi != self.comments[ci]:
223 while mi != self.comments[ci]:
224 ci += 1
224 ci += 1
225 del self.comments[ci]
225 del self.comments[ci]
226
226
227 class queue(object):
227 class queue(object):
228 def __init__(self, ui, path, patchdir=None):
228 def __init__(self, ui, path, patchdir=None):
229 self.basepath = path
229 self.basepath = path
230 self.path = patchdir or os.path.join(path, "patches")
230 self.path = patchdir or os.path.join(path, "patches")
231 self.opener = util.opener(self.path)
231 self.opener = util.opener(self.path)
232 self.ui = ui
232 self.ui = ui
233 self.applied_dirty = 0
233 self.applied_dirty = 0
234 self.series_dirty = 0
234 self.series_dirty = 0
235 self.series_path = "series"
235 self.series_path = "series"
236 self.status_path = "status"
236 self.status_path = "status"
237 self.guards_path = "guards"
237 self.guards_path = "guards"
238 self.active_guards = None
238 self.active_guards = None
239 self.guards_dirty = False
239 self.guards_dirty = False
240 # Handle mq.git as a bool with extended values
240 # Handle mq.git as a bool with extended values
241 try:
241 try:
242 gitmode = ui.configbool('mq', 'git', None)
242 gitmode = ui.configbool('mq', 'git', None)
243 if gitmode is None:
243 if gitmode is None:
244 raise error.ConfigError()
244 raise error.ConfigError()
245 self.gitmode = gitmode and 'yes' or 'no'
245 self.gitmode = gitmode and 'yes' or 'no'
246 except error.ConfigError:
246 except error.ConfigError:
247 self.gitmode = ui.config('mq', 'git', 'auto').lower()
247 self.gitmode = ui.config('mq', 'git', 'auto').lower()
248
248
249 @util.propertycache
249 @util.propertycache
250 def applied(self):
250 def applied(self):
251 if os.path.exists(self.join(self.status_path)):
251 if os.path.exists(self.join(self.status_path)):
252 lines = self.opener(self.status_path).read().splitlines()
252 lines = self.opener(self.status_path).read().splitlines()
253 return [statusentry(l) for l in lines]
253 return [statusentry(l) for l in lines]
254 return []
254 return []
255
255
256 @util.propertycache
256 @util.propertycache
257 def full_series(self):
257 def full_series(self):
258 if os.path.exists(self.join(self.series_path)):
258 if os.path.exists(self.join(self.series_path)):
259 return self.opener(self.series_path).read().splitlines()
259 return self.opener(self.series_path).read().splitlines()
260 return []
260 return []
261
261
262 @util.propertycache
262 @util.propertycache
263 def series(self):
263 def series(self):
264 self.parse_series()
264 self.parse_series()
265 return self.series
265 return self.series
266
266
267 @util.propertycache
267 @util.propertycache
268 def series_guards(self):
268 def series_guards(self):
269 self.parse_series()
269 self.parse_series()
270 return self.series_guards
270 return self.series_guards
271
271
272 def invalidate(self):
272 def invalidate(self):
273 for a in 'applied full_series series series_guards'.split():
273 for a in 'applied full_series series series_guards'.split():
274 if a in self.__dict__:
274 if a in self.__dict__:
275 delattr(self, a)
275 delattr(self, a)
276 self.applied_dirty = 0
276 self.applied_dirty = 0
277 self.series_dirty = 0
277 self.series_dirty = 0
278 self.guards_dirty = False
278 self.guards_dirty = False
279 self.active_guards = None
279 self.active_guards = None
280
280
281 def diffopts(self, opts={}, patchfn=None):
281 def diffopts(self, opts={}, patchfn=None):
282 diffopts = patch.diffopts(self.ui, opts)
282 diffopts = patch.diffopts(self.ui, opts)
283 if self.gitmode == 'auto':
283 if self.gitmode == 'auto':
284 diffopts.upgrade = True
284 diffopts.upgrade = True
285 elif self.gitmode == 'keep':
285 elif self.gitmode == 'keep':
286 pass
286 pass
287 elif self.gitmode in ('yes', 'no'):
287 elif self.gitmode in ('yes', 'no'):
288 diffopts.git = self.gitmode == 'yes'
288 diffopts.git = self.gitmode == 'yes'
289 else:
289 else:
290 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
290 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
291 ' got %s') % self.gitmode)
291 ' got %s') % self.gitmode)
292 if patchfn:
292 if patchfn:
293 diffopts = self.patchopts(diffopts, patchfn)
293 diffopts = self.patchopts(diffopts, patchfn)
294 return diffopts
294 return diffopts
295
295
296 def patchopts(self, diffopts, *patches):
296 def patchopts(self, diffopts, *patches):
297 """Return a copy of input diff options with git set to true if
297 """Return a copy of input diff options with git set to true if
298 referenced patch is a git patch and should be preserved as such.
298 referenced patch is a git patch and should be preserved as such.
299 """
299 """
300 diffopts = diffopts.copy()
300 diffopts = diffopts.copy()
301 if not diffopts.git and self.gitmode == 'keep':
301 if not diffopts.git and self.gitmode == 'keep':
302 for patchfn in patches:
302 for patchfn in patches:
303 patchf = self.opener(patchfn, 'r')
303 patchf = self.opener(patchfn, 'r')
304 # if the patch was a git patch, refresh it as a git patch
304 # if the patch was a git patch, refresh it as a git patch
305 for line in patchf:
305 for line in patchf:
306 if line.startswith('diff --git'):
306 if line.startswith('diff --git'):
307 diffopts.git = True
307 diffopts.git = True
308 break
308 break
309 patchf.close()
309 patchf.close()
310 return diffopts
310 return diffopts
311
311
312 def join(self, *p):
312 def join(self, *p):
313 return os.path.join(self.path, *p)
313 return os.path.join(self.path, *p)
314
314
315 def find_series(self, patch):
315 def find_series(self, patch):
316 pre = re.compile("(\s*)([^#]+)")
316 pre = re.compile("(\s*)([^#]+)")
317 index = 0
317 index = 0
318 for l in self.full_series:
318 for l in self.full_series:
319 m = pre.match(l)
319 m = pre.match(l)
320 if m:
320 if m:
321 s = m.group(2)
321 s = m.group(2)
322 s = s.rstrip()
322 s = s.rstrip()
323 if s == patch:
323 if s == patch:
324 return index
324 return index
325 index += 1
325 index += 1
326 return None
326 return None
327
327
328 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
328 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
329
329
330 def parse_series(self):
330 def parse_series(self):
331 self.series = []
331 self.series = []
332 self.series_guards = []
332 self.series_guards = []
333 for l in self.full_series:
333 for l in self.full_series:
334 h = l.find('#')
334 h = l.find('#')
335 if h == -1:
335 if h == -1:
336 patch = l
336 patch = l
337 comment = ''
337 comment = ''
338 elif h == 0:
338 elif h == 0:
339 continue
339 continue
340 else:
340 else:
341 patch = l[:h]
341 patch = l[:h]
342 comment = l[h:]
342 comment = l[h:]
343 patch = patch.strip()
343 patch = patch.strip()
344 if patch:
344 if patch:
345 if patch in self.series:
345 if patch in self.series:
346 raise util.Abort(_('%s appears more than once in %s') %
346 raise util.Abort(_('%s appears more than once in %s') %
347 (patch, self.join(self.series_path)))
347 (patch, self.join(self.series_path)))
348 self.series.append(patch)
348 self.series.append(patch)
349 self.series_guards.append(self.guard_re.findall(comment))
349 self.series_guards.append(self.guard_re.findall(comment))
350
350
351 def check_guard(self, guard):
351 def check_guard(self, guard):
352 if not guard:
352 if not guard:
353 return _('guard cannot be an empty string')
353 return _('guard cannot be an empty string')
354 bad_chars = '# \t\r\n\f'
354 bad_chars = '# \t\r\n\f'
355 first = guard[0]
355 first = guard[0]
356 if first in '-+':
356 if first in '-+':
357 return (_('guard %r starts with invalid character: %r') %
357 return (_('guard %r starts with invalid character: %r') %
358 (guard, first))
358 (guard, first))
359 for c in bad_chars:
359 for c in bad_chars:
360 if c in guard:
360 if c in guard:
361 return _('invalid character in guard %r: %r') % (guard, c)
361 return _('invalid character in guard %r: %r') % (guard, c)
362
362
363 def set_active(self, guards):
363 def set_active(self, guards):
364 for guard in guards:
364 for guard in guards:
365 bad = self.check_guard(guard)
365 bad = self.check_guard(guard)
366 if bad:
366 if bad:
367 raise util.Abort(bad)
367 raise util.Abort(bad)
368 guards = sorted(set(guards))
368 guards = sorted(set(guards))
369 self.ui.debug('active guards: %s\n' % ' '.join(guards))
369 self.ui.debug('active guards: %s\n' % ' '.join(guards))
370 self.active_guards = guards
370 self.active_guards = guards
371 self.guards_dirty = True
371 self.guards_dirty = True
372
372
373 def active(self):
373 def active(self):
374 if self.active_guards is None:
374 if self.active_guards is None:
375 self.active_guards = []
375 self.active_guards = []
376 try:
376 try:
377 guards = self.opener(self.guards_path).read().split()
377 guards = self.opener(self.guards_path).read().split()
378 except IOError, err:
378 except IOError, err:
379 if err.errno != errno.ENOENT: raise
379 if err.errno != errno.ENOENT: raise
380 guards = []
380 guards = []
381 for i, guard in enumerate(guards):
381 for i, guard in enumerate(guards):
382 bad = self.check_guard(guard)
382 bad = self.check_guard(guard)
383 if bad:
383 if bad:
384 self.ui.warn('%s:%d: %s\n' %
384 self.ui.warn('%s:%d: %s\n' %
385 (self.join(self.guards_path), i + 1, bad))
385 (self.join(self.guards_path), i + 1, bad))
386 else:
386 else:
387 self.active_guards.append(guard)
387 self.active_guards.append(guard)
388 return self.active_guards
388 return self.active_guards
389
389
390 def set_guards(self, idx, guards):
390 def set_guards(self, idx, guards):
391 for g in guards:
391 for g in guards:
392 if len(g) < 2:
392 if len(g) < 2:
393 raise util.Abort(_('guard %r too short') % g)
393 raise util.Abort(_('guard %r too short') % g)
394 if g[0] not in '-+':
394 if g[0] not in '-+':
395 raise util.Abort(_('guard %r starts with invalid char') % g)
395 raise util.Abort(_('guard %r starts with invalid char') % g)
396 bad = self.check_guard(g[1:])
396 bad = self.check_guard(g[1:])
397 if bad:
397 if bad:
398 raise util.Abort(bad)
398 raise util.Abort(bad)
399 drop = self.guard_re.sub('', self.full_series[idx])
399 drop = self.guard_re.sub('', self.full_series[idx])
400 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
400 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
401 self.parse_series()
401 self.parse_series()
402 self.series_dirty = True
402 self.series_dirty = True
403
403
404 def pushable(self, idx):
404 def pushable(self, idx):
405 if isinstance(idx, str):
405 if isinstance(idx, str):
406 idx = self.series.index(idx)
406 idx = self.series.index(idx)
407 patchguards = self.series_guards[idx]
407 patchguards = self.series_guards[idx]
408 if not patchguards:
408 if not patchguards:
409 return True, None
409 return True, None
410 guards = self.active()
410 guards = self.active()
411 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
411 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
412 if exactneg:
412 if exactneg:
413 return False, exactneg[0]
413 return False, exactneg[0]
414 pos = [g for g in patchguards if g[0] == '+']
414 pos = [g for g in patchguards if g[0] == '+']
415 exactpos = [g for g in pos if g[1:] in guards]
415 exactpos = [g for g in pos if g[1:] in guards]
416 if pos:
416 if pos:
417 if exactpos:
417 if exactpos:
418 return True, exactpos[0]
418 return True, exactpos[0]
419 return False, pos
419 return False, pos
420 return True, ''
420 return True, ''
421
421
422 def explain_pushable(self, idx, all_patches=False):
422 def explain_pushable(self, idx, all_patches=False):
423 write = all_patches and self.ui.write or self.ui.warn
423 write = all_patches and self.ui.write or self.ui.warn
424 if all_patches or self.ui.verbose:
424 if all_patches or self.ui.verbose:
425 if isinstance(idx, str):
425 if isinstance(idx, str):
426 idx = self.series.index(idx)
426 idx = self.series.index(idx)
427 pushable, why = self.pushable(idx)
427 pushable, why = self.pushable(idx)
428 if all_patches and pushable:
428 if all_patches and pushable:
429 if why is None:
429 if why is None:
430 write(_('allowing %s - no guards in effect\n') %
430 write(_('allowing %s - no guards in effect\n') %
431 self.series[idx])
431 self.series[idx])
432 else:
432 else:
433 if not why:
433 if not why:
434 write(_('allowing %s - no matching negative guards\n') %
434 write(_('allowing %s - no matching negative guards\n') %
435 self.series[idx])
435 self.series[idx])
436 else:
436 else:
437 write(_('allowing %s - guarded by %r\n') %
437 write(_('allowing %s - guarded by %r\n') %
438 (self.series[idx], why))
438 (self.series[idx], why))
439 if not pushable:
439 if not pushable:
440 if why:
440 if why:
441 write(_('skipping %s - guarded by %r\n') %
441 write(_('skipping %s - guarded by %r\n') %
442 (self.series[idx], why))
442 (self.series[idx], why))
443 else:
443 else:
444 write(_('skipping %s - no matching guards\n') %
444 write(_('skipping %s - no matching guards\n') %
445 self.series[idx])
445 self.series[idx])
446
446
447 def save_dirty(self):
447 def save_dirty(self):
448 def write_list(items, path):
448 def write_list(items, path):
449 fp = self.opener(path, 'w')
449 fp = self.opener(path, 'w')
450 for i in items:
450 for i in items:
451 fp.write("%s\n" % i)
451 fp.write("%s\n" % i)
452 fp.close()
452 fp.close()
453 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
453 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
454 if self.series_dirty: write_list(self.full_series, self.series_path)
454 if self.series_dirty: write_list(self.full_series, self.series_path)
455 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
455 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
456
456
457 def removeundo(self, repo):
457 def removeundo(self, repo):
458 undo = repo.sjoin('undo')
458 undo = repo.sjoin('undo')
459 if not os.path.exists(undo):
459 if not os.path.exists(undo):
460 return
460 return
461 try:
461 try:
462 os.unlink(undo)
462 os.unlink(undo)
463 except OSError, inst:
463 except OSError, inst:
464 self.ui.warn(_('error removing undo: %s\n') % str(inst))
464 self.ui.warn(_('error removing undo: %s\n') % str(inst))
465
465
466 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
466 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
467 fp=None, changes=None, opts={}):
467 fp=None, changes=None, opts={}):
468 stat = opts.get('stat')
468 stat = opts.get('stat')
469 if stat:
469 if stat:
470 opts['unified'] = '0'
470 opts['unified'] = '0'
471
471
472 m = cmdutil.match(repo, files, opts)
472 m = cmdutil.match(repo, files, opts)
473 chunks = patch.diff(repo, node1, node2, m, changes, diffopts)
473 chunks = patch.diff(repo, node1, node2, m, changes, diffopts)
474 write = fp is None and repo.ui.write or fp.write
474 write = fp is None and repo.ui.write or fp.write
475 if stat:
475 if stat:
476 width = self.ui.interactive() and util.termwidth() or 80
476 width = self.ui.interactive() and util.termwidth() or 80
477 write(patch.diffstat(util.iterlines(chunks), width=width,
477 write(patch.diffstat(util.iterlines(chunks), width=width,
478 git=diffopts.git))
478 git=diffopts.git))
479 else:
479 else:
480 for chunk in chunks:
480 for chunk in chunks:
481 write(chunk)
481 write(chunk)
482
482
483 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
483 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
484 # first try just applying the patch
484 # first try just applying the patch
485 (err, n) = self.apply(repo, [ patch ], update_status=False,
485 (err, n) = self.apply(repo, [ patch ], update_status=False,
486 strict=True, merge=rev)
486 strict=True, merge=rev)
487
487
488 if err == 0:
488 if err == 0:
489 return (err, n)
489 return (err, n)
490
490
491 if n is None:
491 if n is None:
492 raise util.Abort(_("apply failed for patch %s") % patch)
492 raise util.Abort(_("apply failed for patch %s") % patch)
493
493
494 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
494 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
495
495
496 # apply failed, strip away that rev and merge.
496 # apply failed, strip away that rev and merge.
497 hg.clean(repo, head)
497 hg.clean(repo, head)
498 self.strip(repo, n, update=False, backup='strip')
498 self.strip(repo, n, update=False, backup='strip')
499
499
500 ctx = repo[rev]
500 ctx = repo[rev]
501 ret = hg.merge(repo, rev)
501 ret = hg.merge(repo, rev)
502 if ret:
502 if ret:
503 raise util.Abort(_("update returned %d") % ret)
503 raise util.Abort(_("update returned %d") % ret)
504 n = repo.commit(ctx.description(), ctx.user(), force=True)
504 n = repo.commit(ctx.description(), ctx.user(), force=True)
505 if n is None:
505 if n is None:
506 raise util.Abort(_("repo commit failed"))
506 raise util.Abort(_("repo commit failed"))
507 try:
507 try:
508 ph = patchheader(mergeq.join(patch))
508 ph = patchheader(mergeq.join(patch))
509 except:
509 except:
510 raise util.Abort(_("unable to read %s") % patch)
510 raise util.Abort(_("unable to read %s") % patch)
511
511
512 diffopts = self.patchopts(diffopts, patch)
512 diffopts = self.patchopts(diffopts, patch)
513 patchf = self.opener(patch, "w")
513 patchf = self.opener(patch, "w")
514 comments = str(ph)
514 comments = str(ph)
515 if comments:
515 if comments:
516 patchf.write(comments)
516 patchf.write(comments)
517 self.printdiff(repo, diffopts, head, n, fp=patchf)
517 self.printdiff(repo, diffopts, head, n, fp=patchf)
518 patchf.close()
518 patchf.close()
519 self.removeundo(repo)
519 self.removeundo(repo)
520 return (0, n)
520 return (0, n)
521
521
522 def qparents(self, repo, rev=None):
522 def qparents(self, repo, rev=None):
523 if rev is None:
523 if rev is None:
524 (p1, p2) = repo.dirstate.parents()
524 (p1, p2) = repo.dirstate.parents()
525 if p2 == nullid:
525 if p2 == nullid:
526 return p1
526 return p1
527 if len(self.applied) == 0:
527 if len(self.applied) == 0:
528 return None
528 return None
529 return bin(self.applied[-1].rev)
529 return bin(self.applied[-1].rev)
530 pp = repo.changelog.parents(rev)
530 pp = repo.changelog.parents(rev)
531 if pp[1] != nullid:
531 if pp[1] != nullid:
532 arevs = [ x.rev for x in self.applied ]
532 arevs = [ x.rev for x in self.applied ]
533 p0 = hex(pp[0])
533 p0 = hex(pp[0])
534 p1 = hex(pp[1])
534 p1 = hex(pp[1])
535 if p0 in arevs:
535 if p0 in arevs:
536 return pp[0]
536 return pp[0]
537 if p1 in arevs:
537 if p1 in arevs:
538 return pp[1]
538 return pp[1]
539 return pp[0]
539 return pp[0]
540
540
541 def mergepatch(self, repo, mergeq, series, diffopts):
541 def mergepatch(self, repo, mergeq, series, diffopts):
542 if len(self.applied) == 0:
542 if len(self.applied) == 0:
543 # each of the patches merged in will have two parents. This
543 # each of the patches merged in will have two parents. This
544 # can confuse the qrefresh, qdiff, and strip code because it
544 # can confuse the qrefresh, qdiff, and strip code because it
545 # needs to know which parent is actually in the patch queue.
545 # needs to know which parent is actually in the patch queue.
546 # so, we insert a merge marker with only one parent. This way
546 # so, we insert a merge marker with only one parent. This way
547 # the first patch in the queue is never a merge patch
547 # the first patch in the queue is never a merge patch
548 #
548 #
549 pname = ".hg.patches.merge.marker"
549 pname = ".hg.patches.merge.marker"
550 n = repo.commit('[mq]: merge marker', force=True)
550 n = repo.commit('[mq]: merge marker', force=True)
551 self.removeundo(repo)
551 self.removeundo(repo)
552 self.applied.append(statusentry(hex(n), pname))
552 self.applied.append(statusentry(hex(n), pname))
553 self.applied_dirty = 1
553 self.applied_dirty = 1
554
554
555 head = self.qparents(repo)
555 head = self.qparents(repo)
556
556
557 for patch in series:
557 for patch in series:
558 patch = mergeq.lookup(patch, strict=True)
558 patch = mergeq.lookup(patch, strict=True)
559 if not patch:
559 if not patch:
560 self.ui.warn(_("patch %s does not exist\n") % patch)
560 self.ui.warn(_("patch %s does not exist\n") % patch)
561 return (1, None)
561 return (1, None)
562 pushable, reason = self.pushable(patch)
562 pushable, reason = self.pushable(patch)
563 if not pushable:
563 if not pushable:
564 self.explain_pushable(patch, all_patches=True)
564 self.explain_pushable(patch, all_patches=True)
565 continue
565 continue
566 info = mergeq.isapplied(patch)
566 info = mergeq.isapplied(patch)
567 if not info:
567 if not info:
568 self.ui.warn(_("patch %s is not applied\n") % patch)
568 self.ui.warn(_("patch %s is not applied\n") % patch)
569 return (1, None)
569 return (1, None)
570 rev = bin(info[1])
570 rev = bin(info[1])
571 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
571 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
572 if head:
572 if head:
573 self.applied.append(statusentry(hex(head), patch))
573 self.applied.append(statusentry(hex(head), patch))
574 self.applied_dirty = 1
574 self.applied_dirty = 1
575 if err:
575 if err:
576 return (err, head)
576 return (err, head)
577 self.save_dirty()
577 self.save_dirty()
578 return (0, head)
578 return (0, head)
579
579
580 def patch(self, repo, patchfile):
580 def patch(self, repo, patchfile):
581 '''Apply patchfile to the working directory.
581 '''Apply patchfile to the working directory.
582 patchfile: name of patch file'''
582 patchfile: name of patch file'''
583 files = {}
583 files = {}
584 try:
584 try:
585 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
585 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
586 files=files, eolmode=None)
586 files=files, eolmode=None)
587 except Exception, inst:
587 except Exception, inst:
588 self.ui.note(str(inst) + '\n')
588 self.ui.note(str(inst) + '\n')
589 if not self.ui.verbose:
589 if not self.ui.verbose:
590 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
590 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
591 return (False, files, False)
591 return (False, files, False)
592
592
593 return (True, files, fuzz)
593 return (True, files, fuzz)
594
594
595 def apply(self, repo, series, list=False, update_status=True,
595 def apply(self, repo, series, list=False, update_status=True,
596 strict=False, patchdir=None, merge=None, all_files={}):
596 strict=False, patchdir=None, merge=None, all_files={}):
597 wlock = lock = tr = None
597 wlock = lock = tr = None
598 try:
598 try:
599 wlock = repo.wlock()
599 wlock = repo.wlock()
600 lock = repo.lock()
600 lock = repo.lock()
601 tr = repo.transaction()
601 tr = repo.transaction()
602 try:
602 try:
603 ret = self._apply(repo, series, list, update_status,
603 ret = self._apply(repo, series, list, update_status,
604 strict, patchdir, merge, all_files=all_files)
604 strict, patchdir, merge, all_files=all_files)
605 tr.close()
605 tr.close()
606 self.save_dirty()
606 self.save_dirty()
607 return ret
607 return ret
608 except:
608 except:
609 try:
609 try:
610 tr.abort()
610 tr.abort()
611 finally:
611 finally:
612 repo.invalidate()
612 repo.invalidate()
613 repo.dirstate.invalidate()
613 repo.dirstate.invalidate()
614 raise
614 raise
615 finally:
615 finally:
616 del tr
616 del tr
617 release(lock, wlock)
617 release(lock, wlock)
618 self.removeundo(repo)
618 self.removeundo(repo)
619
619
620 def _apply(self, repo, series, list=False, update_status=True,
620 def _apply(self, repo, series, list=False, update_status=True,
621 strict=False, patchdir=None, merge=None, all_files={}):
621 strict=False, patchdir=None, merge=None, all_files={}):
622 '''returns (error, hash)
622 '''returns (error, hash)
623 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
623 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
624 # TODO unify with commands.py
624 # TODO unify with commands.py
625 if not patchdir:
625 if not patchdir:
626 patchdir = self.path
626 patchdir = self.path
627 err = 0
627 err = 0
628 n = None
628 n = None
629 for patchname in series:
629 for patchname in series:
630 pushable, reason = self.pushable(patchname)
630 pushable, reason = self.pushable(patchname)
631 if not pushable:
631 if not pushable:
632 self.explain_pushable(patchname, all_patches=True)
632 self.explain_pushable(patchname, all_patches=True)
633 continue
633 continue
634 self.ui.status(_("applying %s\n") % patchname)
634 self.ui.status(_("applying %s\n") % patchname)
635 pf = os.path.join(patchdir, patchname)
635 pf = os.path.join(patchdir, patchname)
636
636
637 try:
637 try:
638 ph = patchheader(self.join(patchname))
638 ph = patchheader(self.join(patchname))
639 except:
639 except:
640 self.ui.warn(_("unable to read %s\n") % patchname)
640 self.ui.warn(_("unable to read %s\n") % patchname)
641 err = 1
641 err = 1
642 break
642 break
643
643
644 message = ph.message
644 message = ph.message
645 if not message:
645 if not message:
646 message = _("imported patch %s\n") % patchname
646 message = _("imported patch %s\n") % patchname
647 else:
647 else:
648 if list:
648 if list:
649 message.append(_("\nimported patch %s") % patchname)
649 message.append(_("\nimported patch %s") % patchname)
650 message = '\n'.join(message)
650 message = '\n'.join(message)
651
651
652 if ph.haspatch:
652 if ph.haspatch:
653 (patcherr, files, fuzz) = self.patch(repo, pf)
653 (patcherr, files, fuzz) = self.patch(repo, pf)
654 all_files.update(files)
654 all_files.update(files)
655 patcherr = not patcherr
655 patcherr = not patcherr
656 else:
656 else:
657 self.ui.warn(_("patch %s is empty\n") % patchname)
657 self.ui.warn(_("patch %s is empty\n") % patchname)
658 patcherr, files, fuzz = 0, [], 0
658 patcherr, files, fuzz = 0, [], 0
659
659
660 if merge and files:
660 if merge and files:
661 # Mark as removed/merged and update dirstate parent info
661 # Mark as removed/merged and update dirstate parent info
662 removed = []
662 removed = []
663 merged = []
663 merged = []
664 for f in files:
664 for f in files:
665 if os.path.exists(repo.wjoin(f)):
665 if os.path.exists(repo.wjoin(f)):
666 merged.append(f)
666 merged.append(f)
667 else:
667 else:
668 removed.append(f)
668 removed.append(f)
669 for f in removed:
669 for f in removed:
670 repo.dirstate.remove(f)
670 repo.dirstate.remove(f)
671 for f in merged:
671 for f in merged:
672 repo.dirstate.merge(f)
672 repo.dirstate.merge(f)
673 p1, p2 = repo.dirstate.parents()
673 p1, p2 = repo.dirstate.parents()
674 repo.dirstate.setparents(p1, merge)
674 repo.dirstate.setparents(p1, merge)
675
675
676 files = patch.updatedir(self.ui, repo, files)
676 files = patch.updatedir(self.ui, repo, files)
677 match = cmdutil.matchfiles(repo, files or [])
677 match = cmdutil.matchfiles(repo, files or [])
678 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
678 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
679
679
680 if n is None:
680 if n is None:
681 raise util.Abort(_("repo commit failed"))
681 raise util.Abort(_("repo commit failed"))
682
682
683 if update_status:
683 if update_status:
684 self.applied.append(statusentry(hex(n), patchname))
684 self.applied.append(statusentry(hex(n), patchname))
685
685
686 if patcherr:
686 if patcherr:
687 self.ui.warn(_("patch failed, rejects left in working dir\n"))
687 self.ui.warn(_("patch failed, rejects left in working dir\n"))
688 err = 2
688 err = 2
689 break
689 break
690
690
691 if fuzz and strict:
691 if fuzz and strict:
692 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
692 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
693 err = 3
693 err = 3
694 break
694 break
695 return (err, n)
695 return (err, n)
696
696
697 def _cleanup(self, patches, numrevs, keep=False):
697 def _cleanup(self, patches, numrevs, keep=False):
698 if not keep:
698 if not keep:
699 r = self.qrepo()
699 r = self.qrepo()
700 if r:
700 if r:
701 r.remove(patches, True)
701 r.remove(patches, True)
702 else:
702 else:
703 for p in patches:
703 for p in patches:
704 os.unlink(self.join(p))
704 os.unlink(self.join(p))
705
705
706 if numrevs:
706 if numrevs:
707 del self.applied[:numrevs]
707 del self.applied[:numrevs]
708 self.applied_dirty = 1
708 self.applied_dirty = 1
709
709
710 for i in sorted([self.find_series(p) for p in patches], reverse=True):
710 for i in sorted([self.find_series(p) for p in patches], reverse=True):
711 del self.full_series[i]
711 del self.full_series[i]
712 self.parse_series()
712 self.parse_series()
713 self.series_dirty = 1
713 self.series_dirty = 1
714
714
715 def _revpatches(self, repo, revs):
715 def _revpatches(self, repo, revs):
716 firstrev = repo[self.applied[0].rev].rev()
716 firstrev = repo[self.applied[0].rev].rev()
717 patches = []
717 patches = []
718 for i, rev in enumerate(revs):
718 for i, rev in enumerate(revs):
719
719
720 if rev < firstrev:
720 if rev < firstrev:
721 raise util.Abort(_('revision %d is not managed') % rev)
721 raise util.Abort(_('revision %d is not managed') % rev)
722
722
723 ctx = repo[rev]
723 ctx = repo[rev]
724 base = bin(self.applied[i].rev)
724 base = bin(self.applied[i].rev)
725 if ctx.node() != base:
725 if ctx.node() != base:
726 msg = _('cannot delete revision %d above applied patches')
726 msg = _('cannot delete revision %d above applied patches')
727 raise util.Abort(msg % rev)
727 raise util.Abort(msg % rev)
728
728
729 patch = self.applied[i].name
729 patch = self.applied[i].name
730 for fmt in ('[mq]: %s', 'imported patch %s'):
730 for fmt in ('[mq]: %s', 'imported patch %s'):
731 if ctx.description() == fmt % patch:
731 if ctx.description() == fmt % patch:
732 msg = _('patch %s finalized without changeset message\n')
732 msg = _('patch %s finalized without changeset message\n')
733 repo.ui.status(msg % patch)
733 repo.ui.status(msg % patch)
734 break
734 break
735
735
736 patches.append(patch)
736 patches.append(patch)
737 return patches
737 return patches
738
738
739 def finish(self, repo, revs):
739 def finish(self, repo, revs):
740 patches = self._revpatches(repo, sorted(revs))
740 patches = self._revpatches(repo, sorted(revs))
741 self._cleanup(patches, len(patches))
741 self._cleanup(patches, len(patches))
742
742
743 def delete(self, repo, patches, opts):
743 def delete(self, repo, patches, opts):
744 if not patches and not opts.get('rev'):
744 if not patches and not opts.get('rev'):
745 raise util.Abort(_('qdelete requires at least one revision or '
745 raise util.Abort(_('qdelete requires at least one revision or '
746 'patch name'))
746 'patch name'))
747
747
748 realpatches = []
748 realpatches = []
749 for patch in patches:
749 for patch in patches:
750 patch = self.lookup(patch, strict=True)
750 patch = self.lookup(patch, strict=True)
751 info = self.isapplied(patch)
751 info = self.isapplied(patch)
752 if info:
752 if info:
753 raise util.Abort(_("cannot delete applied patch %s") % patch)
753 raise util.Abort(_("cannot delete applied patch %s") % patch)
754 if patch not in self.series:
754 if patch not in self.series:
755 raise util.Abort(_("patch %s not in series file") % patch)
755 raise util.Abort(_("patch %s not in series file") % patch)
756 realpatches.append(patch)
756 realpatches.append(patch)
757
757
758 numrevs = 0
758 numrevs = 0
759 if opts.get('rev'):
759 if opts.get('rev'):
760 if not self.applied:
760 if not self.applied:
761 raise util.Abort(_('no patches applied'))
761 raise util.Abort(_('no patches applied'))
762 revs = cmdutil.revrange(repo, opts['rev'])
762 revs = cmdutil.revrange(repo, opts['rev'])
763 if len(revs) > 1 and revs[0] > revs[1]:
763 if len(revs) > 1 and revs[0] > revs[1]:
764 revs.reverse()
764 revs.reverse()
765 revpatches = self._revpatches(repo, revs)
765 revpatches = self._revpatches(repo, revs)
766 realpatches += revpatches
766 realpatches += revpatches
767 numrevs = len(revpatches)
767 numrevs = len(revpatches)
768
768
769 self._cleanup(realpatches, numrevs, opts.get('keep'))
769 self._cleanup(realpatches, numrevs, opts.get('keep'))
770
770
771 def check_toppatch(self, repo):
771 def check_toppatch(self, repo):
772 if len(self.applied) > 0:
772 if len(self.applied) > 0:
773 top = bin(self.applied[-1].rev)
773 top = bin(self.applied[-1].rev)
774 patch = self.applied[-1].name
774 pp = repo.dirstate.parents()
775 pp = repo.dirstate.parents()
775 if top not in pp:
776 if top not in pp:
776 raise util.Abort(_("working directory revision is not qtip"))
777 raise util.Abort(_("working directory revision is not qtip"))
777 return top
778 return top, patch
778 return None
779 return None, None
780
779 def check_localchanges(self, repo, force=False, refresh=True):
781 def check_localchanges(self, repo, force=False, refresh=True):
780 m, a, r, d = repo.status()[:4]
782 m, a, r, d = repo.status()[:4]
781 if m or a or r or d:
783 if m or a or r or d:
782 if not force:
784 if not force:
783 if refresh:
785 if refresh:
784 raise util.Abort(_("local changes found, refresh first"))
786 raise util.Abort(_("local changes found, refresh first"))
785 else:
787 else:
786 raise util.Abort(_("local changes found"))
788 raise util.Abort(_("local changes found"))
787 return m, a, r, d
789 return m, a, r, d
788
790
789 _reserved = ('series', 'status', 'guards')
791 _reserved = ('series', 'status', 'guards')
790 def check_reserved_name(self, name):
792 def check_reserved_name(self, name):
791 if (name in self._reserved or name.startswith('.hg')
793 if (name in self._reserved or name.startswith('.hg')
792 or name.startswith('.mq')):
794 or name.startswith('.mq')):
793 raise util.Abort(_('"%s" cannot be used as the name of a patch')
795 raise util.Abort(_('"%s" cannot be used as the name of a patch')
794 % name)
796 % name)
795
797
796 def new(self, repo, patchfn, *pats, **opts):
798 def new(self, repo, patchfn, *pats, **opts):
797 """options:
799 """options:
798 msg: a string or a no-argument function returning a string
800 msg: a string or a no-argument function returning a string
799 """
801 """
800 msg = opts.get('msg')
802 msg = opts.get('msg')
801 force = opts.get('force')
803 force = opts.get('force')
802 user = opts.get('user')
804 user = opts.get('user')
803 date = opts.get('date')
805 date = opts.get('date')
804 if date:
806 if date:
805 date = util.parsedate(date)
807 date = util.parsedate(date)
806 diffopts = self.diffopts({'git': opts.get('git')})
808 diffopts = self.diffopts({'git': opts.get('git')})
807 self.check_reserved_name(patchfn)
809 self.check_reserved_name(patchfn)
808 if os.path.exists(self.join(patchfn)):
810 if os.path.exists(self.join(patchfn)):
809 raise util.Abort(_('patch "%s" already exists') % patchfn)
811 raise util.Abort(_('patch "%s" already exists') % patchfn)
810 if opts.get('include') or opts.get('exclude') or pats:
812 if opts.get('include') or opts.get('exclude') or pats:
811 match = cmdutil.match(repo, pats, opts)
813 match = cmdutil.match(repo, pats, opts)
812 # detect missing files in pats
814 # detect missing files in pats
813 def badfn(f, msg):
815 def badfn(f, msg):
814 raise util.Abort('%s: %s' % (f, msg))
816 raise util.Abort('%s: %s' % (f, msg))
815 match.bad = badfn
817 match.bad = badfn
816 m, a, r, d = repo.status(match=match)[:4]
818 m, a, r, d = repo.status(match=match)[:4]
817 else:
819 else:
818 m, a, r, d = self.check_localchanges(repo, force)
820 m, a, r, d = self.check_localchanges(repo, force)
819 match = cmdutil.matchfiles(repo, m + a + r)
821 match = cmdutil.matchfiles(repo, m + a + r)
820 if force:
822 if force:
821 p = repo[None].parents()
823 p = repo[None].parents()
822 if len(p) > 1:
824 if len(p) > 1:
823 raise util.Abort(_('cannot manage merge changesets'))
825 raise util.Abort(_('cannot manage merge changesets'))
824 commitfiles = m + a + r
826 commitfiles = m + a + r
825 self.check_toppatch(repo)
827 self.check_toppatch(repo)
826 insert = self.full_series_end()
828 insert = self.full_series_end()
827 wlock = repo.wlock()
829 wlock = repo.wlock()
828 try:
830 try:
829 # if patch file write fails, abort early
831 # if patch file write fails, abort early
830 p = self.opener(patchfn, "w")
832 p = self.opener(patchfn, "w")
831 try:
833 try:
832 if date:
834 if date:
833 p.write("# HG changeset patch\n")
835 p.write("# HG changeset patch\n")
834 if user:
836 if user:
835 p.write("# User " + user + "\n")
837 p.write("# User " + user + "\n")
836 p.write("# Date %d %d\n\n" % date)
838 p.write("# Date %d %d\n\n" % date)
837 elif user:
839 elif user:
838 p.write("From: " + user + "\n\n")
840 p.write("From: " + user + "\n\n")
839
841
840 if hasattr(msg, '__call__'):
842 if hasattr(msg, '__call__'):
841 msg = msg()
843 msg = msg()
842 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
844 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
843 n = repo.commit(commitmsg, user, date, match=match, force=True)
845 n = repo.commit(commitmsg, user, date, match=match, force=True)
844 if n is None:
846 if n is None:
845 raise util.Abort(_("repo commit failed"))
847 raise util.Abort(_("repo commit failed"))
846 try:
848 try:
847 self.full_series[insert:insert] = [patchfn]
849 self.full_series[insert:insert] = [patchfn]
848 self.applied.append(statusentry(hex(n), patchfn))
850 self.applied.append(statusentry(hex(n), patchfn))
849 self.parse_series()
851 self.parse_series()
850 self.series_dirty = 1
852 self.series_dirty = 1
851 self.applied_dirty = 1
853 self.applied_dirty = 1
852 if msg:
854 if msg:
853 msg = msg + "\n\n"
855 msg = msg + "\n\n"
854 p.write(msg)
856 p.write(msg)
855 if commitfiles:
857 if commitfiles:
856 parent = self.qparents(repo, n)
858 parent = self.qparents(repo, n)
857 chunks = patch.diff(repo, node1=parent, node2=n,
859 chunks = patch.diff(repo, node1=parent, node2=n,
858 match=match, opts=diffopts)
860 match=match, opts=diffopts)
859 for chunk in chunks:
861 for chunk in chunks:
860 p.write(chunk)
862 p.write(chunk)
861 p.close()
863 p.close()
862 wlock.release()
864 wlock.release()
863 wlock = None
865 wlock = None
864 r = self.qrepo()
866 r = self.qrepo()
865 if r: r.add([patchfn])
867 if r: r.add([patchfn])
866 except:
868 except:
867 repo.rollback()
869 repo.rollback()
868 raise
870 raise
869 except Exception:
871 except Exception:
870 patchpath = self.join(patchfn)
872 patchpath = self.join(patchfn)
871 try:
873 try:
872 os.unlink(patchpath)
874 os.unlink(patchpath)
873 except:
875 except:
874 self.ui.warn(_('error unlinking %s\n') % patchpath)
876 self.ui.warn(_('error unlinking %s\n') % patchpath)
875 raise
877 raise
876 self.removeundo(repo)
878 self.removeundo(repo)
877 finally:
879 finally:
878 release(wlock)
880 release(wlock)
879
881
880 def strip(self, repo, rev, update=True, backup="all", force=None):
882 def strip(self, repo, rev, update=True, backup="all", force=None):
881 wlock = lock = None
883 wlock = lock = None
882 try:
884 try:
883 wlock = repo.wlock()
885 wlock = repo.wlock()
884 lock = repo.lock()
886 lock = repo.lock()
885
887
886 if update:
888 if update:
887 self.check_localchanges(repo, force=force, refresh=False)
889 self.check_localchanges(repo, force=force, refresh=False)
888 urev = self.qparents(repo, rev)
890 urev = self.qparents(repo, rev)
889 hg.clean(repo, urev)
891 hg.clean(repo, urev)
890 repo.dirstate.write()
892 repo.dirstate.write()
891
893
892 self.removeundo(repo)
894 self.removeundo(repo)
893 repair.strip(self.ui, repo, rev, backup)
895 repair.strip(self.ui, repo, rev, backup)
894 # strip may have unbundled a set of backed up revisions after
896 # strip may have unbundled a set of backed up revisions after
895 # the actual strip
897 # the actual strip
896 self.removeundo(repo)
898 self.removeundo(repo)
897 finally:
899 finally:
898 release(lock, wlock)
900 release(lock, wlock)
899
901
900 def isapplied(self, patch):
902 def isapplied(self, patch):
901 """returns (index, rev, patch)"""
903 """returns (index, rev, patch)"""
902 for i, a in enumerate(self.applied):
904 for i, a in enumerate(self.applied):
903 if a.name == patch:
905 if a.name == patch:
904 return (i, a.rev, a.name)
906 return (i, a.rev, a.name)
905 return None
907 return None
906
908
907 # 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
908 # variations. If strict is passed, we try only #1
910 # variations. If strict is passed, we try only #1
909 #
911 #
910 # 1) a number to indicate an offset in the series file
912 # 1) a number to indicate an offset in the series file
911 # 2) a unique substring of the patch name was given
913 # 2) a unique substring of the patch name was given
912 # 3) patchname[-+]num to indicate an offset in the series file
914 # 3) patchname[-+]num to indicate an offset in the series file
913 def lookup(self, patch, strict=False):
915 def lookup(self, patch, strict=False):
914 patch = patch and str(patch)
916 patch = patch and str(patch)
915
917
916 def partial_name(s):
918 def partial_name(s):
917 if s in self.series:
919 if s in self.series:
918 return s
920 return s
919 matches = [x for x in self.series if s in x]
921 matches = [x for x in self.series if s in x]
920 if len(matches) > 1:
922 if len(matches) > 1:
921 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
923 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
922 for m in matches:
924 for m in matches:
923 self.ui.warn(' %s\n' % m)
925 self.ui.warn(' %s\n' % m)
924 return None
926 return None
925 if matches:
927 if matches:
926 return matches[0]
928 return matches[0]
927 if len(self.series) > 0 and len(self.applied) > 0:
929 if len(self.series) > 0 and len(self.applied) > 0:
928 if s == 'qtip':
930 if s == 'qtip':
929 return self.series[self.series_end(True)-1]
931 return self.series[self.series_end(True)-1]
930 if s == 'qbase':
932 if s == 'qbase':
931 return self.series[0]
933 return self.series[0]
932 return None
934 return None
933
935
934 if patch is None:
936 if patch is None:
935 return None
937 return None
936 if patch in self.series:
938 if patch in self.series:
937 return patch
939 return patch
938
940
939 if not os.path.isfile(self.join(patch)):
941 if not os.path.isfile(self.join(patch)):
940 try:
942 try:
941 sno = int(patch)
943 sno = int(patch)
942 except(ValueError, OverflowError):
944 except(ValueError, OverflowError):
943 pass
945 pass
944 else:
946 else:
945 if -len(self.series) <= sno < len(self.series):
947 if -len(self.series) <= sno < len(self.series):
946 return self.series[sno]
948 return self.series[sno]
947
949
948 if not strict:
950 if not strict:
949 res = partial_name(patch)
951 res = partial_name(patch)
950 if res:
952 if res:
951 return res
953 return res
952 minus = patch.rfind('-')
954 minus = patch.rfind('-')
953 if minus >= 0:
955 if minus >= 0:
954 res = partial_name(patch[:minus])
956 res = partial_name(patch[:minus])
955 if res:
957 if res:
956 i = self.series.index(res)
958 i = self.series.index(res)
957 try:
959 try:
958 off = int(patch[minus+1:] or 1)
960 off = int(patch[minus+1:] or 1)
959 except(ValueError, OverflowError):
961 except(ValueError, OverflowError):
960 pass
962 pass
961 else:
963 else:
962 if i - off >= 0:
964 if i - off >= 0:
963 return self.series[i - off]
965 return self.series[i - off]
964 plus = patch.rfind('+')
966 plus = patch.rfind('+')
965 if plus >= 0:
967 if plus >= 0:
966 res = partial_name(patch[:plus])
968 res = partial_name(patch[:plus])
967 if res:
969 if res:
968 i = self.series.index(res)
970 i = self.series.index(res)
969 try:
971 try:
970 off = int(patch[plus+1:] or 1)
972 off = int(patch[plus+1:] or 1)
971 except(ValueError, OverflowError):
973 except(ValueError, OverflowError):
972 pass
974 pass
973 else:
975 else:
974 if i + off < len(self.series):
976 if i + off < len(self.series):
975 return self.series[i + off]
977 return self.series[i + off]
976 raise util.Abort(_("patch %s not in series") % patch)
978 raise util.Abort(_("patch %s not in series") % patch)
977
979
978 def push(self, repo, patch=None, force=False, list=False,
980 def push(self, repo, patch=None, force=False, list=False,
979 mergeq=None, all=False):
981 mergeq=None, all=False):
980 diffopts = self.diffopts()
982 diffopts = self.diffopts()
981 wlock = repo.wlock()
983 wlock = repo.wlock()
982 try:
984 try:
983 if repo.dirstate.parents()[0] not in repo.heads():
985 if repo.dirstate.parents()[0] not in repo.heads():
984 self.ui.status(_("(working directory not at a head)\n"))
986 self.ui.status(_("(working directory not at a head)\n"))
985
987
986 if not self.series:
988 if not self.series:
987 self.ui.warn(_('no patches in series\n'))
989 self.ui.warn(_('no patches in series\n'))
988 return 0
990 return 0
989
991
990 patch = self.lookup(patch)
992 patch = self.lookup(patch)
991 # Suppose our series file is: A B C and the current 'top'
993 # Suppose our series file is: A B C and the current 'top'
992 # patch is B. qpush C should be performed (moving forward)
994 # patch is B. qpush C should be performed (moving forward)
993 # qpush B is a NOP (no change) qpush A is an error (can't
995 # qpush B is a NOP (no change) qpush A is an error (can't
994 # go backwards with qpush)
996 # go backwards with qpush)
995 if patch:
997 if patch:
996 info = self.isapplied(patch)
998 info = self.isapplied(patch)
997 if info:
999 if info:
998 if info[0] < len(self.applied) - 1:
1000 if info[0] < len(self.applied) - 1:
999 raise util.Abort(
1001 raise util.Abort(
1000 _("cannot push to a previous patch: %s") % patch)
1002 _("cannot push to a previous patch: %s") % patch)
1001 self.ui.warn(
1003 self.ui.warn(
1002 _('qpush: %s is already at the top\n') % patch)
1004 _('qpush: %s is already at the top\n') % patch)
1003 return
1005 return
1004 pushable, reason = self.pushable(patch)
1006 pushable, reason = self.pushable(patch)
1005 if not pushable:
1007 if not pushable:
1006 if reason:
1008 if reason:
1007 reason = _('guarded by %r') % reason
1009 reason = _('guarded by %r') % reason
1008 else:
1010 else:
1009 reason = _('no matching guards')
1011 reason = _('no matching guards')
1010 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1012 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1011 return 1
1013 return 1
1012 elif all:
1014 elif all:
1013 patch = self.series[-1]
1015 patch = self.series[-1]
1014 if self.isapplied(patch):
1016 if self.isapplied(patch):
1015 self.ui.warn(_('all patches are currently applied\n'))
1017 self.ui.warn(_('all patches are currently applied\n'))
1016 return 0
1018 return 0
1017
1019
1018 # Following the above example, starting at 'top' of B:
1020 # Following the above example, starting at 'top' of B:
1019 # qpush should be performed (pushes C), but a subsequent
1021 # qpush should be performed (pushes C), but a subsequent
1020 # qpush without an argument is an error (nothing to
1022 # qpush without an argument is an error (nothing to
1021 # apply). This allows a loop of "...while hg qpush..." to
1023 # apply). This allows a loop of "...while hg qpush..." to
1022 # work as it detects an error when done
1024 # work as it detects an error when done
1023 start = self.series_end()
1025 start = self.series_end()
1024 if start == len(self.series):
1026 if start == len(self.series):
1025 self.ui.warn(_('patch series already fully applied\n'))
1027 self.ui.warn(_('patch series already fully applied\n'))
1026 return 1
1028 return 1
1027 if not force:
1029 if not force:
1028 self.check_localchanges(repo)
1030 self.check_localchanges(repo)
1029
1031
1030 self.applied_dirty = 1
1032 self.applied_dirty = 1
1031 if start > 0:
1033 if start > 0:
1032 self.check_toppatch(repo)
1034 self.check_toppatch(repo)
1033 if not patch:
1035 if not patch:
1034 patch = self.series[start]
1036 patch = self.series[start]
1035 end = start + 1
1037 end = start + 1
1036 else:
1038 else:
1037 end = self.series.index(patch, start) + 1
1039 end = self.series.index(patch, start) + 1
1038
1040
1039 s = self.series[start:end]
1041 s = self.series[start:end]
1040 all_files = {}
1042 all_files = {}
1041 try:
1043 try:
1042 if mergeq:
1044 if mergeq:
1043 ret = self.mergepatch(repo, mergeq, s, diffopts)
1045 ret = self.mergepatch(repo, mergeq, s, diffopts)
1044 else:
1046 else:
1045 ret = self.apply(repo, s, list, all_files=all_files)
1047 ret = self.apply(repo, s, list, all_files=all_files)
1046 except:
1048 except:
1047 self.ui.warn(_('cleaning up working directory...'))
1049 self.ui.warn(_('cleaning up working directory...'))
1048 node = repo.dirstate.parents()[0]
1050 node = repo.dirstate.parents()[0]
1049 hg.revert(repo, node, None)
1051 hg.revert(repo, node, None)
1050 unknown = repo.status(unknown=True)[4]
1052 unknown = repo.status(unknown=True)[4]
1051 # only remove unknown files that we know we touched or
1053 # only remove unknown files that we know we touched or
1052 # created while patching
1054 # created while patching
1053 for f in unknown:
1055 for f in unknown:
1054 if f in all_files:
1056 if f in all_files:
1055 util.unlink(repo.wjoin(f))
1057 util.unlink(repo.wjoin(f))
1056 self.ui.warn(_('done\n'))
1058 self.ui.warn(_('done\n'))
1057 raise
1059 raise
1058
1060
1059 if not self.applied:
1061 if not self.applied:
1060 return ret[0]
1062 return ret[0]
1061 top = self.applied[-1].name
1063 top = self.applied[-1].name
1062 if ret[0] and ret[0] > 1:
1064 if ret[0] and ret[0] > 1:
1063 msg = _("errors during apply, please fix and refresh %s\n")
1065 msg = _("errors during apply, please fix and refresh %s\n")
1064 self.ui.write(msg % top)
1066 self.ui.write(msg % top)
1065 else:
1067 else:
1066 self.ui.write(_("now at: %s\n") % top)
1068 self.ui.write(_("now at: %s\n") % top)
1067 return ret[0]
1069 return ret[0]
1068
1070
1069 finally:
1071 finally:
1070 wlock.release()
1072 wlock.release()
1071
1073
1072 def pop(self, repo, patch=None, force=False, update=True, all=False):
1074 def pop(self, repo, patch=None, force=False, update=True, all=False):
1073 def getfile(f, rev, flags):
1075 def getfile(f, rev, flags):
1074 t = repo.file(f).read(rev)
1076 t = repo.file(f).read(rev)
1075 repo.wwrite(f, t, flags)
1077 repo.wwrite(f, t, flags)
1076
1078
1077 wlock = repo.wlock()
1079 wlock = repo.wlock()
1078 try:
1080 try:
1079 if patch:
1081 if patch:
1080 # index, rev, patch
1082 # index, rev, patch
1081 info = self.isapplied(patch)
1083 info = self.isapplied(patch)
1082 if not info:
1084 if not info:
1083 patch = self.lookup(patch)
1085 patch = self.lookup(patch)
1084 info = self.isapplied(patch)
1086 info = self.isapplied(patch)
1085 if not info:
1087 if not info:
1086 raise util.Abort(_("patch %s is not applied") % patch)
1088 raise util.Abort(_("patch %s is not applied") % patch)
1087
1089
1088 if len(self.applied) == 0:
1090 if len(self.applied) == 0:
1089 # Allow qpop -a to work repeatedly,
1091 # Allow qpop -a to work repeatedly,
1090 # but not qpop without an argument
1092 # but not qpop without an argument
1091 self.ui.warn(_("no patches applied\n"))
1093 self.ui.warn(_("no patches applied\n"))
1092 return not all
1094 return not all
1093
1095
1094 if all:
1096 if all:
1095 start = 0
1097 start = 0
1096 elif patch:
1098 elif patch:
1097 start = info[0] + 1
1099 start = info[0] + 1
1098 else:
1100 else:
1099 start = len(self.applied) - 1
1101 start = len(self.applied) - 1
1100
1102
1101 if start >= len(self.applied):
1103 if start >= len(self.applied):
1102 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1104 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1103 return
1105 return
1104
1106
1105 if not update:
1107 if not update:
1106 parents = repo.dirstate.parents()
1108 parents = repo.dirstate.parents()
1107 rr = [ bin(x.rev) for x in self.applied ]
1109 rr = [ bin(x.rev) for x in self.applied ]
1108 for p in parents:
1110 for p in parents:
1109 if p in rr:
1111 if p in rr:
1110 self.ui.warn(_("qpop: forcing dirstate update\n"))
1112 self.ui.warn(_("qpop: forcing dirstate update\n"))
1111 update = True
1113 update = True
1112 else:
1114 else:
1113 parents = [p.hex() for p in repo[None].parents()]
1115 parents = [p.hex() for p in repo[None].parents()]
1114 needupdate = False
1116 needupdate = False
1115 for entry in self.applied[start:]:
1117 for entry in self.applied[start:]:
1116 if entry.rev in parents:
1118 if entry.rev in parents:
1117 needupdate = True
1119 needupdate = True
1118 break
1120 break
1119 update = needupdate
1121 update = needupdate
1120
1122
1121 if not force and update:
1123 if not force and update:
1122 self.check_localchanges(repo)
1124 self.check_localchanges(repo)
1123
1125
1124 self.applied_dirty = 1
1126 self.applied_dirty = 1
1125 end = len(self.applied)
1127 end = len(self.applied)
1126 rev = bin(self.applied[start].rev)
1128 rev = bin(self.applied[start].rev)
1127 if update:
1129 if update:
1128 top = self.check_toppatch(repo)
1130 top = self.check_toppatch(repo)[0]
1129
1131
1130 try:
1132 try:
1131 heads = repo.changelog.heads(rev)
1133 heads = repo.changelog.heads(rev)
1132 except error.LookupError:
1134 except error.LookupError:
1133 node = short(rev)
1135 node = short(rev)
1134 raise util.Abort(_('trying to pop unknown node %s') % node)
1136 raise util.Abort(_('trying to pop unknown node %s') % node)
1135
1137
1136 if heads != [bin(self.applied[-1].rev)]:
1138 if heads != [bin(self.applied[-1].rev)]:
1137 raise util.Abort(_("popping would remove a revision not "
1139 raise util.Abort(_("popping would remove a revision not "
1138 "managed by this patch queue"))
1140 "managed by this patch queue"))
1139
1141
1140 # we know there are no local changes, so we can make a simplified
1142 # we know there are no local changes, so we can make a simplified
1141 # form of hg.update.
1143 # form of hg.update.
1142 if update:
1144 if update:
1143 qp = self.qparents(repo, rev)
1145 qp = self.qparents(repo, rev)
1144 changes = repo.changelog.read(qp)
1146 changes = repo.changelog.read(qp)
1145 mmap = repo.manifest.read(changes[0])
1147 mmap = repo.manifest.read(changes[0])
1146 m, a, r, d = repo.status(qp, top)[:4]
1148 m, a, r, d = repo.status(qp, top)[:4]
1147 if d:
1149 if d:
1148 raise util.Abort(_("deletions found between repo revs"))
1150 raise util.Abort(_("deletions found between repo revs"))
1149 for f in a:
1151 for f in a:
1150 try:
1152 try:
1151 os.unlink(repo.wjoin(f))
1153 os.unlink(repo.wjoin(f))
1152 except OSError, e:
1154 except OSError, e:
1153 if e.errno != errno.ENOENT:
1155 if e.errno != errno.ENOENT:
1154 raise
1156 raise
1155 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1157 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1156 except: pass
1158 except: pass
1157 repo.dirstate.forget(f)
1159 repo.dirstate.forget(f)
1158 for f in m:
1160 for f in m:
1159 getfile(f, mmap[f], mmap.flags(f))
1161 getfile(f, mmap[f], mmap.flags(f))
1160 for f in r:
1162 for f in r:
1161 getfile(f, mmap[f], mmap.flags(f))
1163 getfile(f, mmap[f], mmap.flags(f))
1162 for f in m + r:
1164 for f in m + r:
1163 repo.dirstate.normal(f)
1165 repo.dirstate.normal(f)
1164 repo.dirstate.setparents(qp, nullid)
1166 repo.dirstate.setparents(qp, nullid)
1165 for patch in reversed(self.applied[start:end]):
1167 for patch in reversed(self.applied[start:end]):
1166 self.ui.status(_("popping %s\n") % patch.name)
1168 self.ui.status(_("popping %s\n") % patch.name)
1167 del self.applied[start:end]
1169 del self.applied[start:end]
1168 self.strip(repo, rev, update=False, backup='strip')
1170 self.strip(repo, rev, update=False, backup='strip')
1169 if len(self.applied):
1171 if len(self.applied):
1170 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1172 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1171 else:
1173 else:
1172 self.ui.write(_("patch queue now empty\n"))
1174 self.ui.write(_("patch queue now empty\n"))
1173 finally:
1175 finally:
1174 wlock.release()
1176 wlock.release()
1175
1177
1176 def diff(self, repo, pats, opts):
1178 def diff(self, repo, pats, opts):
1177 top = self.check_toppatch(repo)
1179 top, patch = self.check_toppatch(repo)
1178 if not top:
1180 if not top:
1179 self.ui.write(_("no patches applied\n"))
1181 self.ui.write(_("no patches applied\n"))
1180 return
1182 return
1181 qp = self.qparents(repo, top)
1183 qp = self.qparents(repo, top)
1182 if opts.get('reverse'):
1184 if opts.get('reverse'):
1183 node1, node2 = None, qp
1185 node1, node2 = None, qp
1184 else:
1186 else:
1185 node1, node2 = qp, None
1187 node1, node2 = qp, None
1186 diffopts = self.diffopts(opts)
1188 diffopts = self.diffopts(opts, patch)
1187 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1189 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1188
1190
1189 def refresh(self, repo, pats=None, **opts):
1191 def refresh(self, repo, pats=None, **opts):
1190 if len(self.applied) == 0:
1192 if len(self.applied) == 0:
1191 self.ui.write(_("no patches applied\n"))
1193 self.ui.write(_("no patches applied\n"))
1192 return 1
1194 return 1
1193 msg = opts.get('msg', '').rstrip()
1195 msg = opts.get('msg', '').rstrip()
1194 newuser = opts.get('user')
1196 newuser = opts.get('user')
1195 newdate = opts.get('date')
1197 newdate = opts.get('date')
1196 if newdate:
1198 if newdate:
1197 newdate = '%d %d' % util.parsedate(newdate)
1199 newdate = '%d %d' % util.parsedate(newdate)
1198 wlock = repo.wlock()
1200 wlock = repo.wlock()
1199 try:
1201 try:
1200 self.check_toppatch(repo)
1202 self.check_toppatch(repo)
1201 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1203 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1202 top = bin(top)
1204 top = bin(top)
1203 if repo.changelog.heads(top) != [top]:
1205 if repo.changelog.heads(top) != [top]:
1204 raise util.Abort(_("cannot refresh a revision with children"))
1206 raise util.Abort(_("cannot refresh a revision with children"))
1205 cparents = repo.changelog.parents(top)
1207 cparents = repo.changelog.parents(top)
1206 patchparent = self.qparents(repo, top)
1208 patchparent = self.qparents(repo, top)
1207 ph = patchheader(self.join(patchfn))
1209 ph = patchheader(self.join(patchfn))
1208 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1210 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1209 if msg:
1211 if msg:
1210 ph.setmessage(msg)
1212 ph.setmessage(msg)
1211 if newuser:
1213 if newuser:
1212 ph.setuser(newuser)
1214 ph.setuser(newuser)
1213 if newdate:
1215 if newdate:
1214 ph.setdate(newdate)
1216 ph.setdate(newdate)
1215
1217
1216 # only commit new patch when write is complete
1218 # only commit new patch when write is complete
1217 patchf = self.opener(patchfn, 'w', atomictemp=True)
1219 patchf = self.opener(patchfn, 'w', atomictemp=True)
1218
1220
1219 comments = str(ph)
1221 comments = str(ph)
1220 if comments:
1222 if comments:
1221 patchf.write(comments)
1223 patchf.write(comments)
1222
1224
1223 tip = repo.changelog.tip()
1225 tip = repo.changelog.tip()
1224 if top == tip:
1226 if top == tip:
1225 # if the top of our patch queue is also the tip, there is an
1227 # if the top of our patch queue is also the tip, there is an
1226 # optimization here. We update the dirstate in place and strip
1228 # optimization here. We update the dirstate in place and strip
1227 # off the tip commit. Then just commit the current directory
1229 # off the tip commit. Then just commit the current directory
1228 # tree. We can also send repo.commit the list of files
1230 # tree. We can also send repo.commit the list of files
1229 # changed to speed up the diff
1231 # changed to speed up the diff
1230 #
1232 #
1231 # in short mode, we only diff the files included in the
1233 # in short mode, we only diff the files included in the
1232 # patch already plus specified files
1234 # patch already plus specified files
1233 #
1235 #
1234 # this should really read:
1236 # this should really read:
1235 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1237 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1236 # but we do it backwards to take advantage of manifest/chlog
1238 # but we do it backwards to take advantage of manifest/chlog
1237 # caching against the next repo.status call
1239 # caching against the next repo.status call
1238 #
1240 #
1239 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1241 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1240 changes = repo.changelog.read(tip)
1242 changes = repo.changelog.read(tip)
1241 man = repo.manifest.read(changes[0])
1243 man = repo.manifest.read(changes[0])
1242 aaa = aa[:]
1244 aaa = aa[:]
1243 matchfn = cmdutil.match(repo, pats, opts)
1245 matchfn = cmdutil.match(repo, pats, opts)
1244 if opts.get('short'):
1246 if opts.get('short'):
1245 # if amending a patch, we start with existing
1247 # if amending a patch, we start with existing
1246 # files plus specified files - unfiltered
1248 # files plus specified files - unfiltered
1247 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1249 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1248 # filter with inc/exl options
1250 # filter with inc/exl options
1249 matchfn = cmdutil.match(repo, opts=opts)
1251 matchfn = cmdutil.match(repo, opts=opts)
1250 else:
1252 else:
1251 match = cmdutil.matchall(repo)
1253 match = cmdutil.matchall(repo)
1252 m, a, r, d = repo.status(match=match)[:4]
1254 m, a, r, d = repo.status(match=match)[:4]
1253
1255
1254 # we might end up with files that were added between
1256 # we might end up with files that were added between
1255 # tip and the dirstate parent, but then changed in the
1257 # tip and the dirstate parent, but then changed in the
1256 # local dirstate. in this case, we want them to only
1258 # local dirstate. in this case, we want them to only
1257 # show up in the added section
1259 # show up in the added section
1258 for x in m:
1260 for x in m:
1259 if x not in aa:
1261 if x not in aa:
1260 mm.append(x)
1262 mm.append(x)
1261 # we might end up with files added by the local dirstate that
1263 # we might end up with files added by the local dirstate that
1262 # were deleted by the patch. In this case, they should only
1264 # were deleted by the patch. In this case, they should only
1263 # show up in the changed section.
1265 # show up in the changed section.
1264 for x in a:
1266 for x in a:
1265 if x in dd:
1267 if x in dd:
1266 del dd[dd.index(x)]
1268 del dd[dd.index(x)]
1267 mm.append(x)
1269 mm.append(x)
1268 else:
1270 else:
1269 aa.append(x)
1271 aa.append(x)
1270 # make sure any files deleted in the local dirstate
1272 # make sure any files deleted in the local dirstate
1271 # are not in the add or change column of the patch
1273 # are not in the add or change column of the patch
1272 forget = []
1274 forget = []
1273 for x in d + r:
1275 for x in d + r:
1274 if x in aa:
1276 if x in aa:
1275 del aa[aa.index(x)]
1277 del aa[aa.index(x)]
1276 forget.append(x)
1278 forget.append(x)
1277 continue
1279 continue
1278 elif x in mm:
1280 elif x in mm:
1279 del mm[mm.index(x)]
1281 del mm[mm.index(x)]
1280 dd.append(x)
1282 dd.append(x)
1281
1283
1282 m = list(set(mm))
1284 m = list(set(mm))
1283 r = list(set(dd))
1285 r = list(set(dd))
1284 a = list(set(aa))
1286 a = list(set(aa))
1285 c = [filter(matchfn, l) for l in (m, a, r)]
1287 c = [filter(matchfn, l) for l in (m, a, r)]
1286 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1288 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1287 chunks = patch.diff(repo, patchparent, match=match,
1289 chunks = patch.diff(repo, patchparent, match=match,
1288 changes=c, opts=diffopts)
1290 changes=c, opts=diffopts)
1289 for chunk in chunks:
1291 for chunk in chunks:
1290 patchf.write(chunk)
1292 patchf.write(chunk)
1291
1293
1292 try:
1294 try:
1293 if diffopts.git or diffopts.upgrade:
1295 if diffopts.git or diffopts.upgrade:
1294 copies = {}
1296 copies = {}
1295 for dst in a:
1297 for dst in a:
1296 src = repo.dirstate.copied(dst)
1298 src = repo.dirstate.copied(dst)
1297 # during qfold, the source file for copies may
1299 # during qfold, the source file for copies may
1298 # be removed. Treat this as a simple add.
1300 # be removed. Treat this as a simple add.
1299 if src is not None and src in repo.dirstate:
1301 if src is not None and src in repo.dirstate:
1300 copies.setdefault(src, []).append(dst)
1302 copies.setdefault(src, []).append(dst)
1301 repo.dirstate.add(dst)
1303 repo.dirstate.add(dst)
1302 # remember the copies between patchparent and tip
1304 # remember the copies between patchparent and tip
1303 for dst in aaa:
1305 for dst in aaa:
1304 f = repo.file(dst)
1306 f = repo.file(dst)
1305 src = f.renamed(man[dst])
1307 src = f.renamed(man[dst])
1306 if src:
1308 if src:
1307 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1309 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1308 if dst in a:
1310 if dst in a:
1309 copies[src[0]].append(dst)
1311 copies[src[0]].append(dst)
1310 # we can't copy a file created by the patch itself
1312 # we can't copy a file created by the patch itself
1311 if dst in copies:
1313 if dst in copies:
1312 del copies[dst]
1314 del copies[dst]
1313 for src, dsts in copies.iteritems():
1315 for src, dsts in copies.iteritems():
1314 for dst in dsts:
1316 for dst in dsts:
1315 repo.dirstate.copy(src, dst)
1317 repo.dirstate.copy(src, dst)
1316 else:
1318 else:
1317 for dst in a:
1319 for dst in a:
1318 repo.dirstate.add(dst)
1320 repo.dirstate.add(dst)
1319 # Drop useless copy information
1321 # Drop useless copy information
1320 for f in list(repo.dirstate.copies()):
1322 for f in list(repo.dirstate.copies()):
1321 repo.dirstate.copy(None, f)
1323 repo.dirstate.copy(None, f)
1322 for f in r:
1324 for f in r:
1323 repo.dirstate.remove(f)
1325 repo.dirstate.remove(f)
1324 # if the patch excludes a modified file, mark that
1326 # if the patch excludes a modified file, mark that
1325 # file with mtime=0 so status can see it.
1327 # file with mtime=0 so status can see it.
1326 mm = []
1328 mm = []
1327 for i in xrange(len(m)-1, -1, -1):
1329 for i in xrange(len(m)-1, -1, -1):
1328 if not matchfn(m[i]):
1330 if not matchfn(m[i]):
1329 mm.append(m[i])
1331 mm.append(m[i])
1330 del m[i]
1332 del m[i]
1331 for f in m:
1333 for f in m:
1332 repo.dirstate.normal(f)
1334 repo.dirstate.normal(f)
1333 for f in mm:
1335 for f in mm:
1334 repo.dirstate.normallookup(f)
1336 repo.dirstate.normallookup(f)
1335 for f in forget:
1337 for f in forget:
1336 repo.dirstate.forget(f)
1338 repo.dirstate.forget(f)
1337
1339
1338 if not msg:
1340 if not msg:
1339 if not ph.message:
1341 if not ph.message:
1340 message = "[mq]: %s\n" % patchfn
1342 message = "[mq]: %s\n" % patchfn
1341 else:
1343 else:
1342 message = "\n".join(ph.message)
1344 message = "\n".join(ph.message)
1343 else:
1345 else:
1344 message = msg
1346 message = msg
1345
1347
1346 user = ph.user or changes[1]
1348 user = ph.user or changes[1]
1347
1349
1348 # assumes strip can roll itself back if interrupted
1350 # assumes strip can roll itself back if interrupted
1349 repo.dirstate.setparents(*cparents)
1351 repo.dirstate.setparents(*cparents)
1350 self.applied.pop()
1352 self.applied.pop()
1351 self.applied_dirty = 1
1353 self.applied_dirty = 1
1352 self.strip(repo, top, update=False,
1354 self.strip(repo, top, update=False,
1353 backup='strip')
1355 backup='strip')
1354 except:
1356 except:
1355 repo.dirstate.invalidate()
1357 repo.dirstate.invalidate()
1356 raise
1358 raise
1357
1359
1358 try:
1360 try:
1359 # might be nice to attempt to roll back strip after this
1361 # might be nice to attempt to roll back strip after this
1360 patchf.rename()
1362 patchf.rename()
1361 n = repo.commit(message, user, ph.date, match=match,
1363 n = repo.commit(message, user, ph.date, match=match,
1362 force=True)
1364 force=True)
1363 self.applied.append(statusentry(hex(n), patchfn))
1365 self.applied.append(statusentry(hex(n), patchfn))
1364 except:
1366 except:
1365 ctx = repo[cparents[0]]
1367 ctx = repo[cparents[0]]
1366 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1368 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1367 self.save_dirty()
1369 self.save_dirty()
1368 self.ui.warn(_('refresh interrupted while patch was popped! '
1370 self.ui.warn(_('refresh interrupted while patch was popped! '
1369 '(revert --all, qpush to recover)\n'))
1371 '(revert --all, qpush to recover)\n'))
1370 raise
1372 raise
1371 else:
1373 else:
1372 self.printdiff(repo, diffopts, patchparent, fp=patchf)
1374 self.printdiff(repo, diffopts, patchparent, fp=patchf)
1373 patchf.rename()
1375 patchf.rename()
1374 added = repo.status()[1]
1376 added = repo.status()[1]
1375 for a in added:
1377 for a in added:
1376 f = repo.wjoin(a)
1378 f = repo.wjoin(a)
1377 try:
1379 try:
1378 os.unlink(f)
1380 os.unlink(f)
1379 except OSError, e:
1381 except OSError, e:
1380 if e.errno != errno.ENOENT:
1382 if e.errno != errno.ENOENT:
1381 raise
1383 raise
1382 try: os.removedirs(os.path.dirname(f))
1384 try: os.removedirs(os.path.dirname(f))
1383 except: pass
1385 except: pass
1384 # forget the file copies in the dirstate
1386 # forget the file copies in the dirstate
1385 # push should readd the files later on
1387 # push should readd the files later on
1386 repo.dirstate.forget(a)
1388 repo.dirstate.forget(a)
1387 self.pop(repo, force=True)
1389 self.pop(repo, force=True)
1388 self.push(repo, force=True)
1390 self.push(repo, force=True)
1389 finally:
1391 finally:
1390 wlock.release()
1392 wlock.release()
1391 self.removeundo(repo)
1393 self.removeundo(repo)
1392
1394
1393 def init(self, repo, create=False):
1395 def init(self, repo, create=False):
1394 if not create and os.path.isdir(self.path):
1396 if not create and os.path.isdir(self.path):
1395 raise util.Abort(_("patch queue directory already exists"))
1397 raise util.Abort(_("patch queue directory already exists"))
1396 try:
1398 try:
1397 os.mkdir(self.path)
1399 os.mkdir(self.path)
1398 except OSError, inst:
1400 except OSError, inst:
1399 if inst.errno != errno.EEXIST or not create:
1401 if inst.errno != errno.EEXIST or not create:
1400 raise
1402 raise
1401 if create:
1403 if create:
1402 return self.qrepo(create=True)
1404 return self.qrepo(create=True)
1403
1405
1404 def unapplied(self, repo, patch=None):
1406 def unapplied(self, repo, patch=None):
1405 if patch and patch not in self.series:
1407 if patch and patch not in self.series:
1406 raise util.Abort(_("patch %s is not in series file") % patch)
1408 raise util.Abort(_("patch %s is not in series file") % patch)
1407 if not patch:
1409 if not patch:
1408 start = self.series_end()
1410 start = self.series_end()
1409 else:
1411 else:
1410 start = self.series.index(patch) + 1
1412 start = self.series.index(patch) + 1
1411 unapplied = []
1413 unapplied = []
1412 for i in xrange(start, len(self.series)):
1414 for i in xrange(start, len(self.series)):
1413 pushable, reason = self.pushable(i)
1415 pushable, reason = self.pushable(i)
1414 if pushable:
1416 if pushable:
1415 unapplied.append((i, self.series[i]))
1417 unapplied.append((i, self.series[i]))
1416 self.explain_pushable(i)
1418 self.explain_pushable(i)
1417 return unapplied
1419 return unapplied
1418
1420
1419 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1421 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1420 summary=False):
1422 summary=False):
1421 def displayname(pfx, patchname):
1423 def displayname(pfx, patchname):
1422 if summary:
1424 if summary:
1423 ph = patchheader(self.join(patchname))
1425 ph = patchheader(self.join(patchname))
1424 msg = ph.message and ph.message[0] or ''
1426 msg = ph.message and ph.message[0] or ''
1425 if self.ui.interactive():
1427 if self.ui.interactive():
1426 width = util.termwidth() - len(pfx) - len(patchname) - 2
1428 width = util.termwidth() - len(pfx) - len(patchname) - 2
1427 if width > 0:
1429 if width > 0:
1428 msg = util.ellipsis(msg, width)
1430 msg = util.ellipsis(msg, width)
1429 else:
1431 else:
1430 msg = ''
1432 msg = ''
1431 msg = "%s%s: %s" % (pfx, patchname, msg)
1433 msg = "%s%s: %s" % (pfx, patchname, msg)
1432 else:
1434 else:
1433 msg = pfx + patchname
1435 msg = pfx + patchname
1434 self.ui.write(msg + '\n')
1436 self.ui.write(msg + '\n')
1435
1437
1436 applied = set([p.name for p in self.applied])
1438 applied = set([p.name for p in self.applied])
1437 if length is None:
1439 if length is None:
1438 length = len(self.series) - start
1440 length = len(self.series) - start
1439 if not missing:
1441 if not missing:
1440 if self.ui.verbose:
1442 if self.ui.verbose:
1441 idxwidth = len(str(start+length - 1))
1443 idxwidth = len(str(start+length - 1))
1442 for i in xrange(start, start+length):
1444 for i in xrange(start, start+length):
1443 patch = self.series[i]
1445 patch = self.series[i]
1444 if patch in applied:
1446 if patch in applied:
1445 stat = 'A'
1447 stat = 'A'
1446 elif self.pushable(i)[0]:
1448 elif self.pushable(i)[0]:
1447 stat = 'U'
1449 stat = 'U'
1448 else:
1450 else:
1449 stat = 'G'
1451 stat = 'G'
1450 pfx = ''
1452 pfx = ''
1451 if self.ui.verbose:
1453 if self.ui.verbose:
1452 pfx = '%*d %s ' % (idxwidth, i, stat)
1454 pfx = '%*d %s ' % (idxwidth, i, stat)
1453 elif status and status != stat:
1455 elif status and status != stat:
1454 continue
1456 continue
1455 displayname(pfx, patch)
1457 displayname(pfx, patch)
1456 else:
1458 else:
1457 msng_list = []
1459 msng_list = []
1458 for root, dirs, files in os.walk(self.path):
1460 for root, dirs, files in os.walk(self.path):
1459 d = root[len(self.path) + 1:]
1461 d = root[len(self.path) + 1:]
1460 for f in files:
1462 for f in files:
1461 fl = os.path.join(d, f)
1463 fl = os.path.join(d, f)
1462 if (fl not in self.series and
1464 if (fl not in self.series and
1463 fl not in (self.status_path, self.series_path,
1465 fl not in (self.status_path, self.series_path,
1464 self.guards_path)
1466 self.guards_path)
1465 and not fl.startswith('.')):
1467 and not fl.startswith('.')):
1466 msng_list.append(fl)
1468 msng_list.append(fl)
1467 for x in sorted(msng_list):
1469 for x in sorted(msng_list):
1468 pfx = self.ui.verbose and ('D ') or ''
1470 pfx = self.ui.verbose and ('D ') or ''
1469 displayname(pfx, x)
1471 displayname(pfx, x)
1470
1472
1471 def issaveline(self, l):
1473 def issaveline(self, l):
1472 if l.name == '.hg.patches.save.line':
1474 if l.name == '.hg.patches.save.line':
1473 return True
1475 return True
1474
1476
1475 def qrepo(self, create=False):
1477 def qrepo(self, create=False):
1476 if create or os.path.isdir(self.join(".hg")):
1478 if create or os.path.isdir(self.join(".hg")):
1477 return hg.repository(self.ui, path=self.path, create=create)
1479 return hg.repository(self.ui, path=self.path, create=create)
1478
1480
1479 def restore(self, repo, rev, delete=None, qupdate=None):
1481 def restore(self, repo, rev, delete=None, qupdate=None):
1480 c = repo.changelog.read(rev)
1482 c = repo.changelog.read(rev)
1481 desc = c[4].strip()
1483 desc = c[4].strip()
1482 lines = desc.splitlines()
1484 lines = desc.splitlines()
1483 i = 0
1485 i = 0
1484 datastart = None
1486 datastart = None
1485 series = []
1487 series = []
1486 applied = []
1488 applied = []
1487 qpp = None
1489 qpp = None
1488 for i, line in enumerate(lines):
1490 for i, line in enumerate(lines):
1489 if line == 'Patch Data:':
1491 if line == 'Patch Data:':
1490 datastart = i + 1
1492 datastart = i + 1
1491 elif line.startswith('Dirstate:'):
1493 elif line.startswith('Dirstate:'):
1492 l = line.rstrip()
1494 l = line.rstrip()
1493 l = l[10:].split(' ')
1495 l = l[10:].split(' ')
1494 qpp = [ bin(x) for x in l ]
1496 qpp = [ bin(x) for x in l ]
1495 elif datastart != None:
1497 elif datastart != None:
1496 l = line.rstrip()
1498 l = line.rstrip()
1497 se = statusentry(l)
1499 se = statusentry(l)
1498 file_ = se.name
1500 file_ = se.name
1499 if se.rev:
1501 if se.rev:
1500 applied.append(se)
1502 applied.append(se)
1501 else:
1503 else:
1502 series.append(file_)
1504 series.append(file_)
1503 if datastart is None:
1505 if datastart is None:
1504 self.ui.warn(_("No saved patch data found\n"))
1506 self.ui.warn(_("No saved patch data found\n"))
1505 return 1
1507 return 1
1506 self.ui.warn(_("restoring status: %s\n") % lines[0])
1508 self.ui.warn(_("restoring status: %s\n") % lines[0])
1507 self.full_series = series
1509 self.full_series = series
1508 self.applied = applied
1510 self.applied = applied
1509 self.parse_series()
1511 self.parse_series()
1510 self.series_dirty = 1
1512 self.series_dirty = 1
1511 self.applied_dirty = 1
1513 self.applied_dirty = 1
1512 heads = repo.changelog.heads()
1514 heads = repo.changelog.heads()
1513 if delete:
1515 if delete:
1514 if rev not in heads:
1516 if rev not in heads:
1515 self.ui.warn(_("save entry has children, leaving it alone\n"))
1517 self.ui.warn(_("save entry has children, leaving it alone\n"))
1516 else:
1518 else:
1517 self.ui.warn(_("removing save entry %s\n") % short(rev))
1519 self.ui.warn(_("removing save entry %s\n") % short(rev))
1518 pp = repo.dirstate.parents()
1520 pp = repo.dirstate.parents()
1519 if rev in pp:
1521 if rev in pp:
1520 update = True
1522 update = True
1521 else:
1523 else:
1522 update = False
1524 update = False
1523 self.strip(repo, rev, update=update, backup='strip')
1525 self.strip(repo, rev, update=update, backup='strip')
1524 if qpp:
1526 if qpp:
1525 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1527 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1526 (short(qpp[0]), short(qpp[1])))
1528 (short(qpp[0]), short(qpp[1])))
1527 if qupdate:
1529 if qupdate:
1528 self.ui.status(_("queue directory updating\n"))
1530 self.ui.status(_("queue directory updating\n"))
1529 r = self.qrepo()
1531 r = self.qrepo()
1530 if not r:
1532 if not r:
1531 self.ui.warn(_("Unable to load queue repository\n"))
1533 self.ui.warn(_("Unable to load queue repository\n"))
1532 return 1
1534 return 1
1533 hg.clean(r, qpp[0])
1535 hg.clean(r, qpp[0])
1534
1536
1535 def save(self, repo, msg=None):
1537 def save(self, repo, msg=None):
1536 if len(self.applied) == 0:
1538 if len(self.applied) == 0:
1537 self.ui.warn(_("save: no patches applied, exiting\n"))
1539 self.ui.warn(_("save: no patches applied, exiting\n"))
1538 return 1
1540 return 1
1539 if self.issaveline(self.applied[-1]):
1541 if self.issaveline(self.applied[-1]):
1540 self.ui.warn(_("status is already saved\n"))
1542 self.ui.warn(_("status is already saved\n"))
1541 return 1
1543 return 1
1542
1544
1543 ar = [ ':' + x for x in self.full_series ]
1545 ar = [ ':' + x for x in self.full_series ]
1544 if not msg:
1546 if not msg:
1545 msg = _("hg patches saved state")
1547 msg = _("hg patches saved state")
1546 else:
1548 else:
1547 msg = "hg patches: " + msg.rstrip('\r\n')
1549 msg = "hg patches: " + msg.rstrip('\r\n')
1548 r = self.qrepo()
1550 r = self.qrepo()
1549 if r:
1551 if r:
1550 pp = r.dirstate.parents()
1552 pp = r.dirstate.parents()
1551 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1553 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1552 msg += "\n\nPatch Data:\n"
1554 msg += "\n\nPatch Data:\n"
1553 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1555 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1554 "\n".join(ar) + '\n' or "")
1556 "\n".join(ar) + '\n' or "")
1555 n = repo.commit(text, force=True)
1557 n = repo.commit(text, force=True)
1556 if not n:
1558 if not n:
1557 self.ui.warn(_("repo commit failed\n"))
1559 self.ui.warn(_("repo commit failed\n"))
1558 return 1
1560 return 1
1559 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1561 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1560 self.applied_dirty = 1
1562 self.applied_dirty = 1
1561 self.removeundo(repo)
1563 self.removeundo(repo)
1562
1564
1563 def full_series_end(self):
1565 def full_series_end(self):
1564 if len(self.applied) > 0:
1566 if len(self.applied) > 0:
1565 p = self.applied[-1].name
1567 p = self.applied[-1].name
1566 end = self.find_series(p)
1568 end = self.find_series(p)
1567 if end is None:
1569 if end is None:
1568 return len(self.full_series)
1570 return len(self.full_series)
1569 return end + 1
1571 return end + 1
1570 return 0
1572 return 0
1571
1573
1572 def series_end(self, all_patches=False):
1574 def series_end(self, all_patches=False):
1573 """If all_patches is False, return the index of the next pushable patch
1575 """If all_patches is False, return the index of the next pushable patch
1574 in the series, or the series length. If all_patches is True, return the
1576 in the series, or the series length. If all_patches is True, return the
1575 index of the first patch past the last applied one.
1577 index of the first patch past the last applied one.
1576 """
1578 """
1577 end = 0
1579 end = 0
1578 def next(start):
1580 def next(start):
1579 if all_patches:
1581 if all_patches:
1580 return start
1582 return start
1581 i = start
1583 i = start
1582 while i < len(self.series):
1584 while i < len(self.series):
1583 p, reason = self.pushable(i)
1585 p, reason = self.pushable(i)
1584 if p:
1586 if p:
1585 break
1587 break
1586 self.explain_pushable(i)
1588 self.explain_pushable(i)
1587 i += 1
1589 i += 1
1588 return i
1590 return i
1589 if len(self.applied) > 0:
1591 if len(self.applied) > 0:
1590 p = self.applied[-1].name
1592 p = self.applied[-1].name
1591 try:
1593 try:
1592 end = self.series.index(p)
1594 end = self.series.index(p)
1593 except ValueError:
1595 except ValueError:
1594 return 0
1596 return 0
1595 return next(end + 1)
1597 return next(end + 1)
1596 return next(end)
1598 return next(end)
1597
1599
1598 def appliedname(self, index):
1600 def appliedname(self, index):
1599 pname = self.applied[index].name
1601 pname = self.applied[index].name
1600 if not self.ui.verbose:
1602 if not self.ui.verbose:
1601 p = pname
1603 p = pname
1602 else:
1604 else:
1603 p = str(self.series.index(pname)) + " " + pname
1605 p = str(self.series.index(pname)) + " " + pname
1604 return p
1606 return p
1605
1607
1606 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1608 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1607 force=None, git=False):
1609 force=None, git=False):
1608 def checkseries(patchname):
1610 def checkseries(patchname):
1609 if patchname in self.series:
1611 if patchname in self.series:
1610 raise util.Abort(_('patch %s is already in the series file')
1612 raise util.Abort(_('patch %s is already in the series file')
1611 % patchname)
1613 % patchname)
1612 def checkfile(patchname):
1614 def checkfile(patchname):
1613 if not force and os.path.exists(self.join(patchname)):
1615 if not force and os.path.exists(self.join(patchname)):
1614 raise util.Abort(_('patch "%s" already exists')
1616 raise util.Abort(_('patch "%s" already exists')
1615 % patchname)
1617 % patchname)
1616
1618
1617 if rev:
1619 if rev:
1618 if files:
1620 if files:
1619 raise util.Abort(_('option "-r" not valid when importing '
1621 raise util.Abort(_('option "-r" not valid when importing '
1620 'files'))
1622 'files'))
1621 rev = cmdutil.revrange(repo, rev)
1623 rev = cmdutil.revrange(repo, rev)
1622 rev.sort(reverse=True)
1624 rev.sort(reverse=True)
1623 if (len(files) > 1 or len(rev) > 1) and patchname:
1625 if (len(files) > 1 or len(rev) > 1) and patchname:
1624 raise util.Abort(_('option "-n" not valid when importing multiple '
1626 raise util.Abort(_('option "-n" not valid when importing multiple '
1625 'patches'))
1627 'patches'))
1626 i = 0
1628 i = 0
1627 added = []
1629 added = []
1628 if rev:
1630 if rev:
1629 # If mq patches are applied, we can only import revisions
1631 # If mq patches are applied, we can only import revisions
1630 # that form a linear path to qbase.
1632 # that form a linear path to qbase.
1631 # Otherwise, they should form a linear path to a head.
1633 # Otherwise, they should form a linear path to a head.
1632 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1634 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1633 if len(heads) > 1:
1635 if len(heads) > 1:
1634 raise util.Abort(_('revision %d is the root of more than one '
1636 raise util.Abort(_('revision %d is the root of more than one '
1635 'branch') % rev[-1])
1637 'branch') % rev[-1])
1636 if self.applied:
1638 if self.applied:
1637 base = hex(repo.changelog.node(rev[0]))
1639 base = hex(repo.changelog.node(rev[0]))
1638 if base in [n.rev for n in self.applied]:
1640 if base in [n.rev for n in self.applied]:
1639 raise util.Abort(_('revision %d is already managed')
1641 raise util.Abort(_('revision %d is already managed')
1640 % rev[0])
1642 % rev[0])
1641 if heads != [bin(self.applied[-1].rev)]:
1643 if heads != [bin(self.applied[-1].rev)]:
1642 raise util.Abort(_('revision %d is not the parent of '
1644 raise util.Abort(_('revision %d is not the parent of '
1643 'the queue') % rev[0])
1645 'the queue') % rev[0])
1644 base = repo.changelog.rev(bin(self.applied[0].rev))
1646 base = repo.changelog.rev(bin(self.applied[0].rev))
1645 lastparent = repo.changelog.parentrevs(base)[0]
1647 lastparent = repo.changelog.parentrevs(base)[0]
1646 else:
1648 else:
1647 if heads != [repo.changelog.node(rev[0])]:
1649 if heads != [repo.changelog.node(rev[0])]:
1648 raise util.Abort(_('revision %d has unmanaged children')
1650 raise util.Abort(_('revision %d has unmanaged children')
1649 % rev[0])
1651 % rev[0])
1650 lastparent = None
1652 lastparent = None
1651
1653
1652 diffopts = self.diffopts({'git': git})
1654 diffopts = self.diffopts({'git': git})
1653 for r in rev:
1655 for r in rev:
1654 p1, p2 = repo.changelog.parentrevs(r)
1656 p1, p2 = repo.changelog.parentrevs(r)
1655 n = repo.changelog.node(r)
1657 n = repo.changelog.node(r)
1656 if p2 != nullrev:
1658 if p2 != nullrev:
1657 raise util.Abort(_('cannot import merge revision %d') % r)
1659 raise util.Abort(_('cannot import merge revision %d') % r)
1658 if lastparent and lastparent != r:
1660 if lastparent and lastparent != r:
1659 raise util.Abort(_('revision %d is not the parent of %d')
1661 raise util.Abort(_('revision %d is not the parent of %d')
1660 % (r, lastparent))
1662 % (r, lastparent))
1661 lastparent = p1
1663 lastparent = p1
1662
1664
1663 if not patchname:
1665 if not patchname:
1664 patchname = normname('%d.diff' % r)
1666 patchname = normname('%d.diff' % r)
1665 self.check_reserved_name(patchname)
1667 self.check_reserved_name(patchname)
1666 checkseries(patchname)
1668 checkseries(patchname)
1667 checkfile(patchname)
1669 checkfile(patchname)
1668 self.full_series.insert(0, patchname)
1670 self.full_series.insert(0, patchname)
1669
1671
1670 patchf = self.opener(patchname, "w")
1672 patchf = self.opener(patchname, "w")
1671 patch.export(repo, [n], fp=patchf, opts=diffopts)
1673 patch.export(repo, [n], fp=patchf, opts=diffopts)
1672 patchf.close()
1674 patchf.close()
1673
1675
1674 se = statusentry(hex(n), patchname)
1676 se = statusentry(hex(n), patchname)
1675 self.applied.insert(0, se)
1677 self.applied.insert(0, se)
1676
1678
1677 added.append(patchname)
1679 added.append(patchname)
1678 patchname = None
1680 patchname = None
1679 self.parse_series()
1681 self.parse_series()
1680 self.applied_dirty = 1
1682 self.applied_dirty = 1
1681
1683
1682 for filename in files:
1684 for filename in files:
1683 if existing:
1685 if existing:
1684 if filename == '-':
1686 if filename == '-':
1685 raise util.Abort(_('-e is incompatible with import from -'))
1687 raise util.Abort(_('-e is incompatible with import from -'))
1686 if not patchname:
1688 if not patchname:
1687 patchname = normname(filename)
1689 patchname = normname(filename)
1688 self.check_reserved_name(patchname)
1690 self.check_reserved_name(patchname)
1689 if not os.path.isfile(self.join(patchname)):
1691 if not os.path.isfile(self.join(patchname)):
1690 raise util.Abort(_("patch %s does not exist") % patchname)
1692 raise util.Abort(_("patch %s does not exist") % patchname)
1691 else:
1693 else:
1692 try:
1694 try:
1693 if filename == '-':
1695 if filename == '-':
1694 if not patchname:
1696 if not patchname:
1695 raise util.Abort(_('need --name to import a patch from -'))
1697 raise util.Abort(_('need --name to import a patch from -'))
1696 text = sys.stdin.read()
1698 text = sys.stdin.read()
1697 else:
1699 else:
1698 text = url.open(self.ui, filename).read()
1700 text = url.open(self.ui, filename).read()
1699 except (OSError, IOError):
1701 except (OSError, IOError):
1700 raise util.Abort(_("unable to read %s") % filename)
1702 raise util.Abort(_("unable to read %s") % filename)
1701 if not patchname:
1703 if not patchname:
1702 patchname = normname(os.path.basename(filename))
1704 patchname = normname(os.path.basename(filename))
1703 self.check_reserved_name(patchname)
1705 self.check_reserved_name(patchname)
1704 checkfile(patchname)
1706 checkfile(patchname)
1705 patchf = self.opener(patchname, "w")
1707 patchf = self.opener(patchname, "w")
1706 patchf.write(text)
1708 patchf.write(text)
1707 if not force:
1709 if not force:
1708 checkseries(patchname)
1710 checkseries(patchname)
1709 if patchname not in self.series:
1711 if patchname not in self.series:
1710 index = self.full_series_end() + i
1712 index = self.full_series_end() + i
1711 self.full_series[index:index] = [patchname]
1713 self.full_series[index:index] = [patchname]
1712 self.parse_series()
1714 self.parse_series()
1713 self.ui.warn(_("adding %s to series file\n") % patchname)
1715 self.ui.warn(_("adding %s to series file\n") % patchname)
1714 i += 1
1716 i += 1
1715 added.append(patchname)
1717 added.append(patchname)
1716 patchname = None
1718 patchname = None
1717 self.series_dirty = 1
1719 self.series_dirty = 1
1718 qrepo = self.qrepo()
1720 qrepo = self.qrepo()
1719 if qrepo:
1721 if qrepo:
1720 qrepo.add(added)
1722 qrepo.add(added)
1721
1723
1722 def delete(ui, repo, *patches, **opts):
1724 def delete(ui, repo, *patches, **opts):
1723 """remove patches from queue
1725 """remove patches from queue
1724
1726
1725 The patches must not be applied, and at least one patch is required. With
1727 The patches must not be applied, and at least one patch is required. With
1726 -k/--keep, the patch files are preserved in the patch directory.
1728 -k/--keep, the patch files are preserved in the patch directory.
1727
1729
1728 To stop managing a patch and move it into permanent history,
1730 To stop managing a patch and move it into permanent history,
1729 use the qfinish command."""
1731 use the qfinish command."""
1730 q = repo.mq
1732 q = repo.mq
1731 q.delete(repo, patches, opts)
1733 q.delete(repo, patches, opts)
1732 q.save_dirty()
1734 q.save_dirty()
1733 return 0
1735 return 0
1734
1736
1735 def applied(ui, repo, patch=None, **opts):
1737 def applied(ui, repo, patch=None, **opts):
1736 """print the patches already applied"""
1738 """print the patches already applied"""
1737
1739
1738 q = repo.mq
1740 q = repo.mq
1739 l = len(q.applied)
1741 l = len(q.applied)
1740
1742
1741 if patch:
1743 if patch:
1742 if patch not in q.series:
1744 if patch not in q.series:
1743 raise util.Abort(_("patch %s is not in series file") % patch)
1745 raise util.Abort(_("patch %s is not in series file") % patch)
1744 end = q.series.index(patch) + 1
1746 end = q.series.index(patch) + 1
1745 else:
1747 else:
1746 end = q.series_end(True)
1748 end = q.series_end(True)
1747
1749
1748 if opts.get('last') and not end:
1750 if opts.get('last') and not end:
1749 ui.write(_("no patches applied\n"))
1751 ui.write(_("no patches applied\n"))
1750 return 1
1752 return 1
1751 elif opts.get('last') and end == 1:
1753 elif opts.get('last') and end == 1:
1752 ui.write(_("only one patch applied\n"))
1754 ui.write(_("only one patch applied\n"))
1753 return 1
1755 return 1
1754 elif opts.get('last'):
1756 elif opts.get('last'):
1755 start = end - 2
1757 start = end - 2
1756 end = 1
1758 end = 1
1757 else:
1759 else:
1758 start = 0
1760 start = 0
1759
1761
1760 return q.qseries(repo, length=end, start=start, status='A',
1762 return q.qseries(repo, length=end, start=start, status='A',
1761 summary=opts.get('summary'))
1763 summary=opts.get('summary'))
1762
1764
1763 def unapplied(ui, repo, patch=None, **opts):
1765 def unapplied(ui, repo, patch=None, **opts):
1764 """print the patches not yet applied"""
1766 """print the patches not yet applied"""
1765
1767
1766 q = repo.mq
1768 q = repo.mq
1767 if patch:
1769 if patch:
1768 if patch not in q.series:
1770 if patch not in q.series:
1769 raise util.Abort(_("patch %s is not in series file") % patch)
1771 raise util.Abort(_("patch %s is not in series file") % patch)
1770 start = q.series.index(patch) + 1
1772 start = q.series.index(patch) + 1
1771 else:
1773 else:
1772 start = q.series_end(True)
1774 start = q.series_end(True)
1773
1775
1774 if start == len(q.series) and opts.get('first'):
1776 if start == len(q.series) and opts.get('first'):
1775 ui.write(_("all patches applied\n"))
1777 ui.write(_("all patches applied\n"))
1776 return 1
1778 return 1
1777
1779
1778 length = opts.get('first') and 1 or None
1780 length = opts.get('first') and 1 or None
1779 return q.qseries(repo, start=start, length=length, status='U',
1781 return q.qseries(repo, start=start, length=length, status='U',
1780 summary=opts.get('summary'))
1782 summary=opts.get('summary'))
1781
1783
1782 def qimport(ui, repo, *filename, **opts):
1784 def qimport(ui, repo, *filename, **opts):
1783 """import a patch
1785 """import a patch
1784
1786
1785 The patch is inserted into the series after the last applied
1787 The patch is inserted into the series after the last applied
1786 patch. If no patches have been applied, qimport prepends the patch
1788 patch. If no patches have been applied, qimport prepends the patch
1787 to the series.
1789 to the series.
1788
1790
1789 The patch will have the same name as its source file unless you
1791 The patch will have the same name as its source file unless you
1790 give it a new one with -n/--name.
1792 give it a new one with -n/--name.
1791
1793
1792 You can register an existing patch inside the patch directory with
1794 You can register an existing patch inside the patch directory with
1793 the -e/--existing flag.
1795 the -e/--existing flag.
1794
1796
1795 With -f/--force, an existing patch of the same name will be
1797 With -f/--force, an existing patch of the same name will be
1796 overwritten.
1798 overwritten.
1797
1799
1798 An existing changeset may be placed under mq control with -r/--rev
1800 An existing changeset may be placed under mq control with -r/--rev
1799 (e.g. qimport --rev tip -n patch will place tip under mq control).
1801 (e.g. qimport --rev tip -n patch will place tip under mq control).
1800 With -g/--git, patches imported with --rev will use the git diff
1802 With -g/--git, patches imported with --rev will use the git diff
1801 format. See the diffs help topic for information on why this is
1803 format. See the diffs help topic for information on why this is
1802 important for preserving rename/copy information and permission
1804 important for preserving rename/copy information and permission
1803 changes.
1805 changes.
1804
1806
1805 To import a patch from standard input, pass - as the patch file.
1807 To import a patch from standard input, pass - as the patch file.
1806 When importing from standard input, a patch name must be specified
1808 When importing from standard input, a patch name must be specified
1807 using the --name flag.
1809 using the --name flag.
1808 """
1810 """
1809 q = repo.mq
1811 q = repo.mq
1810 q.qimport(repo, filename, patchname=opts['name'],
1812 q.qimport(repo, filename, patchname=opts['name'],
1811 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1813 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1812 git=opts['git'])
1814 git=opts['git'])
1813 q.save_dirty()
1815 q.save_dirty()
1814
1816
1815 if opts.get('push') and not opts.get('rev'):
1817 if opts.get('push') and not opts.get('rev'):
1816 return q.push(repo, None)
1818 return q.push(repo, None)
1817 return 0
1819 return 0
1818
1820
1819 def init(ui, repo, **opts):
1821 def init(ui, repo, **opts):
1820 """init a new queue repository
1822 """init a new queue repository
1821
1823
1822 The queue repository is unversioned by default. If
1824 The queue repository is unversioned by default. If
1823 -c/--create-repo is specified, qinit will create a separate nested
1825 -c/--create-repo is specified, qinit will create a separate nested
1824 repository for patches (qinit -c may also be run later to convert
1826 repository for patches (qinit -c may also be run later to convert
1825 an unversioned patch repository into a versioned one). You can use
1827 an unversioned patch repository into a versioned one). You can use
1826 qcommit to commit changes to this queue repository."""
1828 qcommit to commit changes to this queue repository."""
1827 q = repo.mq
1829 q = repo.mq
1828 r = q.init(repo, create=opts['create_repo'])
1830 r = q.init(repo, create=opts['create_repo'])
1829 q.save_dirty()
1831 q.save_dirty()
1830 if r:
1832 if r:
1831 if not os.path.exists(r.wjoin('.hgignore')):
1833 if not os.path.exists(r.wjoin('.hgignore')):
1832 fp = r.wopener('.hgignore', 'w')
1834 fp = r.wopener('.hgignore', 'w')
1833 fp.write('^\\.hg\n')
1835 fp.write('^\\.hg\n')
1834 fp.write('^\\.mq\n')
1836 fp.write('^\\.mq\n')
1835 fp.write('syntax: glob\n')
1837 fp.write('syntax: glob\n')
1836 fp.write('status\n')
1838 fp.write('status\n')
1837 fp.write('guards\n')
1839 fp.write('guards\n')
1838 fp.close()
1840 fp.close()
1839 if not os.path.exists(r.wjoin('series')):
1841 if not os.path.exists(r.wjoin('series')):
1840 r.wopener('series', 'w').close()
1842 r.wopener('series', 'w').close()
1841 r.add(['.hgignore', 'series'])
1843 r.add(['.hgignore', 'series'])
1842 commands.add(ui, r)
1844 commands.add(ui, r)
1843 return 0
1845 return 0
1844
1846
1845 def clone(ui, source, dest=None, **opts):
1847 def clone(ui, source, dest=None, **opts):
1846 '''clone main and patch repository at same time
1848 '''clone main and patch repository at same time
1847
1849
1848 If source is local, destination will have no patches applied. If
1850 If source is local, destination will have no patches applied. If
1849 source is remote, this command can not check if patches are
1851 source is remote, this command can not check if patches are
1850 applied in source, so cannot guarantee that patches are not
1852 applied in source, so cannot guarantee that patches are not
1851 applied in destination. If you clone remote repository, be sure
1853 applied in destination. If you clone remote repository, be sure
1852 before that it has no patches applied.
1854 before that it has no patches applied.
1853
1855
1854 Source patch repository is looked for in <src>/.hg/patches by
1856 Source patch repository is looked for in <src>/.hg/patches by
1855 default. Use -p <url> to change.
1857 default. Use -p <url> to change.
1856
1858
1857 The patch directory must be a nested Mercurial repository, as
1859 The patch directory must be a nested Mercurial repository, as
1858 would be created by qinit -c.
1860 would be created by qinit -c.
1859 '''
1861 '''
1860 def patchdir(repo):
1862 def patchdir(repo):
1861 url = repo.url()
1863 url = repo.url()
1862 if url.endswith('/'):
1864 if url.endswith('/'):
1863 url = url[:-1]
1865 url = url[:-1]
1864 return url + '/.hg/patches'
1866 return url + '/.hg/patches'
1865 if dest is None:
1867 if dest is None:
1866 dest = hg.defaultdest(source)
1868 dest = hg.defaultdest(source)
1867 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1869 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1868 if opts['patches']:
1870 if opts['patches']:
1869 patchespath = ui.expandpath(opts['patches'])
1871 patchespath = ui.expandpath(opts['patches'])
1870 else:
1872 else:
1871 patchespath = patchdir(sr)
1873 patchespath = patchdir(sr)
1872 try:
1874 try:
1873 hg.repository(ui, patchespath)
1875 hg.repository(ui, patchespath)
1874 except error.RepoError:
1876 except error.RepoError:
1875 raise util.Abort(_('versioned patch repository not found'
1877 raise util.Abort(_('versioned patch repository not found'
1876 ' (see qinit -c)'))
1878 ' (see qinit -c)'))
1877 qbase, destrev = None, None
1879 qbase, destrev = None, None
1878 if sr.local():
1880 if sr.local():
1879 if sr.mq.applied:
1881 if sr.mq.applied:
1880 qbase = bin(sr.mq.applied[0].rev)
1882 qbase = bin(sr.mq.applied[0].rev)
1881 if not hg.islocal(dest):
1883 if not hg.islocal(dest):
1882 heads = set(sr.heads())
1884 heads = set(sr.heads())
1883 destrev = list(heads.difference(sr.heads(qbase)))
1885 destrev = list(heads.difference(sr.heads(qbase)))
1884 destrev.append(sr.changelog.parents(qbase)[0])
1886 destrev.append(sr.changelog.parents(qbase)[0])
1885 elif sr.capable('lookup'):
1887 elif sr.capable('lookup'):
1886 try:
1888 try:
1887 qbase = sr.lookup('qbase')
1889 qbase = sr.lookup('qbase')
1888 except error.RepoError:
1890 except error.RepoError:
1889 pass
1891 pass
1890 ui.note(_('cloning main repository\n'))
1892 ui.note(_('cloning main repository\n'))
1891 sr, dr = hg.clone(ui, sr.url(), dest,
1893 sr, dr = hg.clone(ui, sr.url(), dest,
1892 pull=opts['pull'],
1894 pull=opts['pull'],
1893 rev=destrev,
1895 rev=destrev,
1894 update=False,
1896 update=False,
1895 stream=opts['uncompressed'])
1897 stream=opts['uncompressed'])
1896 ui.note(_('cloning patch repository\n'))
1898 ui.note(_('cloning patch repository\n'))
1897 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1899 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1898 pull=opts['pull'], update=not opts['noupdate'],
1900 pull=opts['pull'], update=not opts['noupdate'],
1899 stream=opts['uncompressed'])
1901 stream=opts['uncompressed'])
1900 if dr.local():
1902 if dr.local():
1901 if qbase:
1903 if qbase:
1902 ui.note(_('stripping applied patches from destination '
1904 ui.note(_('stripping applied patches from destination '
1903 'repository\n'))
1905 'repository\n'))
1904 dr.mq.strip(dr, qbase, update=False, backup=None)
1906 dr.mq.strip(dr, qbase, update=False, backup=None)
1905 if not opts['noupdate']:
1907 if not opts['noupdate']:
1906 ui.note(_('updating destination repository\n'))
1908 ui.note(_('updating destination repository\n'))
1907 hg.update(dr, dr.changelog.tip())
1909 hg.update(dr, dr.changelog.tip())
1908
1910
1909 def commit(ui, repo, *pats, **opts):
1911 def commit(ui, repo, *pats, **opts):
1910 """commit changes in the queue repository"""
1912 """commit changes in the queue repository"""
1911 q = repo.mq
1913 q = repo.mq
1912 r = q.qrepo()
1914 r = q.qrepo()
1913 if not r: raise util.Abort('no queue repository')
1915 if not r: raise util.Abort('no queue repository')
1914 commands.commit(r.ui, r, *pats, **opts)
1916 commands.commit(r.ui, r, *pats, **opts)
1915
1917
1916 def series(ui, repo, **opts):
1918 def series(ui, repo, **opts):
1917 """print the entire series file"""
1919 """print the entire series file"""
1918 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1920 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1919 return 0
1921 return 0
1920
1922
1921 def top(ui, repo, **opts):
1923 def top(ui, repo, **opts):
1922 """print the name of the current patch"""
1924 """print the name of the current patch"""
1923 q = repo.mq
1925 q = repo.mq
1924 t = q.applied and q.series_end(True) or 0
1926 t = q.applied and q.series_end(True) or 0
1925 if t:
1927 if t:
1926 return q.qseries(repo, start=t-1, length=1, status='A',
1928 return q.qseries(repo, start=t-1, length=1, status='A',
1927 summary=opts.get('summary'))
1929 summary=opts.get('summary'))
1928 else:
1930 else:
1929 ui.write(_("no patches applied\n"))
1931 ui.write(_("no patches applied\n"))
1930 return 1
1932 return 1
1931
1933
1932 def next(ui, repo, **opts):
1934 def next(ui, repo, **opts):
1933 """print the name of the next patch"""
1935 """print the name of the next patch"""
1934 q = repo.mq
1936 q = repo.mq
1935 end = q.series_end()
1937 end = q.series_end()
1936 if end == len(q.series):
1938 if end == len(q.series):
1937 ui.write(_("all patches applied\n"))
1939 ui.write(_("all patches applied\n"))
1938 return 1
1940 return 1
1939 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1941 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1940
1942
1941 def prev(ui, repo, **opts):
1943 def prev(ui, repo, **opts):
1942 """print the name of the previous patch"""
1944 """print the name of the previous patch"""
1943 q = repo.mq
1945 q = repo.mq
1944 l = len(q.applied)
1946 l = len(q.applied)
1945 if l == 1:
1947 if l == 1:
1946 ui.write(_("only one patch applied\n"))
1948 ui.write(_("only one patch applied\n"))
1947 return 1
1949 return 1
1948 if not l:
1950 if not l:
1949 ui.write(_("no patches applied\n"))
1951 ui.write(_("no patches applied\n"))
1950 return 1
1952 return 1
1951 return q.qseries(repo, start=l-2, length=1, status='A',
1953 return q.qseries(repo, start=l-2, length=1, status='A',
1952 summary=opts.get('summary'))
1954 summary=opts.get('summary'))
1953
1955
1954 def setupheaderopts(ui, opts):
1956 def setupheaderopts(ui, opts):
1955 if not opts.get('user') and opts.get('currentuser'):
1957 if not opts.get('user') and opts.get('currentuser'):
1956 opts['user'] = ui.username()
1958 opts['user'] = ui.username()
1957 if not opts.get('date') and opts.get('currentdate'):
1959 if not opts.get('date') and opts.get('currentdate'):
1958 opts['date'] = "%d %d" % util.makedate()
1960 opts['date'] = "%d %d" % util.makedate()
1959
1961
1960 def new(ui, repo, patch, *args, **opts):
1962 def new(ui, repo, patch, *args, **opts):
1961 """create a new patch
1963 """create a new patch
1962
1964
1963 qnew creates a new patch on top of the currently-applied patch (if
1965 qnew creates a new patch on top of the currently-applied patch (if
1964 any). It will refuse to run if there are any outstanding changes
1966 any). It will refuse to run if there are any outstanding changes
1965 unless -f/--force is specified, in which case the patch will be
1967 unless -f/--force is specified, in which case the patch will be
1966 initialized with them. You may also use -I/--include,
1968 initialized with them. You may also use -I/--include,
1967 -X/--exclude, and/or a list of files after the patch name to add
1969 -X/--exclude, and/or a list of files after the patch name to add
1968 only changes to matching files to the new patch, leaving the rest
1970 only changes to matching files to the new patch, leaving the rest
1969 as uncommitted modifications.
1971 as uncommitted modifications.
1970
1972
1971 -u/--user and -d/--date can be used to set the (given) user and
1973 -u/--user and -d/--date can be used to set the (given) user and
1972 date, respectively. -U/--currentuser and -D/--currentdate set user
1974 date, respectively. -U/--currentuser and -D/--currentdate set user
1973 to current user and date to current date.
1975 to current user and date to current date.
1974
1976
1975 -e/--edit, -m/--message or -l/--logfile set the patch header as
1977 -e/--edit, -m/--message or -l/--logfile set the patch header as
1976 well as the commit message. If none is specified, the header is
1978 well as the commit message. If none is specified, the header is
1977 empty and the commit message is '[mq]: PATCH'.
1979 empty and the commit message is '[mq]: PATCH'.
1978
1980
1979 Use the -g/--git option to keep the patch in the git extended diff
1981 Use the -g/--git option to keep the patch in the git extended diff
1980 format. Read the diffs help topic for more information on why this
1982 format. Read the diffs help topic for more information on why this
1981 is important for preserving permission changes and copy/rename
1983 is important for preserving permission changes and copy/rename
1982 information.
1984 information.
1983 """
1985 """
1984 msg = cmdutil.logmessage(opts)
1986 msg = cmdutil.logmessage(opts)
1985 def getmsg(): return ui.edit(msg, ui.username())
1987 def getmsg(): return ui.edit(msg, ui.username())
1986 q = repo.mq
1988 q = repo.mq
1987 opts['msg'] = msg
1989 opts['msg'] = msg
1988 if opts.get('edit'):
1990 if opts.get('edit'):
1989 opts['msg'] = getmsg
1991 opts['msg'] = getmsg
1990 else:
1992 else:
1991 opts['msg'] = msg
1993 opts['msg'] = msg
1992 setupheaderopts(ui, opts)
1994 setupheaderopts(ui, opts)
1993 q.new(repo, patch, *args, **opts)
1995 q.new(repo, patch, *args, **opts)
1994 q.save_dirty()
1996 q.save_dirty()
1995 return 0
1997 return 0
1996
1998
1997 def refresh(ui, repo, *pats, **opts):
1999 def refresh(ui, repo, *pats, **opts):
1998 """update the current patch
2000 """update the current patch
1999
2001
2000 If any file patterns are provided, the refreshed patch will
2002 If any file patterns are provided, the refreshed patch will
2001 contain only the modifications that match those patterns; the
2003 contain only the modifications that match those patterns; the
2002 remaining modifications will remain in the working directory.
2004 remaining modifications will remain in the working directory.
2003
2005
2004 If -s/--short is specified, files currently included in the patch
2006 If -s/--short is specified, files currently included in the patch
2005 will be refreshed just like matched files and remain in the patch.
2007 will be refreshed just like matched files and remain in the patch.
2006
2008
2007 hg add/remove/copy/rename work as usual, though you might want to
2009 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
2010 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
2011 and renames. See the diffs help topic for more information on the
2010 git diff format.
2012 git diff format.
2011 """
2013 """
2012 q = repo.mq
2014 q = repo.mq
2013 message = cmdutil.logmessage(opts)
2015 message = cmdutil.logmessage(opts)
2014 if opts['edit']:
2016 if opts['edit']:
2015 if not q.applied:
2017 if not q.applied:
2016 ui.write(_("no patches applied\n"))
2018 ui.write(_("no patches applied\n"))
2017 return 1
2019 return 1
2018 if message:
2020 if message:
2019 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2021 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2020 patch = q.applied[-1].name
2022 patch = q.applied[-1].name
2021 ph = patchheader(q.join(patch))
2023 ph = patchheader(q.join(patch))
2022 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2024 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2023 setupheaderopts(ui, opts)
2025 setupheaderopts(ui, opts)
2024 ret = q.refresh(repo, pats, msg=message, **opts)
2026 ret = q.refresh(repo, pats, msg=message, **opts)
2025 q.save_dirty()
2027 q.save_dirty()
2026 return ret
2028 return ret
2027
2029
2028 def diff(ui, repo, *pats, **opts):
2030 def diff(ui, repo, *pats, **opts):
2029 """diff of the current patch and subsequent modifications
2031 """diff of the current patch and subsequent modifications
2030
2032
2031 Shows a diff which includes the current patch as well as any
2033 Shows a diff which includes the current patch as well as any
2032 changes which have been made in the working directory since the
2034 changes which have been made in the working directory since the
2033 last refresh (thus showing what the current patch would become
2035 last refresh (thus showing what the current patch would become
2034 after a qrefresh).
2036 after a qrefresh).
2035
2037
2036 Use 'hg diff' if you only want to see the changes made since the
2038 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 made
2039 last qrefresh, or 'hg export qtip' if you want to see changes made
2038 by the current patch without including changes made since the
2040 by the current patch without including changes made since the
2039 qrefresh.
2041 qrefresh.
2040 """
2042 """
2041 repo.mq.diff(repo, pats, opts)
2043 repo.mq.diff(repo, pats, opts)
2042 return 0
2044 return 0
2043
2045
2044 def fold(ui, repo, *files, **opts):
2046 def fold(ui, repo, *files, **opts):
2045 """fold the named patches into the current patch
2047 """fold the named patches into the current patch
2046
2048
2047 Patches must not yet be applied. Each patch will be successively
2049 Patches must not yet be applied. Each patch will be successively
2048 applied to the current patch in the order given. If all the
2050 applied to the current patch in the order given. If all the
2049 patches apply successfully, the current patch will be refreshed
2051 patches apply successfully, the current patch will be refreshed
2050 with the new cumulative patch, and the folded patches will be
2052 with the new cumulative patch, and the folded patches will be
2051 deleted. With -k/--keep, the folded patch files will not be
2053 deleted. With -k/--keep, the folded patch files will not be
2052 removed afterwards.
2054 removed afterwards.
2053
2055
2054 The header for each folded patch will be concatenated with the
2056 The header for each folded patch will be concatenated with the
2055 current patch header, separated by a line of '* * *'."""
2057 current patch header, separated by a line of '* * *'."""
2056
2058
2057 q = repo.mq
2059 q = repo.mq
2058
2060
2059 if not files:
2061 if not files:
2060 raise util.Abort(_('qfold requires at least one patch name'))
2062 raise util.Abort(_('qfold requires at least one patch name'))
2061 if not q.check_toppatch(repo):
2063 if not q.check_toppatch(repo)[0]:
2062 raise util.Abort(_('No patches applied'))
2064 raise util.Abort(_('No patches applied'))
2063 q.check_localchanges(repo)
2065 q.check_localchanges(repo)
2064
2066
2065 message = cmdutil.logmessage(opts)
2067 message = cmdutil.logmessage(opts)
2066 if opts['edit']:
2068 if opts['edit']:
2067 if message:
2069 if message:
2068 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2070 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2069
2071
2070 parent = q.lookup('qtip')
2072 parent = q.lookup('qtip')
2071 patches = []
2073 patches = []
2072 messages = []
2074 messages = []
2073 for f in files:
2075 for f in files:
2074 p = q.lookup(f)
2076 p = q.lookup(f)
2075 if p in patches or p == parent:
2077 if p in patches or p == parent:
2076 ui.warn(_('Skipping already folded patch %s') % p)
2078 ui.warn(_('Skipping already folded patch %s') % p)
2077 if q.isapplied(p):
2079 if q.isapplied(p):
2078 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2080 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2079 patches.append(p)
2081 patches.append(p)
2080
2082
2081 for p in patches:
2083 for p in patches:
2082 if not message:
2084 if not message:
2083 ph = patchheader(q.join(p))
2085 ph = patchheader(q.join(p))
2084 if ph.message:
2086 if ph.message:
2085 messages.append(ph.message)
2087 messages.append(ph.message)
2086 pf = q.join(p)
2088 pf = q.join(p)
2087 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2089 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2088 if not patchsuccess:
2090 if not patchsuccess:
2089 raise util.Abort(_('Error folding patch %s') % p)
2091 raise util.Abort(_('Error folding patch %s') % p)
2090 patch.updatedir(ui, repo, files)
2092 patch.updatedir(ui, repo, files)
2091
2093
2092 if not message:
2094 if not message:
2093 ph = patchheader(q.join(parent))
2095 ph = patchheader(q.join(parent))
2094 message, user = ph.message, ph.user
2096 message, user = ph.message, ph.user
2095 for msg in messages:
2097 for msg in messages:
2096 message.append('* * *')
2098 message.append('* * *')
2097 message.extend(msg)
2099 message.extend(msg)
2098 message = '\n'.join(message)
2100 message = '\n'.join(message)
2099
2101
2100 if opts['edit']:
2102 if opts['edit']:
2101 message = ui.edit(message, user or ui.username())
2103 message = ui.edit(message, user or ui.username())
2102
2104
2103 diffopts = q.patchopts(q.diffopts(), *patches)
2105 diffopts = q.patchopts(q.diffopts(), *patches)
2104 q.refresh(repo, msg=message, git=diffopts.git)
2106 q.refresh(repo, msg=message, git=diffopts.git)
2105 q.delete(repo, patches, opts)
2107 q.delete(repo, patches, opts)
2106 q.save_dirty()
2108 q.save_dirty()
2107
2109
2108 def goto(ui, repo, patch, **opts):
2110 def goto(ui, repo, patch, **opts):
2109 '''push or pop patches until named patch is at top of stack'''
2111 '''push or pop patches until named patch is at top of stack'''
2110 q = repo.mq
2112 q = repo.mq
2111 patch = q.lookup(patch)
2113 patch = q.lookup(patch)
2112 if q.isapplied(patch):
2114 if q.isapplied(patch):
2113 ret = q.pop(repo, patch, force=opts['force'])
2115 ret = q.pop(repo, patch, force=opts['force'])
2114 else:
2116 else:
2115 ret = q.push(repo, patch, force=opts['force'])
2117 ret = q.push(repo, patch, force=opts['force'])
2116 q.save_dirty()
2118 q.save_dirty()
2117 return ret
2119 return ret
2118
2120
2119 def guard(ui, repo, *args, **opts):
2121 def guard(ui, repo, *args, **opts):
2120 '''set or print guards for a patch
2122 '''set or print guards for a patch
2121
2123
2122 Guards control whether a patch can be pushed. A patch with no
2124 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
2125 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
2126 pushed only if the qselect command has activated it. A patch with
2125 a negative guard ("-foo") is never pushed if the qselect command
2127 a negative guard ("-foo") is never pushed if the qselect command
2126 has activated it.
2128 has activated it.
2127
2129
2128 With no arguments, print the currently active guards.
2130 With no arguments, print the currently active guards.
2129 With arguments, set guards for the named patch.
2131 With arguments, set guards for the named patch.
2130 NOTE: Specifying negative guards now requires '--'.
2132 NOTE: Specifying negative guards now requires '--'.
2131
2133
2132 To set guards on another patch::
2134 To set guards on another patch::
2133
2135
2134 hg qguard -- other.patch +2.6.17 -stable
2136 hg qguard -- other.patch +2.6.17 -stable
2135 '''
2137 '''
2136 def status(idx):
2138 def status(idx):
2137 guards = q.series_guards[idx] or ['unguarded']
2139 guards = q.series_guards[idx] or ['unguarded']
2138 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2140 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2139 q = repo.mq
2141 q = repo.mq
2140 patch = None
2142 patch = None
2141 args = list(args)
2143 args = list(args)
2142 if opts['list']:
2144 if opts['list']:
2143 if args or opts['none']:
2145 if args or opts['none']:
2144 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2146 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2145 for i in xrange(len(q.series)):
2147 for i in xrange(len(q.series)):
2146 status(i)
2148 status(i)
2147 return
2149 return
2148 if not args or args[0][0:1] in '-+':
2150 if not args or args[0][0:1] in '-+':
2149 if not q.applied:
2151 if not q.applied:
2150 raise util.Abort(_('no patches applied'))
2152 raise util.Abort(_('no patches applied'))
2151 patch = q.applied[-1].name
2153 patch = q.applied[-1].name
2152 if patch is None and args[0][0:1] not in '-+':
2154 if patch is None and args[0][0:1] not in '-+':
2153 patch = args.pop(0)
2155 patch = args.pop(0)
2154 if patch is None:
2156 if patch is None:
2155 raise util.Abort(_('no patch to work with'))
2157 raise util.Abort(_('no patch to work with'))
2156 if args or opts['none']:
2158 if args or opts['none']:
2157 idx = q.find_series(patch)
2159 idx = q.find_series(patch)
2158 if idx is None:
2160 if idx is None:
2159 raise util.Abort(_('no patch named %s') % patch)
2161 raise util.Abort(_('no patch named %s') % patch)
2160 q.set_guards(idx, args)
2162 q.set_guards(idx, args)
2161 q.save_dirty()
2163 q.save_dirty()
2162 else:
2164 else:
2163 status(q.series.index(q.lookup(patch)))
2165 status(q.series.index(q.lookup(patch)))
2164
2166
2165 def header(ui, repo, patch=None):
2167 def header(ui, repo, patch=None):
2166 """print the header of the topmost or specified patch"""
2168 """print the header of the topmost or specified patch"""
2167 q = repo.mq
2169 q = repo.mq
2168
2170
2169 if patch:
2171 if patch:
2170 patch = q.lookup(patch)
2172 patch = q.lookup(patch)
2171 else:
2173 else:
2172 if not q.applied:
2174 if not q.applied:
2173 ui.write('no patches applied\n')
2175 ui.write('no patches applied\n')
2174 return 1
2176 return 1
2175 patch = q.lookup('qtip')
2177 patch = q.lookup('qtip')
2176 ph = patchheader(repo.mq.join(patch))
2178 ph = patchheader(repo.mq.join(patch))
2177
2179
2178 ui.write('\n'.join(ph.message) + '\n')
2180 ui.write('\n'.join(ph.message) + '\n')
2179
2181
2180 def lastsavename(path):
2182 def lastsavename(path):
2181 (directory, base) = os.path.split(path)
2183 (directory, base) = os.path.split(path)
2182 names = os.listdir(directory)
2184 names = os.listdir(directory)
2183 namere = re.compile("%s.([0-9]+)" % base)
2185 namere = re.compile("%s.([0-9]+)" % base)
2184 maxindex = None
2186 maxindex = None
2185 maxname = None
2187 maxname = None
2186 for f in names:
2188 for f in names:
2187 m = namere.match(f)
2189 m = namere.match(f)
2188 if m:
2190 if m:
2189 index = int(m.group(1))
2191 index = int(m.group(1))
2190 if maxindex is None or index > maxindex:
2192 if maxindex is None or index > maxindex:
2191 maxindex = index
2193 maxindex = index
2192 maxname = f
2194 maxname = f
2193 if maxname:
2195 if maxname:
2194 return (os.path.join(directory, maxname), maxindex)
2196 return (os.path.join(directory, maxname), maxindex)
2195 return (None, None)
2197 return (None, None)
2196
2198
2197 def savename(path):
2199 def savename(path):
2198 (last, index) = lastsavename(path)
2200 (last, index) = lastsavename(path)
2199 if last is None:
2201 if last is None:
2200 index = 0
2202 index = 0
2201 newpath = path + ".%d" % (index + 1)
2203 newpath = path + ".%d" % (index + 1)
2202 return newpath
2204 return newpath
2203
2205
2204 def push(ui, repo, patch=None, **opts):
2206 def push(ui, repo, patch=None, **opts):
2205 """push the next patch onto the stack
2207 """push the next patch onto the stack
2206
2208
2207 When -f/--force is applied, all local changes in patched files
2209 When -f/--force is applied, all local changes in patched files
2208 will be lost.
2210 will be lost.
2209 """
2211 """
2210 q = repo.mq
2212 q = repo.mq
2211 mergeq = None
2213 mergeq = None
2212
2214
2213 if opts['merge']:
2215 if opts['merge']:
2214 if opts['name']:
2216 if opts['name']:
2215 newpath = repo.join(opts['name'])
2217 newpath = repo.join(opts['name'])
2216 else:
2218 else:
2217 newpath, i = lastsavename(q.path)
2219 newpath, i = lastsavename(q.path)
2218 if not newpath:
2220 if not newpath:
2219 ui.warn(_("no saved queues found, please use -n\n"))
2221 ui.warn(_("no saved queues found, please use -n\n"))
2220 return 1
2222 return 1
2221 mergeq = queue(ui, repo.join(""), newpath)
2223 mergeq = queue(ui, repo.join(""), newpath)
2222 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2224 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2223 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2225 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2224 mergeq=mergeq, all=opts.get('all'))
2226 mergeq=mergeq, all=opts.get('all'))
2225 return ret
2227 return ret
2226
2228
2227 def pop(ui, repo, patch=None, **opts):
2229 def pop(ui, repo, patch=None, **opts):
2228 """pop the current patch off the stack
2230 """pop the current patch off the stack
2229
2231
2230 By default, pops off the top of the patch stack. If given a patch
2232 By default, pops off the top of the patch stack. If given a patch
2231 name, keeps popping off patches until the named patch is at the
2233 name, keeps popping off patches until the named patch is at the
2232 top of the stack.
2234 top of the stack.
2233 """
2235 """
2234 localupdate = True
2236 localupdate = True
2235 if opts['name']:
2237 if opts['name']:
2236 q = queue(ui, repo.join(""), repo.join(opts['name']))
2238 q = queue(ui, repo.join(""), repo.join(opts['name']))
2237 ui.warn(_('using patch queue: %s\n') % q.path)
2239 ui.warn(_('using patch queue: %s\n') % q.path)
2238 localupdate = False
2240 localupdate = False
2239 else:
2241 else:
2240 q = repo.mq
2242 q = repo.mq
2241 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2243 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2242 all=opts['all'])
2244 all=opts['all'])
2243 q.save_dirty()
2245 q.save_dirty()
2244 return ret
2246 return ret
2245
2247
2246 def rename(ui, repo, patch, name=None, **opts):
2248 def rename(ui, repo, patch, name=None, **opts):
2247 """rename a patch
2249 """rename a patch
2248
2250
2249 With one argument, renames the current patch to PATCH1.
2251 With one argument, renames the current patch to PATCH1.
2250 With two arguments, renames PATCH1 to PATCH2."""
2252 With two arguments, renames PATCH1 to PATCH2."""
2251
2253
2252 q = repo.mq
2254 q = repo.mq
2253
2255
2254 if not name:
2256 if not name:
2255 name = patch
2257 name = patch
2256 patch = None
2258 patch = None
2257
2259
2258 if patch:
2260 if patch:
2259 patch = q.lookup(patch)
2261 patch = q.lookup(patch)
2260 else:
2262 else:
2261 if not q.applied:
2263 if not q.applied:
2262 ui.write(_('no patches applied\n'))
2264 ui.write(_('no patches applied\n'))
2263 return
2265 return
2264 patch = q.lookup('qtip')
2266 patch = q.lookup('qtip')
2265 absdest = q.join(name)
2267 absdest = q.join(name)
2266 if os.path.isdir(absdest):
2268 if os.path.isdir(absdest):
2267 name = normname(os.path.join(name, os.path.basename(patch)))
2269 name = normname(os.path.join(name, os.path.basename(patch)))
2268 absdest = q.join(name)
2270 absdest = q.join(name)
2269 if os.path.exists(absdest):
2271 if os.path.exists(absdest):
2270 raise util.Abort(_('%s already exists') % absdest)
2272 raise util.Abort(_('%s already exists') % absdest)
2271
2273
2272 if name in q.series:
2274 if name in q.series:
2273 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2275 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2274
2276
2275 if ui.verbose:
2277 if ui.verbose:
2276 ui.write('renaming %s to %s\n' % (patch, name))
2278 ui.write('renaming %s to %s\n' % (patch, name))
2277 i = q.find_series(patch)
2279 i = q.find_series(patch)
2278 guards = q.guard_re.findall(q.full_series[i])
2280 guards = q.guard_re.findall(q.full_series[i])
2279 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2281 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2280 q.parse_series()
2282 q.parse_series()
2281 q.series_dirty = 1
2283 q.series_dirty = 1
2282
2284
2283 info = q.isapplied(patch)
2285 info = q.isapplied(patch)
2284 if info:
2286 if info:
2285 q.applied[info[0]] = statusentry(info[1], name)
2287 q.applied[info[0]] = statusentry(info[1], name)
2286 q.applied_dirty = 1
2288 q.applied_dirty = 1
2287
2289
2288 util.rename(q.join(patch), absdest)
2290 util.rename(q.join(patch), absdest)
2289 r = q.qrepo()
2291 r = q.qrepo()
2290 if r:
2292 if r:
2291 wlock = r.wlock()
2293 wlock = r.wlock()
2292 try:
2294 try:
2293 if r.dirstate[patch] == 'a':
2295 if r.dirstate[patch] == 'a':
2294 r.dirstate.forget(patch)
2296 r.dirstate.forget(patch)
2295 r.dirstate.add(name)
2297 r.dirstate.add(name)
2296 else:
2298 else:
2297 if r.dirstate[name] == 'r':
2299 if r.dirstate[name] == 'r':
2298 r.undelete([name])
2300 r.undelete([name])
2299 r.copy(patch, name)
2301 r.copy(patch, name)
2300 r.remove([patch], False)
2302 r.remove([patch], False)
2301 finally:
2303 finally:
2302 wlock.release()
2304 wlock.release()
2303
2305
2304 q.save_dirty()
2306 q.save_dirty()
2305
2307
2306 def restore(ui, repo, rev, **opts):
2308 def restore(ui, repo, rev, **opts):
2307 """restore the queue state saved by a revision"""
2309 """restore the queue state saved by a revision"""
2308 rev = repo.lookup(rev)
2310 rev = repo.lookup(rev)
2309 q = repo.mq
2311 q = repo.mq
2310 q.restore(repo, rev, delete=opts['delete'],
2312 q.restore(repo, rev, delete=opts['delete'],
2311 qupdate=opts['update'])
2313 qupdate=opts['update'])
2312 q.save_dirty()
2314 q.save_dirty()
2313 return 0
2315 return 0
2314
2316
2315 def save(ui, repo, **opts):
2317 def save(ui, repo, **opts):
2316 """save current queue state"""
2318 """save current queue state"""
2317 q = repo.mq
2319 q = repo.mq
2318 message = cmdutil.logmessage(opts)
2320 message = cmdutil.logmessage(opts)
2319 ret = q.save(repo, msg=message)
2321 ret = q.save(repo, msg=message)
2320 if ret:
2322 if ret:
2321 return ret
2323 return ret
2322 q.save_dirty()
2324 q.save_dirty()
2323 if opts['copy']:
2325 if opts['copy']:
2324 path = q.path
2326 path = q.path
2325 if opts['name']:
2327 if opts['name']:
2326 newpath = os.path.join(q.basepath, opts['name'])
2328 newpath = os.path.join(q.basepath, opts['name'])
2327 if os.path.exists(newpath):
2329 if os.path.exists(newpath):
2328 if not os.path.isdir(newpath):
2330 if not os.path.isdir(newpath):
2329 raise util.Abort(_('destination %s exists and is not '
2331 raise util.Abort(_('destination %s exists and is not '
2330 'a directory') % newpath)
2332 'a directory') % newpath)
2331 if not opts['force']:
2333 if not opts['force']:
2332 raise util.Abort(_('destination %s exists, '
2334 raise util.Abort(_('destination %s exists, '
2333 'use -f to force') % newpath)
2335 'use -f to force') % newpath)
2334 else:
2336 else:
2335 newpath = savename(path)
2337 newpath = savename(path)
2336 ui.warn(_("copy %s to %s\n") % (path, newpath))
2338 ui.warn(_("copy %s to %s\n") % (path, newpath))
2337 util.copyfiles(path, newpath)
2339 util.copyfiles(path, newpath)
2338 if opts['empty']:
2340 if opts['empty']:
2339 try:
2341 try:
2340 os.unlink(q.join(q.status_path))
2342 os.unlink(q.join(q.status_path))
2341 except:
2343 except:
2342 pass
2344 pass
2343 return 0
2345 return 0
2344
2346
2345 def strip(ui, repo, rev, **opts):
2347 def strip(ui, repo, rev, **opts):
2346 """strip a revision and all its descendants from the repository
2348 """strip a revision and all its descendants from the repository
2347
2349
2348 If one of the working directory's parent revisions is stripped, the
2350 If one of the working directory's parent revisions is stripped, the
2349 working directory will be updated to the parent of the stripped
2351 working directory will be updated to the parent of the stripped
2350 revision.
2352 revision.
2351 """
2353 """
2352 backup = 'all'
2354 backup = 'all'
2353 if opts['backup']:
2355 if opts['backup']:
2354 backup = 'strip'
2356 backup = 'strip'
2355 elif opts['nobackup']:
2357 elif opts['nobackup']:
2356 backup = 'none'
2358 backup = 'none'
2357
2359
2358 rev = repo.lookup(rev)
2360 rev = repo.lookup(rev)
2359 p = repo.dirstate.parents()
2361 p = repo.dirstate.parents()
2360 cl = repo.changelog
2362 cl = repo.changelog
2361 update = True
2363 update = True
2362 if p[0] == nullid:
2364 if p[0] == nullid:
2363 update = False
2365 update = False
2364 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2366 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2365 update = False
2367 update = False
2366 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2368 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2367 update = False
2369 update = False
2368
2370
2369 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2371 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2370 return 0
2372 return 0
2371
2373
2372 def select(ui, repo, *args, **opts):
2374 def select(ui, repo, *args, **opts):
2373 '''set or print guarded patches to push
2375 '''set or print guarded patches to push
2374
2376
2375 Use the qguard command to set or print guards on patch, then use
2377 Use the qguard command to set or print guards on patch, then use
2376 qselect to tell mq which guards to use. A patch will be pushed if
2378 qselect to tell mq which guards to use. A patch will be pushed if
2377 it has no guards or any positive guards match the currently
2379 it has no guards or any positive guards match the currently
2378 selected guard, but will not be pushed if any negative guards
2380 selected guard, but will not be pushed if any negative guards
2379 match the current guard. For example::
2381 match the current guard. For example::
2380
2382
2381 qguard foo.patch -stable (negative guard)
2383 qguard foo.patch -stable (negative guard)
2382 qguard bar.patch +stable (positive guard)
2384 qguard bar.patch +stable (positive guard)
2383 qselect stable
2385 qselect stable
2384
2386
2385 This activates the "stable" guard. mq will skip foo.patch (because
2387 This activates the "stable" guard. mq will skip foo.patch (because
2386 it has a negative match) but push bar.patch (because it has a
2388 it has a negative match) but push bar.patch (because it has a
2387 positive match).
2389 positive match).
2388
2390
2389 With no arguments, prints the currently active guards.
2391 With no arguments, prints the currently active guards.
2390 With one argument, sets the active guard.
2392 With one argument, sets the active guard.
2391
2393
2392 Use -n/--none to deactivate guards (no other arguments needed).
2394 Use -n/--none to deactivate guards (no other arguments needed).
2393 When no guards are active, patches with positive guards are
2395 When no guards are active, patches with positive guards are
2394 skipped and patches with negative guards are pushed.
2396 skipped and patches with negative guards are pushed.
2395
2397
2396 qselect can change the guards on applied patches. It does not pop
2398 qselect can change the guards on applied patches. It does not pop
2397 guarded patches by default. Use --pop to pop back to the last
2399 guarded patches by default. Use --pop to pop back to the last
2398 applied patch that is not guarded. Use --reapply (which implies
2400 applied patch that is not guarded. Use --reapply (which implies
2399 --pop) to push back to the current patch afterwards, but skip
2401 --pop) to push back to the current patch afterwards, but skip
2400 guarded patches.
2402 guarded patches.
2401
2403
2402 Use -s/--series to print a list of all guards in the series file
2404 Use -s/--series to print a list of all guards in the series file
2403 (no other arguments needed). Use -v for more information.'''
2405 (no other arguments needed). Use -v for more information.'''
2404
2406
2405 q = repo.mq
2407 q = repo.mq
2406 guards = q.active()
2408 guards = q.active()
2407 if args or opts['none']:
2409 if args or opts['none']:
2408 old_unapplied = q.unapplied(repo)
2410 old_unapplied = q.unapplied(repo)
2409 old_guarded = [i for i in xrange(len(q.applied)) if
2411 old_guarded = [i for i in xrange(len(q.applied)) if
2410 not q.pushable(i)[0]]
2412 not q.pushable(i)[0]]
2411 q.set_active(args)
2413 q.set_active(args)
2412 q.save_dirty()
2414 q.save_dirty()
2413 if not args:
2415 if not args:
2414 ui.status(_('guards deactivated\n'))
2416 ui.status(_('guards deactivated\n'))
2415 if not opts['pop'] and not opts['reapply']:
2417 if not opts['pop'] and not opts['reapply']:
2416 unapplied = q.unapplied(repo)
2418 unapplied = q.unapplied(repo)
2417 guarded = [i for i in xrange(len(q.applied))
2419 guarded = [i for i in xrange(len(q.applied))
2418 if not q.pushable(i)[0]]
2420 if not q.pushable(i)[0]]
2419 if len(unapplied) != len(old_unapplied):
2421 if len(unapplied) != len(old_unapplied):
2420 ui.status(_('number of unguarded, unapplied patches has '
2422 ui.status(_('number of unguarded, unapplied patches has '
2421 'changed from %d to %d\n') %
2423 'changed from %d to %d\n') %
2422 (len(old_unapplied), len(unapplied)))
2424 (len(old_unapplied), len(unapplied)))
2423 if len(guarded) != len(old_guarded):
2425 if len(guarded) != len(old_guarded):
2424 ui.status(_('number of guarded, applied patches has changed '
2426 ui.status(_('number of guarded, applied patches has changed '
2425 'from %d to %d\n') %
2427 'from %d to %d\n') %
2426 (len(old_guarded), len(guarded)))
2428 (len(old_guarded), len(guarded)))
2427 elif opts['series']:
2429 elif opts['series']:
2428 guards = {}
2430 guards = {}
2429 noguards = 0
2431 noguards = 0
2430 for gs in q.series_guards:
2432 for gs in q.series_guards:
2431 if not gs:
2433 if not gs:
2432 noguards += 1
2434 noguards += 1
2433 for g in gs:
2435 for g in gs:
2434 guards.setdefault(g, 0)
2436 guards.setdefault(g, 0)
2435 guards[g] += 1
2437 guards[g] += 1
2436 if ui.verbose:
2438 if ui.verbose:
2437 guards['NONE'] = noguards
2439 guards['NONE'] = noguards
2438 guards = guards.items()
2440 guards = guards.items()
2439 guards.sort(key=lambda x: x[0][1:])
2441 guards.sort(key=lambda x: x[0][1:])
2440 if guards:
2442 if guards:
2441 ui.note(_('guards in series file:\n'))
2443 ui.note(_('guards in series file:\n'))
2442 for guard, count in guards:
2444 for guard, count in guards:
2443 ui.note('%2d ' % count)
2445 ui.note('%2d ' % count)
2444 ui.write(guard, '\n')
2446 ui.write(guard, '\n')
2445 else:
2447 else:
2446 ui.note(_('no guards in series file\n'))
2448 ui.note(_('no guards in series file\n'))
2447 else:
2449 else:
2448 if guards:
2450 if guards:
2449 ui.note(_('active guards:\n'))
2451 ui.note(_('active guards:\n'))
2450 for g in guards:
2452 for g in guards:
2451 ui.write(g, '\n')
2453 ui.write(g, '\n')
2452 else:
2454 else:
2453 ui.write(_('no active guards\n'))
2455 ui.write(_('no active guards\n'))
2454 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2456 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2455 popped = False
2457 popped = False
2456 if opts['pop'] or opts['reapply']:
2458 if opts['pop'] or opts['reapply']:
2457 for i in xrange(len(q.applied)):
2459 for i in xrange(len(q.applied)):
2458 pushable, reason = q.pushable(i)
2460 pushable, reason = q.pushable(i)
2459 if not pushable:
2461 if not pushable:
2460 ui.status(_('popping guarded patches\n'))
2462 ui.status(_('popping guarded patches\n'))
2461 popped = True
2463 popped = True
2462 if i == 0:
2464 if i == 0:
2463 q.pop(repo, all=True)
2465 q.pop(repo, all=True)
2464 else:
2466 else:
2465 q.pop(repo, i-1)
2467 q.pop(repo, i-1)
2466 break
2468 break
2467 if popped:
2469 if popped:
2468 try:
2470 try:
2469 if reapply:
2471 if reapply:
2470 ui.status(_('reapplying unguarded patches\n'))
2472 ui.status(_('reapplying unguarded patches\n'))
2471 q.push(repo, reapply)
2473 q.push(repo, reapply)
2472 finally:
2474 finally:
2473 q.save_dirty()
2475 q.save_dirty()
2474
2476
2475 def finish(ui, repo, *revrange, **opts):
2477 def finish(ui, repo, *revrange, **opts):
2476 """move applied patches into repository history
2478 """move applied patches into repository history
2477
2479
2478 Finishes the specified revisions (corresponding to applied
2480 Finishes the specified revisions (corresponding to applied
2479 patches) by moving them out of mq control into regular repository
2481 patches) by moving them out of mq control into regular repository
2480 history.
2482 history.
2481
2483
2482 Accepts a revision range or the -a/--applied option. If --applied
2484 Accepts a revision range or the -a/--applied option. If --applied
2483 is specified, all applied mq revisions are removed from mq
2485 is specified, all applied mq revisions are removed from mq
2484 control. Otherwise, the given revisions must be at the base of the
2486 control. Otherwise, the given revisions must be at the base of the
2485 stack of applied patches.
2487 stack of applied patches.
2486
2488
2487 This can be especially useful if your changes have been applied to
2489 This can be especially useful if your changes have been applied to
2488 an upstream repository, or if you are about to push your changes
2490 an upstream repository, or if you are about to push your changes
2489 to upstream.
2491 to upstream.
2490 """
2492 """
2491 if not opts['applied'] and not revrange:
2493 if not opts['applied'] and not revrange:
2492 raise util.Abort(_('no revisions specified'))
2494 raise util.Abort(_('no revisions specified'))
2493 elif opts['applied']:
2495 elif opts['applied']:
2494 revrange = ('qbase:qtip',) + revrange
2496 revrange = ('qbase:qtip',) + revrange
2495
2497
2496 q = repo.mq
2498 q = repo.mq
2497 if not q.applied:
2499 if not q.applied:
2498 ui.status(_('no patches applied\n'))
2500 ui.status(_('no patches applied\n'))
2499 return 0
2501 return 0
2500
2502
2501 revs = cmdutil.revrange(repo, revrange)
2503 revs = cmdutil.revrange(repo, revrange)
2502 q.finish(repo, revs)
2504 q.finish(repo, revs)
2503 q.save_dirty()
2505 q.save_dirty()
2504 return 0
2506 return 0
2505
2507
2506 def reposetup(ui, repo):
2508 def reposetup(ui, repo):
2507 class mqrepo(repo.__class__):
2509 class mqrepo(repo.__class__):
2508 @util.propertycache
2510 @util.propertycache
2509 def mq(self):
2511 def mq(self):
2510 return queue(self.ui, self.join(""))
2512 return queue(self.ui, self.join(""))
2511
2513
2512 def abort_if_wdir_patched(self, errmsg, force=False):
2514 def abort_if_wdir_patched(self, errmsg, force=False):
2513 if self.mq.applied and not force:
2515 if self.mq.applied and not force:
2514 parent = hex(self.dirstate.parents()[0])
2516 parent = hex(self.dirstate.parents()[0])
2515 if parent in [s.rev for s in self.mq.applied]:
2517 if parent in [s.rev for s in self.mq.applied]:
2516 raise util.Abort(errmsg)
2518 raise util.Abort(errmsg)
2517
2519
2518 def commit(self, text="", user=None, date=None, match=None,
2520 def commit(self, text="", user=None, date=None, match=None,
2519 force=False, editor=False, extra={}):
2521 force=False, editor=False, extra={}):
2520 self.abort_if_wdir_patched(
2522 self.abort_if_wdir_patched(
2521 _('cannot commit over an applied mq patch'),
2523 _('cannot commit over an applied mq patch'),
2522 force)
2524 force)
2523
2525
2524 return super(mqrepo, self).commit(text, user, date, match, force,
2526 return super(mqrepo, self).commit(text, user, date, match, force,
2525 editor, extra)
2527 editor, extra)
2526
2528
2527 def push(self, remote, force=False, revs=None):
2529 def push(self, remote, force=False, revs=None):
2528 if self.mq.applied and not force and not revs:
2530 if self.mq.applied and not force and not revs:
2529 raise util.Abort(_('source has mq patches applied'))
2531 raise util.Abort(_('source has mq patches applied'))
2530 return super(mqrepo, self).push(remote, force, revs)
2532 return super(mqrepo, self).push(remote, force, revs)
2531
2533
2532 def _findtags(self):
2534 def _findtags(self):
2533 '''augment tags from base class with patch tags'''
2535 '''augment tags from base class with patch tags'''
2534 result = super(mqrepo, self)._findtags()
2536 result = super(mqrepo, self)._findtags()
2535
2537
2536 q = self.mq
2538 q = self.mq
2537 if not q.applied:
2539 if not q.applied:
2538 return result
2540 return result
2539
2541
2540 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2542 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2541
2543
2542 if mqtags[-1][0] not in self.changelog.nodemap:
2544 if mqtags[-1][0] not in self.changelog.nodemap:
2543 self.ui.warn(_('mq status file refers to unknown node %s\n')
2545 self.ui.warn(_('mq status file refers to unknown node %s\n')
2544 % short(mqtags[-1][0]))
2546 % short(mqtags[-1][0]))
2545 return result
2547 return result
2546
2548
2547 mqtags.append((mqtags[-1][0], 'qtip'))
2549 mqtags.append((mqtags[-1][0], 'qtip'))
2548 mqtags.append((mqtags[0][0], 'qbase'))
2550 mqtags.append((mqtags[0][0], 'qbase'))
2549 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2551 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2550 tags = result[0]
2552 tags = result[0]
2551 for patch in mqtags:
2553 for patch in mqtags:
2552 if patch[1] in tags:
2554 if patch[1] in tags:
2553 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2555 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2554 % patch[1])
2556 % patch[1])
2555 else:
2557 else:
2556 tags[patch[1]] = patch[0]
2558 tags[patch[1]] = patch[0]
2557
2559
2558 return result
2560 return result
2559
2561
2560 def _branchtags(self, partial, lrev):
2562 def _branchtags(self, partial, lrev):
2561 q = self.mq
2563 q = self.mq
2562 if not q.applied:
2564 if not q.applied:
2563 return super(mqrepo, self)._branchtags(partial, lrev)
2565 return super(mqrepo, self)._branchtags(partial, lrev)
2564
2566
2565 cl = self.changelog
2567 cl = self.changelog
2566 qbasenode = bin(q.applied[0].rev)
2568 qbasenode = bin(q.applied[0].rev)
2567 if qbasenode not in cl.nodemap:
2569 if qbasenode not in cl.nodemap:
2568 self.ui.warn(_('mq status file refers to unknown node %s\n')
2570 self.ui.warn(_('mq status file refers to unknown node %s\n')
2569 % short(qbasenode))
2571 % short(qbasenode))
2570 return super(mqrepo, self)._branchtags(partial, lrev)
2572 return super(mqrepo, self)._branchtags(partial, lrev)
2571
2573
2572 qbase = cl.rev(qbasenode)
2574 qbase = cl.rev(qbasenode)
2573 start = lrev + 1
2575 start = lrev + 1
2574 if start < qbase:
2576 if start < qbase:
2575 # update the cache (excluding the patches) and save it
2577 # update the cache (excluding the patches) and save it
2576 self._updatebranchcache(partial, lrev+1, qbase)
2578 self._updatebranchcache(partial, lrev+1, qbase)
2577 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2579 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2578 start = qbase
2580 start = qbase
2579 # if start = qbase, the cache is as updated as it should be.
2581 # if start = qbase, the cache is as updated as it should be.
2580 # if start > qbase, the cache includes (part of) the patches.
2582 # if start > qbase, the cache includes (part of) the patches.
2581 # we might as well use it, but we won't save it.
2583 # we might as well use it, but we won't save it.
2582
2584
2583 # update the cache up to the tip
2585 # update the cache up to the tip
2584 self._updatebranchcache(partial, start, len(cl))
2586 self._updatebranchcache(partial, start, len(cl))
2585
2587
2586 return partial
2588 return partial
2587
2589
2588 if repo.local():
2590 if repo.local():
2589 repo.__class__ = mqrepo
2591 repo.__class__ = mqrepo
2590
2592
2591 def mqimport(orig, ui, repo, *args, **kwargs):
2593 def mqimport(orig, ui, repo, *args, **kwargs):
2592 if hasattr(repo, 'abort_if_wdir_patched') and not kwargs.get('no_commit', False):
2594 if hasattr(repo, 'abort_if_wdir_patched') and not kwargs.get('no_commit', False):
2593 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2595 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2594 kwargs.get('force'))
2596 kwargs.get('force'))
2595 return orig(ui, repo, *args, **kwargs)
2597 return orig(ui, repo, *args, **kwargs)
2596
2598
2597 def uisetup(ui):
2599 def uisetup(ui):
2598 extensions.wrapcommand(commands.table, 'import', mqimport)
2600 extensions.wrapcommand(commands.table, 'import', mqimport)
2599
2601
2600 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2602 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2601
2603
2602 cmdtable = {
2604 cmdtable = {
2603 "qapplied":
2605 "qapplied":
2604 (applied,
2606 (applied,
2605 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2607 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2606 _('hg qapplied [-1] [-s] [PATCH]')),
2608 _('hg qapplied [-1] [-s] [PATCH]')),
2607 "qclone":
2609 "qclone":
2608 (clone,
2610 (clone,
2609 [('', 'pull', None, _('use pull protocol to copy metadata')),
2611 [('', 'pull', None, _('use pull protocol to copy metadata')),
2610 ('U', 'noupdate', None, _('do not update the new working directories')),
2612 ('U', 'noupdate', None, _('do not update the new working directories')),
2611 ('', 'uncompressed', None,
2613 ('', 'uncompressed', None,
2612 _('use uncompressed transfer (fast over LAN)')),
2614 _('use uncompressed transfer (fast over LAN)')),
2613 ('p', 'patches', '', _('location of source patch repository')),
2615 ('p', 'patches', '', _('location of source patch repository')),
2614 ] + commands.remoteopts,
2616 ] + commands.remoteopts,
2615 _('hg qclone [OPTION]... SOURCE [DEST]')),
2617 _('hg qclone [OPTION]... SOURCE [DEST]')),
2616 "qcommit|qci":
2618 "qcommit|qci":
2617 (commit,
2619 (commit,
2618 commands.table["^commit|ci"][1],
2620 commands.table["^commit|ci"][1],
2619 _('hg qcommit [OPTION]... [FILE]...')),
2621 _('hg qcommit [OPTION]... [FILE]...')),
2620 "^qdiff":
2622 "^qdiff":
2621 (diff,
2623 (diff,
2622 commands.diffopts + commands.diffopts2 + commands.walkopts,
2624 commands.diffopts + commands.diffopts2 + commands.walkopts,
2623 _('hg qdiff [OPTION]... [FILE]...')),
2625 _('hg qdiff [OPTION]... [FILE]...')),
2624 "qdelete|qremove|qrm":
2626 "qdelete|qremove|qrm":
2625 (delete,
2627 (delete,
2626 [('k', 'keep', None, _('keep patch file')),
2628 [('k', 'keep', None, _('keep patch file')),
2627 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2629 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2628 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2630 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2629 'qfold':
2631 'qfold':
2630 (fold,
2632 (fold,
2631 [('e', 'edit', None, _('edit patch header')),
2633 [('e', 'edit', None, _('edit patch header')),
2632 ('k', 'keep', None, _('keep folded patch files')),
2634 ('k', 'keep', None, _('keep folded patch files')),
2633 ] + commands.commitopts,
2635 ] + commands.commitopts,
2634 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2636 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2635 'qgoto':
2637 'qgoto':
2636 (goto,
2638 (goto,
2637 [('f', 'force', None, _('overwrite any local changes'))],
2639 [('f', 'force', None, _('overwrite any local changes'))],
2638 _('hg qgoto [OPTION]... PATCH')),
2640 _('hg qgoto [OPTION]... PATCH')),
2639 'qguard':
2641 'qguard':
2640 (guard,
2642 (guard,
2641 [('l', 'list', None, _('list all patches and guards')),
2643 [('l', 'list', None, _('list all patches and guards')),
2642 ('n', 'none', None, _('drop all guards'))],
2644 ('n', 'none', None, _('drop all guards'))],
2643 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2645 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2644 'qheader': (header, [], _('hg qheader [PATCH]')),
2646 'qheader': (header, [], _('hg qheader [PATCH]')),
2645 "^qimport":
2647 "^qimport":
2646 (qimport,
2648 (qimport,
2647 [('e', 'existing', None, _('import file in patch directory')),
2649 [('e', 'existing', None, _('import file in patch directory')),
2648 ('n', 'name', '', _('name of patch file')),
2650 ('n', 'name', '', _('name of patch file')),
2649 ('f', 'force', None, _('overwrite existing files')),
2651 ('f', 'force', None, _('overwrite existing files')),
2650 ('r', 'rev', [], _('place existing revisions under mq control')),
2652 ('r', 'rev', [], _('place existing revisions under mq control')),
2651 ('g', 'git', None, _('use git extended diff format')),
2653 ('g', 'git', None, _('use git extended diff format')),
2652 ('P', 'push', None, _('qpush after importing'))],
2654 ('P', 'push', None, _('qpush after importing'))],
2653 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2655 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2654 "^qinit":
2656 "^qinit":
2655 (init,
2657 (init,
2656 [('c', 'create-repo', None, _('create queue repository'))],
2658 [('c', 'create-repo', None, _('create queue repository'))],
2657 _('hg qinit [-c]')),
2659 _('hg qinit [-c]')),
2658 "qnew":
2660 "qnew":
2659 (new,
2661 (new,
2660 [('e', 'edit', None, _('edit commit message')),
2662 [('e', 'edit', None, _('edit commit message')),
2661 ('f', 'force', None, _('import uncommitted changes into patch')),
2663 ('f', 'force', None, _('import uncommitted changes into patch')),
2662 ('g', 'git', None, _('use git extended diff format')),
2664 ('g', 'git', None, _('use git extended diff format')),
2663 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2665 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2664 ('u', 'user', '', _('add "From: <given user>" to patch')),
2666 ('u', 'user', '', _('add "From: <given user>" to patch')),
2665 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2667 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2666 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2668 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2667 ] + commands.walkopts + commands.commitopts,
2669 ] + commands.walkopts + commands.commitopts,
2668 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2670 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2669 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2671 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2670 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2672 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2671 "^qpop":
2673 "^qpop":
2672 (pop,
2674 (pop,
2673 [('a', 'all', None, _('pop all patches')),
2675 [('a', 'all', None, _('pop all patches')),
2674 ('n', 'name', '', _('queue name to pop')),
2676 ('n', 'name', '', _('queue name to pop')),
2675 ('f', 'force', None, _('forget any local changes to patched files'))],
2677 ('f', 'force', None, _('forget any local changes to patched files'))],
2676 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2678 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2677 "^qpush":
2679 "^qpush":
2678 (push,
2680 (push,
2679 [('f', 'force', None, _('apply if the patch has rejects')),
2681 [('f', 'force', None, _('apply if the patch has rejects')),
2680 ('l', 'list', None, _('list patch name in commit text')),
2682 ('l', 'list', None, _('list patch name in commit text')),
2681 ('a', 'all', None, _('apply all patches')),
2683 ('a', 'all', None, _('apply all patches')),
2682 ('m', 'merge', None, _('merge from another queue')),
2684 ('m', 'merge', None, _('merge from another queue')),
2683 ('n', 'name', '', _('merge queue name'))],
2685 ('n', 'name', '', _('merge queue name'))],
2684 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2686 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2685 "^qrefresh":
2687 "^qrefresh":
2686 (refresh,
2688 (refresh,
2687 [('e', 'edit', None, _('edit commit message')),
2689 [('e', 'edit', None, _('edit commit message')),
2688 ('g', 'git', None, _('use git extended diff format')),
2690 ('g', 'git', None, _('use git extended diff format')),
2689 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2691 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2690 ('U', 'currentuser', None, _('add/update author field in patch with current user')),
2692 ('U', 'currentuser', None, _('add/update author field in patch with current user')),
2691 ('u', 'user', '', _('add/update author field in patch with given user')),
2693 ('u', 'user', '', _('add/update author field in patch with given user')),
2692 ('D', 'currentdate', None, _('add/update date field in patch with current date')),
2694 ('D', 'currentdate', None, _('add/update date field in patch with current date')),
2693 ('d', 'date', '', _('add/update date field in patch with given date'))
2695 ('d', 'date', '', _('add/update date field in patch with given date'))
2694 ] + commands.walkopts + commands.commitopts,
2696 ] + commands.walkopts + commands.commitopts,
2695 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2697 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2696 'qrename|qmv':
2698 'qrename|qmv':
2697 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2699 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2698 "qrestore":
2700 "qrestore":
2699 (restore,
2701 (restore,
2700 [('d', 'delete', None, _('delete save entry')),
2702 [('d', 'delete', None, _('delete save entry')),
2701 ('u', 'update', None, _('update queue working directory'))],
2703 ('u', 'update', None, _('update queue working directory'))],
2702 _('hg qrestore [-d] [-u] REV')),
2704 _('hg qrestore [-d] [-u] REV')),
2703 "qsave":
2705 "qsave":
2704 (save,
2706 (save,
2705 [('c', 'copy', None, _('copy patch directory')),
2707 [('c', 'copy', None, _('copy patch directory')),
2706 ('n', 'name', '', _('copy directory name')),
2708 ('n', 'name', '', _('copy directory name')),
2707 ('e', 'empty', None, _('clear queue status file')),
2709 ('e', 'empty', None, _('clear queue status file')),
2708 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2710 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2709 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2711 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2710 "qselect":
2712 "qselect":
2711 (select,
2713 (select,
2712 [('n', 'none', None, _('disable all guards')),
2714 [('n', 'none', None, _('disable all guards')),
2713 ('s', 'series', None, _('list all guards in series file')),
2715 ('s', 'series', None, _('list all guards in series file')),
2714 ('', 'pop', None, _('pop to before first guarded applied patch')),
2716 ('', 'pop', None, _('pop to before first guarded applied patch')),
2715 ('', 'reapply', None, _('pop, then reapply patches'))],
2717 ('', 'reapply', None, _('pop, then reapply patches'))],
2716 _('hg qselect [OPTION]... [GUARD]...')),
2718 _('hg qselect [OPTION]... [GUARD]...')),
2717 "qseries":
2719 "qseries":
2718 (series,
2720 (series,
2719 [('m', 'missing', None, _('print patches not in series')),
2721 [('m', 'missing', None, _('print patches not in series')),
2720 ] + seriesopts,
2722 ] + seriesopts,
2721 _('hg qseries [-ms]')),
2723 _('hg qseries [-ms]')),
2722 "^strip":
2724 "^strip":
2723 (strip,
2725 (strip,
2724 [('f', 'force', None, _('force removal with local changes')),
2726 [('f', 'force', None, _('force removal with local changes')),
2725 ('b', 'backup', None, _('bundle unrelated changesets')),
2727 ('b', 'backup', None, _('bundle unrelated changesets')),
2726 ('n', 'nobackup', None, _('no backups'))],
2728 ('n', 'nobackup', None, _('no backups'))],
2727 _('hg strip [-f] [-b] [-n] REV')),
2729 _('hg strip [-f] [-b] [-n] REV')),
2728 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2730 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2729 "qunapplied":
2731 "qunapplied":
2730 (unapplied,
2732 (unapplied,
2731 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2733 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2732 _('hg qunapplied [-1] [-s] [PATCH]')),
2734 _('hg qunapplied [-1] [-s] [PATCH]')),
2733 "qfinish":
2735 "qfinish":
2734 (finish,
2736 (finish,
2735 [('a', 'applied', None, _('finish all applied changesets'))],
2737 [('a', 'applied', None, _('finish all applied changesets'))],
2736 _('hg qfinish [-a] [REV]...')),
2738 _('hg qfinish [-a] [REV]...')),
2737 }
2739 }
@@ -1,62 +1,70 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 echo "[extensions]" >> $HGRCPATH
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
5 echo "[mq]" >> $HGRCPATH
6 echo "git=keep" >> $HGRCPATH
5
7
6 echo % init
8 echo % init
7 hg init a
9 hg init a
8 cd a
10 cd a
9
11
10 echo % commit
12 echo % commit
11 echo 'base' > base
13 echo 'base' > base
12 hg ci -Ambase -d '1 0'
14 hg ci -Ambase -d '1 0'
13
15
14 echo % qnew mqbase
16 echo % qnew mqbase
15 hg qnew -mmqbase mqbase
17 hg qnew -mmqbase mqbase
16
18
17 echo % qrefresh
19 echo % qrefresh
18 echo 'patched' > base
20 echo 'patched' > base
19 hg qrefresh
21 hg qrefresh
20
22
21 echo % qdiff
23 echo % qdiff
22 hg qdiff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
24 hg qdiff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
23 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
25 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
24
26
25 echo % qdiff dirname
27 echo % qdiff dirname
26 hg qdiff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
28 hg qdiff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
27 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
29 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
28
30
29 echo % qdiff filename
31 echo % qdiff filename
30 hg qdiff --nodates base
32 hg qdiff --nodates base
31
33
32 echo % revert
34 echo % revert
33 hg revert -a
35 hg revert -a
34
36
35 echo % qpop
37 echo % qpop
36 hg qpop
38 hg qpop
37
39
38 echo % qdelete mqbase
40 echo % qdelete mqbase
39 hg qdelete mqbase
41 hg qdelete mqbase
40
42
41 echo % commit 2
43 echo % commit 2
42 printf '1\n2\n3\n4\nhello world\ngoodbye world\n7\n8\n9\n' > lines
44 printf '1\n2\n3\n4\nhello world\ngoodbye world\n7\n8\n9\n' > lines
43 hg ci -Amlines -d '2 0'
45 hg ci -Amlines -d '2 0'
44
46
45 echo % qnew 2
47 echo % qnew 2
46 hg qnew -mmqbase2 mqbase2
48 hg qnew -mmqbase2 mqbase2
47 printf '\n\n1\n2\n3\n4\nhello world\n goodbye world\n7\n8\n9\n' > lines
49 printf '\n\n1\n2\n3\n4\nhello world\n goodbye world\n7\n8\n9\n' > lines
48
50
49 echo % qdiff -U 1
51 echo % qdiff -U 1
50 hg qdiff --nodates -U 1
52 hg qdiff --nodates -U 1
51
53
52 echo % qdiff -b
54 echo % qdiff -b
53 hg qdiff --nodates -b
55 hg qdiff --nodates -b
54
56
55 echo % qdiff -U 1 -B
57 echo % qdiff -U 1 -B
56 hg qdiff --nodates -U 1 -B
58 hg qdiff --nodates -U 1 -B
57
59
58 echo % qdiff -w
60 echo % qdiff -w
59 hg qdiff --nodates -w
61 hg qdiff --nodates -w
60
62
61 echo % qdiff --reverse
63 echo % qdiff --reverse
62 hg qdiff --nodates --reverse
64 hg qdiff --nodates --reverse
65
66 echo % qdiff preserve existing git flag
67 hg qrefresh --git
68 echo a >> lines
69 hg qdiff
70
@@ -1,105 +1,124 b''
1 % init
1 % init
2 % commit
2 % commit
3 adding base
3 adding base
4 % qnew mqbase
4 % qnew mqbase
5 % qrefresh
5 % qrefresh
6 % qdiff
6 % qdiff
7 diff -r 67e992f2c4f3 base
7 diff -r 67e992f2c4f3 base
8 --- a/base
8 --- a/base
9 +++ b/base
9 +++ b/base
10 @@ -1,1 +1,1 @@
10 @@ -1,1 +1,1 @@
11 -base
11 -base
12 +patched
12 +patched
13 % qdiff dirname
13 % qdiff dirname
14 diff -r 67e992f2c4f3 base
14 diff -r 67e992f2c4f3 base
15 --- a/base
15 --- a/base
16 +++ b/base
16 +++ b/base
17 @@ -1,1 +1,1 @@
17 @@ -1,1 +1,1 @@
18 -base
18 -base
19 +patched
19 +patched
20 % qdiff filename
20 % qdiff filename
21 diff -r 67e992f2c4f3 base
21 diff -r 67e992f2c4f3 base
22 --- a/base
22 --- a/base
23 +++ b/base
23 +++ b/base
24 @@ -1,1 +1,1 @@
24 @@ -1,1 +1,1 @@
25 -base
25 -base
26 +patched
26 +patched
27 % revert
27 % revert
28 % qpop
28 % qpop
29 popping mqbase
29 popping mqbase
30 patch queue now empty
30 patch queue now empty
31 % qdelete mqbase
31 % qdelete mqbase
32 % commit 2
32 % commit 2
33 adding lines
33 adding lines
34 % qnew 2
34 % qnew 2
35 % qdiff -U 1
35 % qdiff -U 1
36 diff -r 35fb829491c1 lines
36 diff -r 35fb829491c1 lines
37 --- a/lines
37 --- a/lines
38 +++ b/lines
38 +++ b/lines
39 @@ -1,1 +1,3 @@
39 @@ -1,1 +1,3 @@
40 +
40 +
41 +
41 +
42 1
42 1
43 @@ -4,4 +6,4 @@
43 @@ -4,4 +6,4 @@
44 4
44 4
45 -hello world
45 -hello world
46 -goodbye world
46 -goodbye world
47 +hello world
47 +hello world
48 + goodbye world
48 + goodbye world
49 7
49 7
50 % qdiff -b
50 % qdiff -b
51 diff -r 35fb829491c1 lines
51 diff -r 35fb829491c1 lines
52 --- a/lines
52 --- a/lines
53 +++ b/lines
53 +++ b/lines
54 @@ -1,9 +1,11 @@
54 @@ -1,9 +1,11 @@
55 +
55 +
56 +
56 +
57 1
57 1
58 2
58 2
59 3
59 3
60 4
60 4
61 hello world
61 hello world
62 -goodbye world
62 -goodbye world
63 + goodbye world
63 + goodbye world
64 7
64 7
65 8
65 8
66 9
66 9
67 % qdiff -U 1 -B
67 % qdiff -U 1 -B
68 diff -r 35fb829491c1 lines
68 diff -r 35fb829491c1 lines
69 --- a/lines
69 --- a/lines
70 +++ b/lines
70 +++ b/lines
71 @@ -4,4 +6,4 @@
71 @@ -4,4 +6,4 @@
72 4
72 4
73 -hello world
73 -hello world
74 -goodbye world
74 -goodbye world
75 +hello world
75 +hello world
76 + goodbye world
76 + goodbye world
77 7
77 7
78 % qdiff -w
78 % qdiff -w
79 diff -r 35fb829491c1 lines
79 diff -r 35fb829491c1 lines
80 --- a/lines
80 --- a/lines
81 +++ b/lines
81 +++ b/lines
82 @@ -1,3 +1,5 @@
82 @@ -1,3 +1,5 @@
83 +
83 +
84 +
84 +
85 1
85 1
86 2
86 2
87 3
87 3
88 % qdiff --reverse
88 % qdiff --reverse
89 diff -r 35fb829491c1 lines
89 diff -r 35fb829491c1 lines
90 --- a/lines
90 --- a/lines
91 +++ b/lines
91 +++ b/lines
92 @@ -1,11 +1,9 @@
92 @@ -1,11 +1,9 @@
93 -
93 -
94 -
94 -
95 1
95 1
96 2
96 2
97 3
97 3
98 4
98 4
99 -hello world
99 -hello world
100 - goodbye world
100 - goodbye world
101 +hello world
101 +hello world
102 +goodbye world
102 +goodbye world
103 7
103 7
104 8
104 8
105 9
105 9
106 % qdiff preserve existing git flag
107 diff --git a/lines b/lines
108 --- a/lines
109 +++ b/lines
110 @@ -1,9 +1,12 @@
111 +
112 +
113 1
114 2
115 3
116 4
117 -hello world
118 -goodbye world
119 +hello world
120 + goodbye world
121 7
122 8
123 9
124 +a
General Comments 0
You need to be logged in to leave comments. Login now