##// END OF EJS Templates
mq: fix qrefresh -e with no patches applied
Alexis S. L. Carvalho -
r5334:448eb46d default
parent child Browse files
Show More
@@ -1,2225 +1,2228
1 # queue.py - patch queues for mercurial
1 # queue.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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial import commands, cmdutil, hg, patch, revlog, util
33 from mercurial import commands, cmdutil, hg, patch, revlog, util
34 from mercurial import repair
34 from mercurial import repair
35 import os, sys, re, errno
35 import os, sys, re, errno
36
36
37 commands.norepo += " qclone qversion"
37 commands.norepo += " qclone qversion"
38
38
39 # Patch names looks like unix-file names.
39 # Patch names looks like unix-file names.
40 # They must be joinable with queue directory and result in the patch path.
40 # They must be joinable with queue directory and result in the patch path.
41 normname = util.normpath
41 normname = util.normpath
42
42
43 class statusentry:
43 class statusentry:
44 def __init__(self, rev, name=None):
44 def __init__(self, rev, name=None):
45 if not name:
45 if not name:
46 fields = rev.split(':', 1)
46 fields = rev.split(':', 1)
47 if len(fields) == 2:
47 if len(fields) == 2:
48 self.rev, self.name = fields
48 self.rev, self.name = fields
49 else:
49 else:
50 self.rev, self.name = None, None
50 self.rev, self.name = None, None
51 else:
51 else:
52 self.rev, self.name = rev, name
52 self.rev, self.name = rev, name
53
53
54 def __str__(self):
54 def __str__(self):
55 return self.rev + ':' + self.name
55 return self.rev + ':' + self.name
56
56
57 class queue:
57 class queue:
58 def __init__(self, ui, path, patchdir=None):
58 def __init__(self, ui, path, patchdir=None):
59 self.basepath = path
59 self.basepath = path
60 self.path = patchdir or os.path.join(path, "patches")
60 self.path = patchdir or os.path.join(path, "patches")
61 self.opener = util.opener(self.path)
61 self.opener = util.opener(self.path)
62 self.ui = ui
62 self.ui = ui
63 self.applied = []
63 self.applied = []
64 self.full_series = []
64 self.full_series = []
65 self.applied_dirty = 0
65 self.applied_dirty = 0
66 self.series_dirty = 0
66 self.series_dirty = 0
67 self.series_path = "series"
67 self.series_path = "series"
68 self.status_path = "status"
68 self.status_path = "status"
69 self.guards_path = "guards"
69 self.guards_path = "guards"
70 self.active_guards = None
70 self.active_guards = None
71 self.guards_dirty = False
71 self.guards_dirty = False
72 self._diffopts = None
72 self._diffopts = None
73
73
74 if os.path.exists(self.join(self.series_path)):
74 if os.path.exists(self.join(self.series_path)):
75 self.full_series = self.opener(self.series_path).read().splitlines()
75 self.full_series = self.opener(self.series_path).read().splitlines()
76 self.parse_series()
76 self.parse_series()
77
77
78 if os.path.exists(self.join(self.status_path)):
78 if os.path.exists(self.join(self.status_path)):
79 lines = self.opener(self.status_path).read().splitlines()
79 lines = self.opener(self.status_path).read().splitlines()
80 self.applied = [statusentry(l) for l in lines]
80 self.applied = [statusentry(l) for l in lines]
81
81
82 def diffopts(self):
82 def diffopts(self):
83 if self._diffopts is None:
83 if self._diffopts is None:
84 self._diffopts = patch.diffopts(self.ui)
84 self._diffopts = patch.diffopts(self.ui)
85 return self._diffopts
85 return self._diffopts
86
86
87 def join(self, *p):
87 def join(self, *p):
88 return os.path.join(self.path, *p)
88 return os.path.join(self.path, *p)
89
89
90 def find_series(self, patch):
90 def find_series(self, patch):
91 pre = re.compile("(\s*)([^#]+)")
91 pre = re.compile("(\s*)([^#]+)")
92 index = 0
92 index = 0
93 for l in self.full_series:
93 for l in self.full_series:
94 m = pre.match(l)
94 m = pre.match(l)
95 if m:
95 if m:
96 s = m.group(2)
96 s = m.group(2)
97 s = s.rstrip()
97 s = s.rstrip()
98 if s == patch:
98 if s == patch:
99 return index
99 return index
100 index += 1
100 index += 1
101 return None
101 return None
102
102
103 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
103 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
104
104
105 def parse_series(self):
105 def parse_series(self):
106 self.series = []
106 self.series = []
107 self.series_guards = []
107 self.series_guards = []
108 for l in self.full_series:
108 for l in self.full_series:
109 h = l.find('#')
109 h = l.find('#')
110 if h == -1:
110 if h == -1:
111 patch = l
111 patch = l
112 comment = ''
112 comment = ''
113 elif h == 0:
113 elif h == 0:
114 continue
114 continue
115 else:
115 else:
116 patch = l[:h]
116 patch = l[:h]
117 comment = l[h:]
117 comment = l[h:]
118 patch = patch.strip()
118 patch = patch.strip()
119 if patch:
119 if patch:
120 if patch in self.series:
120 if patch in self.series:
121 raise util.Abort(_('%s appears more than once in %s') %
121 raise util.Abort(_('%s appears more than once in %s') %
122 (patch, self.join(self.series_path)))
122 (patch, self.join(self.series_path)))
123 self.series.append(patch)
123 self.series.append(patch)
124 self.series_guards.append(self.guard_re.findall(comment))
124 self.series_guards.append(self.guard_re.findall(comment))
125
125
126 def check_guard(self, guard):
126 def check_guard(self, guard):
127 bad_chars = '# \t\r\n\f'
127 bad_chars = '# \t\r\n\f'
128 first = guard[0]
128 first = guard[0]
129 for c in '-+':
129 for c in '-+':
130 if first == c:
130 if first == c:
131 return (_('guard %r starts with invalid character: %r') %
131 return (_('guard %r starts with invalid character: %r') %
132 (guard, c))
132 (guard, c))
133 for c in bad_chars:
133 for c in bad_chars:
134 if c in guard:
134 if c in guard:
135 return _('invalid character in guard %r: %r') % (guard, c)
135 return _('invalid character in guard %r: %r') % (guard, c)
136
136
137 def set_active(self, guards):
137 def set_active(self, guards):
138 for guard in guards:
138 for guard in guards:
139 bad = self.check_guard(guard)
139 bad = self.check_guard(guard)
140 if bad:
140 if bad:
141 raise util.Abort(bad)
141 raise util.Abort(bad)
142 guards = dict.fromkeys(guards).keys()
142 guards = dict.fromkeys(guards).keys()
143 guards.sort()
143 guards.sort()
144 self.ui.debug('active guards: %s\n' % ' '.join(guards))
144 self.ui.debug('active guards: %s\n' % ' '.join(guards))
145 self.active_guards = guards
145 self.active_guards = guards
146 self.guards_dirty = True
146 self.guards_dirty = True
147
147
148 def active(self):
148 def active(self):
149 if self.active_guards is None:
149 if self.active_guards is None:
150 self.active_guards = []
150 self.active_guards = []
151 try:
151 try:
152 guards = self.opener(self.guards_path).read().split()
152 guards = self.opener(self.guards_path).read().split()
153 except IOError, err:
153 except IOError, err:
154 if err.errno != errno.ENOENT: raise
154 if err.errno != errno.ENOENT: raise
155 guards = []
155 guards = []
156 for i, guard in enumerate(guards):
156 for i, guard in enumerate(guards):
157 bad = self.check_guard(guard)
157 bad = self.check_guard(guard)
158 if bad:
158 if bad:
159 self.ui.warn('%s:%d: %s\n' %
159 self.ui.warn('%s:%d: %s\n' %
160 (self.join(self.guards_path), i + 1, bad))
160 (self.join(self.guards_path), i + 1, bad))
161 else:
161 else:
162 self.active_guards.append(guard)
162 self.active_guards.append(guard)
163 return self.active_guards
163 return self.active_guards
164
164
165 def set_guards(self, idx, guards):
165 def set_guards(self, idx, guards):
166 for g in guards:
166 for g in guards:
167 if len(g) < 2:
167 if len(g) < 2:
168 raise util.Abort(_('guard %r too short') % g)
168 raise util.Abort(_('guard %r too short') % g)
169 if g[0] not in '-+':
169 if g[0] not in '-+':
170 raise util.Abort(_('guard %r starts with invalid char') % g)
170 raise util.Abort(_('guard %r starts with invalid char') % g)
171 bad = self.check_guard(g[1:])
171 bad = self.check_guard(g[1:])
172 if bad:
172 if bad:
173 raise util.Abort(bad)
173 raise util.Abort(bad)
174 drop = self.guard_re.sub('', self.full_series[idx])
174 drop = self.guard_re.sub('', self.full_series[idx])
175 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
175 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
176 self.parse_series()
176 self.parse_series()
177 self.series_dirty = True
177 self.series_dirty = True
178
178
179 def pushable(self, idx):
179 def pushable(self, idx):
180 if isinstance(idx, str):
180 if isinstance(idx, str):
181 idx = self.series.index(idx)
181 idx = self.series.index(idx)
182 patchguards = self.series_guards[idx]
182 patchguards = self.series_guards[idx]
183 if not patchguards:
183 if not patchguards:
184 return True, None
184 return True, None
185 default = False
185 default = False
186 guards = self.active()
186 guards = self.active()
187 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
187 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
188 if exactneg:
188 if exactneg:
189 return False, exactneg[0]
189 return False, exactneg[0]
190 pos = [g for g in patchguards if g[0] == '+']
190 pos = [g for g in patchguards if g[0] == '+']
191 exactpos = [g for g in pos if g[1:] in guards]
191 exactpos = [g for g in pos if g[1:] in guards]
192 if pos:
192 if pos:
193 if exactpos:
193 if exactpos:
194 return True, exactpos[0]
194 return True, exactpos[0]
195 return False, pos
195 return False, pos
196 return True, ''
196 return True, ''
197
197
198 def explain_pushable(self, idx, all_patches=False):
198 def explain_pushable(self, idx, all_patches=False):
199 write = all_patches and self.ui.write or self.ui.warn
199 write = all_patches and self.ui.write or self.ui.warn
200 if all_patches or self.ui.verbose:
200 if all_patches or self.ui.verbose:
201 if isinstance(idx, str):
201 if isinstance(idx, str):
202 idx = self.series.index(idx)
202 idx = self.series.index(idx)
203 pushable, why = self.pushable(idx)
203 pushable, why = self.pushable(idx)
204 if all_patches and pushable:
204 if all_patches and pushable:
205 if why is None:
205 if why is None:
206 write(_('allowing %s - no guards in effect\n') %
206 write(_('allowing %s - no guards in effect\n') %
207 self.series[idx])
207 self.series[idx])
208 else:
208 else:
209 if not why:
209 if not why:
210 write(_('allowing %s - no matching negative guards\n') %
210 write(_('allowing %s - no matching negative guards\n') %
211 self.series[idx])
211 self.series[idx])
212 else:
212 else:
213 write(_('allowing %s - guarded by %r\n') %
213 write(_('allowing %s - guarded by %r\n') %
214 (self.series[idx], why))
214 (self.series[idx], why))
215 if not pushable:
215 if not pushable:
216 if why:
216 if why:
217 write(_('skipping %s - guarded by %r\n') %
217 write(_('skipping %s - guarded by %r\n') %
218 (self.series[idx], why))
218 (self.series[idx], why))
219 else:
219 else:
220 write(_('skipping %s - no matching guards\n') %
220 write(_('skipping %s - no matching guards\n') %
221 self.series[idx])
221 self.series[idx])
222
222
223 def save_dirty(self):
223 def save_dirty(self):
224 def write_list(items, path):
224 def write_list(items, path):
225 fp = self.opener(path, 'w')
225 fp = self.opener(path, 'w')
226 for i in items:
226 for i in items:
227 print >> fp, i
227 print >> fp, i
228 fp.close()
228 fp.close()
229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
230 if self.series_dirty: write_list(self.full_series, self.series_path)
230 if self.series_dirty: write_list(self.full_series, self.series_path)
231 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
231 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
232
232
233 def readheaders(self, patch):
233 def readheaders(self, patch):
234 def eatdiff(lines):
234 def eatdiff(lines):
235 while lines:
235 while lines:
236 l = lines[-1]
236 l = lines[-1]
237 if (l.startswith("diff -") or
237 if (l.startswith("diff -") or
238 l.startswith("Index:") or
238 l.startswith("Index:") or
239 l.startswith("===========")):
239 l.startswith("===========")):
240 del lines[-1]
240 del lines[-1]
241 else:
241 else:
242 break
242 break
243 def eatempty(lines):
243 def eatempty(lines):
244 while lines:
244 while lines:
245 l = lines[-1]
245 l = lines[-1]
246 if re.match('\s*$', l):
246 if re.match('\s*$', l):
247 del lines[-1]
247 del lines[-1]
248 else:
248 else:
249 break
249 break
250
250
251 pf = self.join(patch)
251 pf = self.join(patch)
252 message = []
252 message = []
253 comments = []
253 comments = []
254 user = None
254 user = None
255 date = None
255 date = None
256 format = None
256 format = None
257 subject = None
257 subject = None
258 diffstart = 0
258 diffstart = 0
259
259
260 for line in file(pf):
260 for line in file(pf):
261 line = line.rstrip()
261 line = line.rstrip()
262 if line.startswith('diff --git'):
262 if line.startswith('diff --git'):
263 diffstart = 2
263 diffstart = 2
264 break
264 break
265 if diffstart:
265 if diffstart:
266 if line.startswith('+++ '):
266 if line.startswith('+++ '):
267 diffstart = 2
267 diffstart = 2
268 break
268 break
269 if line.startswith("--- "):
269 if line.startswith("--- "):
270 diffstart = 1
270 diffstart = 1
271 continue
271 continue
272 elif format == "hgpatch":
272 elif format == "hgpatch":
273 # parse values when importing the result of an hg export
273 # parse values when importing the result of an hg export
274 if line.startswith("# User "):
274 if line.startswith("# User "):
275 user = line[7:]
275 user = line[7:]
276 elif line.startswith("# Date "):
276 elif line.startswith("# Date "):
277 date = line[7:]
277 date = line[7:]
278 elif not line.startswith("# ") and line:
278 elif not line.startswith("# ") and line:
279 message.append(line)
279 message.append(line)
280 format = None
280 format = None
281 elif line == '# HG changeset patch':
281 elif line == '# HG changeset patch':
282 format = "hgpatch"
282 format = "hgpatch"
283 elif (format != "tagdone" and (line.startswith("Subject: ") or
283 elif (format != "tagdone" and (line.startswith("Subject: ") or
284 line.startswith("subject: "))):
284 line.startswith("subject: "))):
285 subject = line[9:]
285 subject = line[9:]
286 format = "tag"
286 format = "tag"
287 elif (format != "tagdone" and (line.startswith("From: ") or
287 elif (format != "tagdone" and (line.startswith("From: ") or
288 line.startswith("from: "))):
288 line.startswith("from: "))):
289 user = line[6:]
289 user = line[6:]
290 format = "tag"
290 format = "tag"
291 elif format == "tag" and line == "":
291 elif format == "tag" and line == "":
292 # when looking for tags (subject: from: etc) they
292 # when looking for tags (subject: from: etc) they
293 # end once you find a blank line in the source
293 # end once you find a blank line in the source
294 format = "tagdone"
294 format = "tagdone"
295 elif message or line:
295 elif message or line:
296 message.append(line)
296 message.append(line)
297 comments.append(line)
297 comments.append(line)
298
298
299 eatdiff(message)
299 eatdiff(message)
300 eatdiff(comments)
300 eatdiff(comments)
301 eatempty(message)
301 eatempty(message)
302 eatempty(comments)
302 eatempty(comments)
303
303
304 # make sure message isn't empty
304 # make sure message isn't empty
305 if format and format.startswith("tag") and subject:
305 if format and format.startswith("tag") and subject:
306 message.insert(0, "")
306 message.insert(0, "")
307 message.insert(0, subject)
307 message.insert(0, subject)
308 return (message, comments, user, date, diffstart > 1)
308 return (message, comments, user, date, diffstart > 1)
309
309
310 def removeundo(self, repo):
310 def removeundo(self, repo):
311 undo = repo.sjoin('undo')
311 undo = repo.sjoin('undo')
312 if not os.path.exists(undo):
312 if not os.path.exists(undo):
313 return
313 return
314 try:
314 try:
315 os.unlink(undo)
315 os.unlink(undo)
316 except OSError, inst:
316 except OSError, inst:
317 self.ui.warn('error removing undo: %s\n' % str(inst))
317 self.ui.warn('error removing undo: %s\n' % str(inst))
318
318
319 def printdiff(self, repo, node1, node2=None, files=None,
319 def printdiff(self, repo, node1, node2=None, files=None,
320 fp=None, changes=None, opts={}):
320 fp=None, changes=None, opts={}):
321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
322
322
323 patch.diff(repo, node1, node2, fns, match=matchfn,
323 patch.diff(repo, node1, node2, fns, match=matchfn,
324 fp=fp, changes=changes, opts=self.diffopts())
324 fp=fp, changes=changes, opts=self.diffopts())
325
325
326 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
326 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
327 # first try just applying the patch
327 # first try just applying the patch
328 (err, n) = self.apply(repo, [ patch ], update_status=False,
328 (err, n) = self.apply(repo, [ patch ], update_status=False,
329 strict=True, merge=rev, wlock=wlock)
329 strict=True, merge=rev, wlock=wlock)
330
330
331 if err == 0:
331 if err == 0:
332 return (err, n)
332 return (err, n)
333
333
334 if n is None:
334 if n is None:
335 raise util.Abort(_("apply failed for patch %s") % patch)
335 raise util.Abort(_("apply failed for patch %s") % patch)
336
336
337 self.ui.warn("patch didn't work out, merging %s\n" % patch)
337 self.ui.warn("patch didn't work out, merging %s\n" % patch)
338
338
339 # apply failed, strip away that rev and merge.
339 # apply failed, strip away that rev and merge.
340 hg.clean(repo, head, wlock=wlock)
340 hg.clean(repo, head, wlock=wlock)
341 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
341 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
342
342
343 ctx = repo.changectx(rev)
343 ctx = repo.changectx(rev)
344 ret = hg.merge(repo, rev, wlock=wlock)
344 ret = hg.merge(repo, rev, wlock=wlock)
345 if ret:
345 if ret:
346 raise util.Abort(_("update returned %d") % ret)
346 raise util.Abort(_("update returned %d") % ret)
347 n = repo.commit(None, ctx.description(), ctx.user(),
347 n = repo.commit(None, ctx.description(), ctx.user(),
348 force=1, wlock=wlock)
348 force=1, wlock=wlock)
349 if n == None:
349 if n == None:
350 raise util.Abort(_("repo commit failed"))
350 raise util.Abort(_("repo commit failed"))
351 try:
351 try:
352 message, comments, user, date, patchfound = mergeq.readheaders(patch)
352 message, comments, user, date, patchfound = mergeq.readheaders(patch)
353 except:
353 except:
354 raise util.Abort(_("unable to read %s") % patch)
354 raise util.Abort(_("unable to read %s") % patch)
355
355
356 patchf = self.opener(patch, "w")
356 patchf = self.opener(patch, "w")
357 if comments:
357 if comments:
358 comments = "\n".join(comments) + '\n\n'
358 comments = "\n".join(comments) + '\n\n'
359 patchf.write(comments)
359 patchf.write(comments)
360 self.printdiff(repo, head, n, fp=patchf)
360 self.printdiff(repo, head, n, fp=patchf)
361 patchf.close()
361 patchf.close()
362 self.removeundo(repo)
362 self.removeundo(repo)
363 return (0, n)
363 return (0, n)
364
364
365 def qparents(self, repo, rev=None):
365 def qparents(self, repo, rev=None):
366 if rev is None:
366 if rev is None:
367 (p1, p2) = repo.dirstate.parents()
367 (p1, p2) = repo.dirstate.parents()
368 if p2 == revlog.nullid:
368 if p2 == revlog.nullid:
369 return p1
369 return p1
370 if len(self.applied) == 0:
370 if len(self.applied) == 0:
371 return None
371 return None
372 return revlog.bin(self.applied[-1].rev)
372 return revlog.bin(self.applied[-1].rev)
373 pp = repo.changelog.parents(rev)
373 pp = repo.changelog.parents(rev)
374 if pp[1] != revlog.nullid:
374 if pp[1] != revlog.nullid:
375 arevs = [ x.rev for x in self.applied ]
375 arevs = [ x.rev for x in self.applied ]
376 p0 = revlog.hex(pp[0])
376 p0 = revlog.hex(pp[0])
377 p1 = revlog.hex(pp[1])
377 p1 = revlog.hex(pp[1])
378 if p0 in arevs:
378 if p0 in arevs:
379 return pp[0]
379 return pp[0]
380 if p1 in arevs:
380 if p1 in arevs:
381 return pp[1]
381 return pp[1]
382 return pp[0]
382 return pp[0]
383
383
384 def mergepatch(self, repo, mergeq, series, wlock):
384 def mergepatch(self, repo, mergeq, series, wlock):
385 if len(self.applied) == 0:
385 if len(self.applied) == 0:
386 # each of the patches merged in will have two parents. This
386 # each of the patches merged in will have two parents. This
387 # can confuse the qrefresh, qdiff, and strip code because it
387 # can confuse the qrefresh, qdiff, and strip code because it
388 # needs to know which parent is actually in the patch queue.
388 # needs to know which parent is actually in the patch queue.
389 # so, we insert a merge marker with only one parent. This way
389 # so, we insert a merge marker with only one parent. This way
390 # the first patch in the queue is never a merge patch
390 # the first patch in the queue is never a merge patch
391 #
391 #
392 pname = ".hg.patches.merge.marker"
392 pname = ".hg.patches.merge.marker"
393 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
393 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
394 wlock=wlock)
394 wlock=wlock)
395 self.removeundo(repo)
395 self.removeundo(repo)
396 self.applied.append(statusentry(revlog.hex(n), pname))
396 self.applied.append(statusentry(revlog.hex(n), pname))
397 self.applied_dirty = 1
397 self.applied_dirty = 1
398
398
399 head = self.qparents(repo)
399 head = self.qparents(repo)
400
400
401 for patch in series:
401 for patch in series:
402 patch = mergeq.lookup(patch, strict=True)
402 patch = mergeq.lookup(patch, strict=True)
403 if not patch:
403 if not patch:
404 self.ui.warn("patch %s does not exist\n" % patch)
404 self.ui.warn("patch %s does not exist\n" % patch)
405 return (1, None)
405 return (1, None)
406 pushable, reason = self.pushable(patch)
406 pushable, reason = self.pushable(patch)
407 if not pushable:
407 if not pushable:
408 self.explain_pushable(patch, all_patches=True)
408 self.explain_pushable(patch, all_patches=True)
409 continue
409 continue
410 info = mergeq.isapplied(patch)
410 info = mergeq.isapplied(patch)
411 if not info:
411 if not info:
412 self.ui.warn("patch %s is not applied\n" % patch)
412 self.ui.warn("patch %s is not applied\n" % patch)
413 return (1, None)
413 return (1, None)
414 rev = revlog.bin(info[1])
414 rev = revlog.bin(info[1])
415 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
415 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
416 if head:
416 if head:
417 self.applied.append(statusentry(revlog.hex(head), patch))
417 self.applied.append(statusentry(revlog.hex(head), patch))
418 self.applied_dirty = 1
418 self.applied_dirty = 1
419 if err:
419 if err:
420 return (err, head)
420 return (err, head)
421 self.save_dirty()
421 self.save_dirty()
422 return (0, head)
422 return (0, head)
423
423
424 def patch(self, repo, patchfile):
424 def patch(self, repo, patchfile):
425 '''Apply patchfile to the working directory.
425 '''Apply patchfile to the working directory.
426 patchfile: file name of patch'''
426 patchfile: file name of patch'''
427 files = {}
427 files = {}
428 try:
428 try:
429 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
429 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
430 files=files)
430 files=files)
431 except Exception, inst:
431 except Exception, inst:
432 self.ui.note(str(inst) + '\n')
432 self.ui.note(str(inst) + '\n')
433 if not self.ui.verbose:
433 if not self.ui.verbose:
434 self.ui.warn("patch failed, unable to continue (try -v)\n")
434 self.ui.warn("patch failed, unable to continue (try -v)\n")
435 return (False, files, False)
435 return (False, files, False)
436
436
437 return (True, files, fuzz)
437 return (True, files, fuzz)
438
438
439 def apply(self, repo, series, list=False, update_status=True,
439 def apply(self, repo, series, list=False, update_status=True,
440 strict=False, patchdir=None, merge=None, wlock=None,
440 strict=False, patchdir=None, merge=None, wlock=None,
441 all_files={}):
441 all_files={}):
442 if not wlock:
442 if not wlock:
443 wlock = repo.wlock()
443 wlock = repo.wlock()
444 lock = repo.lock()
444 lock = repo.lock()
445 tr = repo.transaction()
445 tr = repo.transaction()
446 try:
446 try:
447 ret = self._apply(tr, repo, series, list, update_status,
447 ret = self._apply(tr, repo, series, list, update_status,
448 strict, patchdir, merge, wlock,
448 strict, patchdir, merge, wlock,
449 lock=lock, all_files=all_files)
449 lock=lock, all_files=all_files)
450 tr.close()
450 tr.close()
451 self.save_dirty()
451 self.save_dirty()
452 return ret
452 return ret
453 except:
453 except:
454 try:
454 try:
455 tr.abort()
455 tr.abort()
456 finally:
456 finally:
457 repo.invalidate()
457 repo.invalidate()
458 repo.dirstate.invalidate()
458 repo.dirstate.invalidate()
459 raise
459 raise
460
460
461 def _apply(self, tr, repo, series, list=False, update_status=True,
461 def _apply(self, tr, repo, series, list=False, update_status=True,
462 strict=False, patchdir=None, merge=None, wlock=None,
462 strict=False, patchdir=None, merge=None, wlock=None,
463 lock=None, all_files={}):
463 lock=None, all_files={}):
464 # TODO unify with commands.py
464 # TODO unify with commands.py
465 if not patchdir:
465 if not patchdir:
466 patchdir = self.path
466 patchdir = self.path
467 err = 0
467 err = 0
468 n = None
468 n = None
469 for patchname in series:
469 for patchname in series:
470 pushable, reason = self.pushable(patchname)
470 pushable, reason = self.pushable(patchname)
471 if not pushable:
471 if not pushable:
472 self.explain_pushable(patchname, all_patches=True)
472 self.explain_pushable(patchname, all_patches=True)
473 continue
473 continue
474 self.ui.warn("applying %s\n" % patchname)
474 self.ui.warn("applying %s\n" % patchname)
475 pf = os.path.join(patchdir, patchname)
475 pf = os.path.join(patchdir, patchname)
476
476
477 try:
477 try:
478 message, comments, user, date, patchfound = self.readheaders(patchname)
478 message, comments, user, date, patchfound = self.readheaders(patchname)
479 except:
479 except:
480 self.ui.warn("Unable to read %s\n" % patchname)
480 self.ui.warn("Unable to read %s\n" % patchname)
481 err = 1
481 err = 1
482 break
482 break
483
483
484 if not message:
484 if not message:
485 message = "imported patch %s\n" % patchname
485 message = "imported patch %s\n" % patchname
486 else:
486 else:
487 if list:
487 if list:
488 message.append("\nimported patch %s" % patchname)
488 message.append("\nimported patch %s" % patchname)
489 message = '\n'.join(message)
489 message = '\n'.join(message)
490
490
491 (patcherr, files, fuzz) = self.patch(repo, pf)
491 (patcherr, files, fuzz) = self.patch(repo, pf)
492 all_files.update(files)
492 all_files.update(files)
493 patcherr = not patcherr
493 patcherr = not patcherr
494
494
495 if merge and files:
495 if merge and files:
496 # Mark as removed/merged and update dirstate parent info
496 # Mark as removed/merged and update dirstate parent info
497 removed = []
497 removed = []
498 merged = []
498 merged = []
499 for f in files:
499 for f in files:
500 if os.path.exists(repo.dirstate.wjoin(f)):
500 if os.path.exists(repo.dirstate.wjoin(f)):
501 merged.append(f)
501 merged.append(f)
502 else:
502 else:
503 removed.append(f)
503 removed.append(f)
504 repo.dirstate.update(repo.dirstate.filterfiles(removed), 'r')
504 repo.dirstate.update(repo.dirstate.filterfiles(removed), 'r')
505 repo.dirstate.update(repo.dirstate.filterfiles(merged), 'm')
505 repo.dirstate.update(repo.dirstate.filterfiles(merged), 'm')
506 p1, p2 = repo.dirstate.parents()
506 p1, p2 = repo.dirstate.parents()
507 repo.dirstate.setparents(p1, merge)
507 repo.dirstate.setparents(p1, merge)
508 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
508 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
509 n = repo.commit(files, message, user, date, force=1, lock=lock,
509 n = repo.commit(files, message, user, date, force=1, lock=lock,
510 wlock=wlock)
510 wlock=wlock)
511
511
512 if n == None:
512 if n == None:
513 raise util.Abort(_("repo commit failed"))
513 raise util.Abort(_("repo commit failed"))
514
514
515 if update_status:
515 if update_status:
516 self.applied.append(statusentry(revlog.hex(n), patchname))
516 self.applied.append(statusentry(revlog.hex(n), patchname))
517
517
518 if patcherr:
518 if patcherr:
519 if not patchfound:
519 if not patchfound:
520 self.ui.warn("patch %s is empty\n" % patchname)
520 self.ui.warn("patch %s is empty\n" % patchname)
521 err = 0
521 err = 0
522 else:
522 else:
523 self.ui.warn("patch failed, rejects left in working dir\n")
523 self.ui.warn("patch failed, rejects left in working dir\n")
524 err = 1
524 err = 1
525 break
525 break
526
526
527 if fuzz and strict:
527 if fuzz and strict:
528 self.ui.warn("fuzz found when applying patch, stopping\n")
528 self.ui.warn("fuzz found when applying patch, stopping\n")
529 err = 1
529 err = 1
530 break
530 break
531 self.removeundo(repo)
531 self.removeundo(repo)
532 return (err, n)
532 return (err, n)
533
533
534 def delete(self, repo, patches, opts):
534 def delete(self, repo, patches, opts):
535 if not patches and not opts.get('rev'):
535 if not patches and not opts.get('rev'):
536 raise util.Abort(_('qdelete requires at least one revision or '
536 raise util.Abort(_('qdelete requires at least one revision or '
537 'patch name'))
537 'patch name'))
538
538
539 realpatches = []
539 realpatches = []
540 for patch in patches:
540 for patch in patches:
541 patch = self.lookup(patch, strict=True)
541 patch = self.lookup(patch, strict=True)
542 info = self.isapplied(patch)
542 info = self.isapplied(patch)
543 if info:
543 if info:
544 raise util.Abort(_("cannot delete applied patch %s") % patch)
544 raise util.Abort(_("cannot delete applied patch %s") % patch)
545 if patch not in self.series:
545 if patch not in self.series:
546 raise util.Abort(_("patch %s not in series file") % patch)
546 raise util.Abort(_("patch %s not in series file") % patch)
547 realpatches.append(patch)
547 realpatches.append(patch)
548
548
549 appliedbase = 0
549 appliedbase = 0
550 if opts.get('rev'):
550 if opts.get('rev'):
551 if not self.applied:
551 if not self.applied:
552 raise util.Abort(_('no patches applied'))
552 raise util.Abort(_('no patches applied'))
553 revs = cmdutil.revrange(repo, opts['rev'])
553 revs = cmdutil.revrange(repo, opts['rev'])
554 if len(revs) > 1 and revs[0] > revs[1]:
554 if len(revs) > 1 and revs[0] > revs[1]:
555 revs.reverse()
555 revs.reverse()
556 for rev in revs:
556 for rev in revs:
557 if appliedbase >= len(self.applied):
557 if appliedbase >= len(self.applied):
558 raise util.Abort(_("revision %d is not managed") % rev)
558 raise util.Abort(_("revision %d is not managed") % rev)
559
559
560 base = revlog.bin(self.applied[appliedbase].rev)
560 base = revlog.bin(self.applied[appliedbase].rev)
561 node = repo.changelog.node(rev)
561 node = repo.changelog.node(rev)
562 if node != base:
562 if node != base:
563 raise util.Abort(_("cannot delete revision %d above "
563 raise util.Abort(_("cannot delete revision %d above "
564 "applied patches") % rev)
564 "applied patches") % rev)
565 realpatches.append(self.applied[appliedbase].name)
565 realpatches.append(self.applied[appliedbase].name)
566 appliedbase += 1
566 appliedbase += 1
567
567
568 if not opts.get('keep'):
568 if not opts.get('keep'):
569 r = self.qrepo()
569 r = self.qrepo()
570 if r:
570 if r:
571 r.remove(realpatches, True)
571 r.remove(realpatches, True)
572 else:
572 else:
573 for p in realpatches:
573 for p in realpatches:
574 os.unlink(self.join(p))
574 os.unlink(self.join(p))
575
575
576 if appliedbase:
576 if appliedbase:
577 del self.applied[:appliedbase]
577 del self.applied[:appliedbase]
578 self.applied_dirty = 1
578 self.applied_dirty = 1
579 indices = [self.find_series(p) for p in realpatches]
579 indices = [self.find_series(p) for p in realpatches]
580 indices.sort()
580 indices.sort()
581 for i in indices[-1::-1]:
581 for i in indices[-1::-1]:
582 del self.full_series[i]
582 del self.full_series[i]
583 self.parse_series()
583 self.parse_series()
584 self.series_dirty = 1
584 self.series_dirty = 1
585
585
586 def check_toppatch(self, repo):
586 def check_toppatch(self, repo):
587 if len(self.applied) > 0:
587 if len(self.applied) > 0:
588 top = revlog.bin(self.applied[-1].rev)
588 top = revlog.bin(self.applied[-1].rev)
589 pp = repo.dirstate.parents()
589 pp = repo.dirstate.parents()
590 if top not in pp:
590 if top not in pp:
591 raise util.Abort(_("queue top not at same revision as working directory"))
591 raise util.Abort(_("queue top not at same revision as working directory"))
592 return top
592 return top
593 return None
593 return None
594 def check_localchanges(self, repo, force=False, refresh=True):
594 def check_localchanges(self, repo, force=False, refresh=True):
595 m, a, r, d = repo.status()[:4]
595 m, a, r, d = repo.status()[:4]
596 if m or a or r or d:
596 if m or a or r or d:
597 if not force:
597 if not force:
598 if refresh:
598 if refresh:
599 raise util.Abort(_("local changes found, refresh first"))
599 raise util.Abort(_("local changes found, refresh first"))
600 else:
600 else:
601 raise util.Abort(_("local changes found"))
601 raise util.Abort(_("local changes found"))
602 return m, a, r, d
602 return m, a, r, d
603
603
604 def new(self, repo, patch, *pats, **opts):
604 def new(self, repo, patch, *pats, **opts):
605 msg = opts.get('msg')
605 msg = opts.get('msg')
606 force = opts.get('force')
606 force = opts.get('force')
607 if os.path.exists(self.join(patch)):
607 if os.path.exists(self.join(patch)):
608 raise util.Abort(_('patch "%s" already exists') % patch)
608 raise util.Abort(_('patch "%s" already exists') % patch)
609 if opts.get('include') or opts.get('exclude') or pats:
609 if opts.get('include') or opts.get('exclude') or pats:
610 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
610 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
611 m, a, r, d = repo.status(files=fns, match=match)[:4]
611 m, a, r, d = repo.status(files=fns, match=match)[:4]
612 else:
612 else:
613 m, a, r, d = self.check_localchanges(repo, force)
613 m, a, r, d = self.check_localchanges(repo, force)
614 commitfiles = m + a + r
614 commitfiles = m + a + r
615 self.check_toppatch(repo)
615 self.check_toppatch(repo)
616 wlock = repo.wlock()
616 wlock = repo.wlock()
617 insert = self.full_series_end()
617 insert = self.full_series_end()
618 if msg:
618 if msg:
619 n = repo.commit(commitfiles, msg, force=True, wlock=wlock)
619 n = repo.commit(commitfiles, msg, force=True, wlock=wlock)
620 else:
620 else:
621 n = repo.commit(commitfiles,
621 n = repo.commit(commitfiles,
622 "[mq]: %s" % patch, force=True, wlock=wlock)
622 "[mq]: %s" % patch, force=True, wlock=wlock)
623 if n == None:
623 if n == None:
624 raise util.Abort(_("repo commit failed"))
624 raise util.Abort(_("repo commit failed"))
625 self.full_series[insert:insert] = [patch]
625 self.full_series[insert:insert] = [patch]
626 self.applied.append(statusentry(revlog.hex(n), patch))
626 self.applied.append(statusentry(revlog.hex(n), patch))
627 self.parse_series()
627 self.parse_series()
628 self.series_dirty = 1
628 self.series_dirty = 1
629 self.applied_dirty = 1
629 self.applied_dirty = 1
630 p = self.opener(patch, "w")
630 p = self.opener(patch, "w")
631 if msg:
631 if msg:
632 msg = msg + "\n"
632 msg = msg + "\n"
633 p.write(msg)
633 p.write(msg)
634 p.close()
634 p.close()
635 wlock = None
635 wlock = None
636 r = self.qrepo()
636 r = self.qrepo()
637 if r: r.add([patch])
637 if r: r.add([patch])
638 if commitfiles:
638 if commitfiles:
639 self.refresh(repo, short=True)
639 self.refresh(repo, short=True)
640 self.removeundo(repo)
640 self.removeundo(repo)
641
641
642 def strip(self, repo, rev, update=True, backup="all", wlock=None):
642 def strip(self, repo, rev, update=True, backup="all", wlock=None):
643 if not wlock:
643 if not wlock:
644 wlock = repo.wlock()
644 wlock = repo.wlock()
645 lock = repo.lock()
645 lock = repo.lock()
646
646
647 if update:
647 if update:
648 self.check_localchanges(repo, refresh=False)
648 self.check_localchanges(repo, refresh=False)
649 urev = self.qparents(repo, rev)
649 urev = self.qparents(repo, rev)
650 hg.clean(repo, urev, wlock=wlock)
650 hg.clean(repo, urev, wlock=wlock)
651 repo.dirstate.write()
651 repo.dirstate.write()
652
652
653 self.removeundo(repo)
653 self.removeundo(repo)
654 repair.strip(self.ui, repo, rev, backup)
654 repair.strip(self.ui, repo, rev, backup)
655
655
656 def isapplied(self, patch):
656 def isapplied(self, patch):
657 """returns (index, rev, patch)"""
657 """returns (index, rev, patch)"""
658 for i in xrange(len(self.applied)):
658 for i in xrange(len(self.applied)):
659 a = self.applied[i]
659 a = self.applied[i]
660 if a.name == patch:
660 if a.name == patch:
661 return (i, a.rev, a.name)
661 return (i, a.rev, a.name)
662 return None
662 return None
663
663
664 # if the exact patch name does not exist, we try a few
664 # if the exact patch name does not exist, we try a few
665 # variations. If strict is passed, we try only #1
665 # variations. If strict is passed, we try only #1
666 #
666 #
667 # 1) a number to indicate an offset in the series file
667 # 1) a number to indicate an offset in the series file
668 # 2) a unique substring of the patch name was given
668 # 2) a unique substring of the patch name was given
669 # 3) patchname[-+]num to indicate an offset in the series file
669 # 3) patchname[-+]num to indicate an offset in the series file
670 def lookup(self, patch, strict=False):
670 def lookup(self, patch, strict=False):
671 patch = patch and str(patch)
671 patch = patch and str(patch)
672
672
673 def partial_name(s):
673 def partial_name(s):
674 if s in self.series:
674 if s in self.series:
675 return s
675 return s
676 matches = [x for x in self.series if s in x]
676 matches = [x for x in self.series if s in x]
677 if len(matches) > 1:
677 if len(matches) > 1:
678 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
678 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
679 for m in matches:
679 for m in matches:
680 self.ui.warn(' %s\n' % m)
680 self.ui.warn(' %s\n' % m)
681 return None
681 return None
682 if matches:
682 if matches:
683 return matches[0]
683 return matches[0]
684 if len(self.series) > 0 and len(self.applied) > 0:
684 if len(self.series) > 0 and len(self.applied) > 0:
685 if s == 'qtip':
685 if s == 'qtip':
686 return self.series[self.series_end(True)-1]
686 return self.series[self.series_end(True)-1]
687 if s == 'qbase':
687 if s == 'qbase':
688 return self.series[0]
688 return self.series[0]
689 return None
689 return None
690 if patch == None:
690 if patch == None:
691 return None
691 return None
692
692
693 # we don't want to return a partial match until we make
693 # we don't want to return a partial match until we make
694 # sure the file name passed in does not exist (checked below)
694 # sure the file name passed in does not exist (checked below)
695 res = partial_name(patch)
695 res = partial_name(patch)
696 if res and res == patch:
696 if res and res == patch:
697 return res
697 return res
698
698
699 if not os.path.isfile(self.join(patch)):
699 if not os.path.isfile(self.join(patch)):
700 try:
700 try:
701 sno = int(patch)
701 sno = int(patch)
702 except(ValueError, OverflowError):
702 except(ValueError, OverflowError):
703 pass
703 pass
704 else:
704 else:
705 if sno < len(self.series):
705 if sno < len(self.series):
706 return self.series[sno]
706 return self.series[sno]
707 if not strict:
707 if not strict:
708 # return any partial match made above
708 # return any partial match made above
709 if res:
709 if res:
710 return res
710 return res
711 minus = patch.rfind('-')
711 minus = patch.rfind('-')
712 if minus >= 0:
712 if minus >= 0:
713 res = partial_name(patch[:minus])
713 res = partial_name(patch[:minus])
714 if res:
714 if res:
715 i = self.series.index(res)
715 i = self.series.index(res)
716 try:
716 try:
717 off = int(patch[minus+1:] or 1)
717 off = int(patch[minus+1:] or 1)
718 except(ValueError, OverflowError):
718 except(ValueError, OverflowError):
719 pass
719 pass
720 else:
720 else:
721 if i - off >= 0:
721 if i - off >= 0:
722 return self.series[i - off]
722 return self.series[i - off]
723 plus = patch.rfind('+')
723 plus = patch.rfind('+')
724 if plus >= 0:
724 if plus >= 0:
725 res = partial_name(patch[:plus])
725 res = partial_name(patch[:plus])
726 if res:
726 if res:
727 i = self.series.index(res)
727 i = self.series.index(res)
728 try:
728 try:
729 off = int(patch[plus+1:] or 1)
729 off = int(patch[plus+1:] or 1)
730 except(ValueError, OverflowError):
730 except(ValueError, OverflowError):
731 pass
731 pass
732 else:
732 else:
733 if i + off < len(self.series):
733 if i + off < len(self.series):
734 return self.series[i + off]
734 return self.series[i + off]
735 raise util.Abort(_("patch %s not in series") % patch)
735 raise util.Abort(_("patch %s not in series") % patch)
736
736
737 def push(self, repo, patch=None, force=False, list=False,
737 def push(self, repo, patch=None, force=False, list=False,
738 mergeq=None, wlock=None):
738 mergeq=None, wlock=None):
739 if not wlock:
739 if not wlock:
740 wlock = repo.wlock()
740 wlock = repo.wlock()
741 patch = self.lookup(patch)
741 patch = self.lookup(patch)
742 # Suppose our series file is: A B C and the current 'top' patch is B.
742 # Suppose our series file is: A B C and the current 'top' patch is B.
743 # qpush C should be performed (moving forward)
743 # qpush C should be performed (moving forward)
744 # qpush B is a NOP (no change)
744 # qpush B is a NOP (no change)
745 # qpush A is an error (can't go backwards with qpush)
745 # qpush A is an error (can't go backwards with qpush)
746 if patch:
746 if patch:
747 info = self.isapplied(patch)
747 info = self.isapplied(patch)
748 if info:
748 if info:
749 if info[0] < len(self.applied) - 1:
749 if info[0] < len(self.applied) - 1:
750 raise util.Abort(_("cannot push to a previous patch: %s") %
750 raise util.Abort(_("cannot push to a previous patch: %s") %
751 patch)
751 patch)
752 if info[0] < len(self.series) - 1:
752 if info[0] < len(self.series) - 1:
753 self.ui.warn(_('qpush: %s is already at the top\n') % patch)
753 self.ui.warn(_('qpush: %s is already at the top\n') % patch)
754 else:
754 else:
755 self.ui.warn(_('all patches are currently applied\n'))
755 self.ui.warn(_('all patches are currently applied\n'))
756 return
756 return
757
757
758 # Following the above example, starting at 'top' of B:
758 # Following the above example, starting at 'top' of B:
759 # qpush should be performed (pushes C), but a subsequent qpush without
759 # qpush should be performed (pushes C), but a subsequent qpush without
760 # an argument is an error (nothing to apply). This allows a loop
760 # an argument is an error (nothing to apply). This allows a loop
761 # of "...while hg qpush..." to work as it detects an error when done
761 # of "...while hg qpush..." to work as it detects an error when done
762 if self.series_end() == len(self.series):
762 if self.series_end() == len(self.series):
763 self.ui.warn(_('patch series already fully applied\n'))
763 self.ui.warn(_('patch series already fully applied\n'))
764 return 1
764 return 1
765 if not force:
765 if not force:
766 self.check_localchanges(repo)
766 self.check_localchanges(repo)
767
767
768 self.applied_dirty = 1;
768 self.applied_dirty = 1;
769 start = self.series_end()
769 start = self.series_end()
770 if start > 0:
770 if start > 0:
771 self.check_toppatch(repo)
771 self.check_toppatch(repo)
772 if not patch:
772 if not patch:
773 patch = self.series[start]
773 patch = self.series[start]
774 end = start + 1
774 end = start + 1
775 else:
775 else:
776 end = self.series.index(patch, start) + 1
776 end = self.series.index(patch, start) + 1
777 s = self.series[start:end]
777 s = self.series[start:end]
778 all_files = {}
778 all_files = {}
779 try:
779 try:
780 if mergeq:
780 if mergeq:
781 ret = self.mergepatch(repo, mergeq, s, wlock)
781 ret = self.mergepatch(repo, mergeq, s, wlock)
782 else:
782 else:
783 ret = self.apply(repo, s, list, wlock=wlock,
783 ret = self.apply(repo, s, list, wlock=wlock,
784 all_files=all_files)
784 all_files=all_files)
785 except:
785 except:
786 self.ui.warn(_('cleaning up working directory...'))
786 self.ui.warn(_('cleaning up working directory...'))
787 node = repo.dirstate.parents()[0]
787 node = repo.dirstate.parents()[0]
788 hg.revert(repo, node, None, wlock)
788 hg.revert(repo, node, None, wlock)
789 unknown = repo.status(wlock=wlock)[4]
789 unknown = repo.status(wlock=wlock)[4]
790 # only remove unknown files that we know we touched or
790 # only remove unknown files that we know we touched or
791 # created while patching
791 # created while patching
792 for f in unknown:
792 for f in unknown:
793 if f in all_files:
793 if f in all_files:
794 util.unlink(repo.wjoin(f))
794 util.unlink(repo.wjoin(f))
795 self.ui.warn(_('done\n'))
795 self.ui.warn(_('done\n'))
796 raise
796 raise
797 top = self.applied[-1].name
797 top = self.applied[-1].name
798 if ret[0]:
798 if ret[0]:
799 self.ui.write("Errors during apply, please fix and refresh %s\n" %
799 self.ui.write("Errors during apply, please fix and refresh %s\n" %
800 top)
800 top)
801 else:
801 else:
802 self.ui.write("Now at: %s\n" % top)
802 self.ui.write("Now at: %s\n" % top)
803 return ret[0]
803 return ret[0]
804
804
805 def pop(self, repo, patch=None, force=False, update=True, all=False,
805 def pop(self, repo, patch=None, force=False, update=True, all=False,
806 wlock=None):
806 wlock=None):
807 def getfile(f, rev):
807 def getfile(f, rev):
808 t = repo.file(f).read(rev)
808 t = repo.file(f).read(rev)
809 repo.wfile(f, "w").write(t)
809 repo.wfile(f, "w").write(t)
810
810
811 if not wlock:
811 if not wlock:
812 wlock = repo.wlock()
812 wlock = repo.wlock()
813 if patch:
813 if patch:
814 # index, rev, patch
814 # index, rev, patch
815 info = self.isapplied(patch)
815 info = self.isapplied(patch)
816 if not info:
816 if not info:
817 patch = self.lookup(patch)
817 patch = self.lookup(patch)
818 info = self.isapplied(patch)
818 info = self.isapplied(patch)
819 if not info:
819 if not info:
820 raise util.Abort(_("patch %s is not applied") % patch)
820 raise util.Abort(_("patch %s is not applied") % patch)
821
821
822 if len(self.applied) == 0:
822 if len(self.applied) == 0:
823 # Allow qpop -a to work repeatedly,
823 # Allow qpop -a to work repeatedly,
824 # but not qpop without an argument
824 # but not qpop without an argument
825 self.ui.warn(_("no patches applied\n"))
825 self.ui.warn(_("no patches applied\n"))
826 return not all
826 return not all
827
827
828 if not update:
828 if not update:
829 parents = repo.dirstate.parents()
829 parents = repo.dirstate.parents()
830 rr = [ revlog.bin(x.rev) for x in self.applied ]
830 rr = [ revlog.bin(x.rev) for x in self.applied ]
831 for p in parents:
831 for p in parents:
832 if p in rr:
832 if p in rr:
833 self.ui.warn("qpop: forcing dirstate update\n")
833 self.ui.warn("qpop: forcing dirstate update\n")
834 update = True
834 update = True
835
835
836 if not force and update:
836 if not force and update:
837 self.check_localchanges(repo)
837 self.check_localchanges(repo)
838
838
839 self.applied_dirty = 1;
839 self.applied_dirty = 1;
840 end = len(self.applied)
840 end = len(self.applied)
841 if not patch:
841 if not patch:
842 if all:
842 if all:
843 popi = 0
843 popi = 0
844 else:
844 else:
845 popi = len(self.applied) - 1
845 popi = len(self.applied) - 1
846 else:
846 else:
847 popi = info[0] + 1
847 popi = info[0] + 1
848 if popi >= end:
848 if popi >= end:
849 self.ui.warn("qpop: %s is already at the top\n" % patch)
849 self.ui.warn("qpop: %s is already at the top\n" % patch)
850 return
850 return
851 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
851 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
852
852
853 start = info[0]
853 start = info[0]
854 rev = revlog.bin(info[1])
854 rev = revlog.bin(info[1])
855
855
856 # we know there are no local changes, so we can make a simplified
856 # we know there are no local changes, so we can make a simplified
857 # form of hg.update.
857 # form of hg.update.
858 if update:
858 if update:
859 top = self.check_toppatch(repo)
859 top = self.check_toppatch(repo)
860 qp = self.qparents(repo, rev)
860 qp = self.qparents(repo, rev)
861 changes = repo.changelog.read(qp)
861 changes = repo.changelog.read(qp)
862 mmap = repo.manifest.read(changes[0])
862 mmap = repo.manifest.read(changes[0])
863 m, a, r, d, u = repo.status(qp, top)[:5]
863 m, a, r, d, u = repo.status(qp, top)[:5]
864 if d:
864 if d:
865 raise util.Abort("deletions found between repo revs")
865 raise util.Abort("deletions found between repo revs")
866 for f in m:
866 for f in m:
867 getfile(f, mmap[f])
867 getfile(f, mmap[f])
868 for f in r:
868 for f in r:
869 getfile(f, mmap[f])
869 getfile(f, mmap[f])
870 util.set_exec(repo.wjoin(f), mmap.execf(f))
870 util.set_exec(repo.wjoin(f), mmap.execf(f))
871 repo.dirstate.update(m + r, 'n')
871 repo.dirstate.update(m + r, 'n')
872 for f in a:
872 for f in a:
873 try:
873 try:
874 os.unlink(repo.wjoin(f))
874 os.unlink(repo.wjoin(f))
875 except OSError, e:
875 except OSError, e:
876 if e.errno != errno.ENOENT:
876 if e.errno != errno.ENOENT:
877 raise
877 raise
878 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
878 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
879 except: pass
879 except: pass
880 if a:
880 if a:
881 repo.dirstate.forget(a)
881 repo.dirstate.forget(a)
882 repo.dirstate.setparents(qp, revlog.nullid)
882 repo.dirstate.setparents(qp, revlog.nullid)
883 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
883 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
884 del self.applied[start:end]
884 del self.applied[start:end]
885 if len(self.applied):
885 if len(self.applied):
886 self.ui.write("Now at: %s\n" % self.applied[-1].name)
886 self.ui.write("Now at: %s\n" % self.applied[-1].name)
887 else:
887 else:
888 self.ui.write("Patch queue now empty\n")
888 self.ui.write("Patch queue now empty\n")
889
889
890 def diff(self, repo, pats, opts):
890 def diff(self, repo, pats, opts):
891 top = self.check_toppatch(repo)
891 top = self.check_toppatch(repo)
892 if not top:
892 if not top:
893 self.ui.write("No patches applied\n")
893 self.ui.write("No patches applied\n")
894 return
894 return
895 qp = self.qparents(repo, top)
895 qp = self.qparents(repo, top)
896 if opts.get('git'):
896 if opts.get('git'):
897 self.diffopts().git = True
897 self.diffopts().git = True
898 self.printdiff(repo, qp, files=pats, opts=opts)
898 self.printdiff(repo, qp, files=pats, opts=opts)
899
899
900 def refresh(self, repo, pats=None, **opts):
900 def refresh(self, repo, pats=None, **opts):
901 if len(self.applied) == 0:
901 if len(self.applied) == 0:
902 self.ui.write("No patches applied\n")
902 self.ui.write("No patches applied\n")
903 return 1
903 return 1
904 wlock = repo.wlock()
904 wlock = repo.wlock()
905 self.check_toppatch(repo)
905 self.check_toppatch(repo)
906 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
906 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
907 top = revlog.bin(top)
907 top = revlog.bin(top)
908 cparents = repo.changelog.parents(top)
908 cparents = repo.changelog.parents(top)
909 patchparent = self.qparents(repo, top)
909 patchparent = self.qparents(repo, top)
910 message, comments, user, date, patchfound = self.readheaders(patchfn)
910 message, comments, user, date, patchfound = self.readheaders(patchfn)
911
911
912 patchf = self.opener(patchfn, 'r+')
912 patchf = self.opener(patchfn, 'r+')
913
913
914 # if the patch was a git patch, refresh it as a git patch
914 # if the patch was a git patch, refresh it as a git patch
915 for line in patchf:
915 for line in patchf:
916 if line.startswith('diff --git'):
916 if line.startswith('diff --git'):
917 self.diffopts().git = True
917 self.diffopts().git = True
918 break
918 break
919
919
920 msg = opts.get('msg', '').rstrip()
920 msg = opts.get('msg', '').rstrip()
921 if msg:
921 if msg:
922 if comments:
922 if comments:
923 # Remove existing message.
923 # Remove existing message.
924 ci = 0
924 ci = 0
925 subj = None
925 subj = None
926 for mi in xrange(len(message)):
926 for mi in xrange(len(message)):
927 if comments[ci].lower().startswith('subject: '):
927 if comments[ci].lower().startswith('subject: '):
928 subj = comments[ci][9:]
928 subj = comments[ci][9:]
929 while message[mi] != comments[ci] and message[mi] != subj:
929 while message[mi] != comments[ci] and message[mi] != subj:
930 ci += 1
930 ci += 1
931 del comments[ci]
931 del comments[ci]
932 comments.append(msg)
932 comments.append(msg)
933
933
934 patchf.seek(0)
934 patchf.seek(0)
935 patchf.truncate()
935 patchf.truncate()
936
936
937 if comments:
937 if comments:
938 comments = "\n".join(comments) + '\n\n'
938 comments = "\n".join(comments) + '\n\n'
939 patchf.write(comments)
939 patchf.write(comments)
940
940
941 if opts.get('git'):
941 if opts.get('git'):
942 self.diffopts().git = True
942 self.diffopts().git = True
943 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
943 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
944 tip = repo.changelog.tip()
944 tip = repo.changelog.tip()
945 if top == tip:
945 if top == tip:
946 # if the top of our patch queue is also the tip, there is an
946 # if the top of our patch queue is also the tip, there is an
947 # optimization here. We update the dirstate in place and strip
947 # optimization here. We update the dirstate in place and strip
948 # off the tip commit. Then just commit the current directory
948 # off the tip commit. Then just commit the current directory
949 # tree. We can also send repo.commit the list of files
949 # tree. We can also send repo.commit the list of files
950 # changed to speed up the diff
950 # changed to speed up the diff
951 #
951 #
952 # in short mode, we only diff the files included in the
952 # in short mode, we only diff the files included in the
953 # patch already
953 # patch already
954 #
954 #
955 # this should really read:
955 # this should really read:
956 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
956 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
957 # but we do it backwards to take advantage of manifest/chlog
957 # but we do it backwards to take advantage of manifest/chlog
958 # caching against the next repo.status call
958 # caching against the next repo.status call
959 #
959 #
960 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
960 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
961 changes = repo.changelog.read(tip)
961 changes = repo.changelog.read(tip)
962 man = repo.manifest.read(changes[0])
962 man = repo.manifest.read(changes[0])
963 aaa = aa[:]
963 aaa = aa[:]
964 if opts.get('short'):
964 if opts.get('short'):
965 filelist = mm + aa + dd
965 filelist = mm + aa + dd
966 match = dict.fromkeys(filelist).__contains__
966 match = dict.fromkeys(filelist).__contains__
967 else:
967 else:
968 filelist = None
968 filelist = None
969 match = util.always
969 match = util.always
970 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
970 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
971
971
972 # we might end up with files that were added between tip and
972 # we might end up with files that were added between tip and
973 # the dirstate parent, but then changed in the local dirstate.
973 # the dirstate parent, but then changed in the local dirstate.
974 # in this case, we want them to only show up in the added section
974 # in this case, we want them to only show up in the added section
975 for x in m:
975 for x in m:
976 if x not in aa:
976 if x not in aa:
977 mm.append(x)
977 mm.append(x)
978 # we might end up with files added by the local dirstate that
978 # we might end up with files added by the local dirstate that
979 # were deleted by the patch. In this case, they should only
979 # were deleted by the patch. In this case, they should only
980 # show up in the changed section.
980 # show up in the changed section.
981 for x in a:
981 for x in a:
982 if x in dd:
982 if x in dd:
983 del dd[dd.index(x)]
983 del dd[dd.index(x)]
984 mm.append(x)
984 mm.append(x)
985 else:
985 else:
986 aa.append(x)
986 aa.append(x)
987 # make sure any files deleted in the local dirstate
987 # make sure any files deleted in the local dirstate
988 # are not in the add or change column of the patch
988 # are not in the add or change column of the patch
989 forget = []
989 forget = []
990 for x in d + r:
990 for x in d + r:
991 if x in aa:
991 if x in aa:
992 del aa[aa.index(x)]
992 del aa[aa.index(x)]
993 forget.append(x)
993 forget.append(x)
994 continue
994 continue
995 elif x in mm:
995 elif x in mm:
996 del mm[mm.index(x)]
996 del mm[mm.index(x)]
997 dd.append(x)
997 dd.append(x)
998
998
999 m = util.unique(mm)
999 m = util.unique(mm)
1000 r = util.unique(dd)
1000 r = util.unique(dd)
1001 a = util.unique(aa)
1001 a = util.unique(aa)
1002 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1002 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1003 filelist = util.unique(c[0] + c[1] + c[2])
1003 filelist = util.unique(c[0] + c[1] + c[2])
1004 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1004 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1005 fp=patchf, changes=c, opts=self.diffopts())
1005 fp=patchf, changes=c, opts=self.diffopts())
1006 patchf.close()
1006 patchf.close()
1007
1007
1008 repo.dirstate.setparents(*cparents)
1008 repo.dirstate.setparents(*cparents)
1009 copies = {}
1009 copies = {}
1010 for dst in a:
1010 for dst in a:
1011 src = repo.dirstate.copied(dst)
1011 src = repo.dirstate.copied(dst)
1012 if src is None:
1012 if src is None:
1013 continue
1013 continue
1014 copies.setdefault(src, []).append(dst)
1014 copies.setdefault(src, []).append(dst)
1015 repo.dirstate.update(a, 'a')
1015 repo.dirstate.update(a, 'a')
1016 # remember the copies between patchparent and tip
1016 # remember the copies between patchparent and tip
1017 # this may be slow, so don't do it if we're not tracking copies
1017 # this may be slow, so don't do it if we're not tracking copies
1018 if self.diffopts().git:
1018 if self.diffopts().git:
1019 for dst in aaa:
1019 for dst in aaa:
1020 f = repo.file(dst)
1020 f = repo.file(dst)
1021 src = f.renamed(man[dst])
1021 src = f.renamed(man[dst])
1022 if src:
1022 if src:
1023 copies[src[0]] = copies.get(dst, [])
1023 copies[src[0]] = copies.get(dst, [])
1024 if dst in a:
1024 if dst in a:
1025 copies[src[0]].append(dst)
1025 copies[src[0]].append(dst)
1026 # we can't copy a file created by the patch itself
1026 # we can't copy a file created by the patch itself
1027 if dst in copies:
1027 if dst in copies:
1028 del copies[dst]
1028 del copies[dst]
1029 for src, dsts in copies.iteritems():
1029 for src, dsts in copies.iteritems():
1030 for dst in dsts:
1030 for dst in dsts:
1031 repo.dirstate.copy(src, dst)
1031 repo.dirstate.copy(src, dst)
1032 repo.dirstate.update(r, 'r')
1032 repo.dirstate.update(r, 'r')
1033 # if the patch excludes a modified file, mark that file with mtime=0
1033 # if the patch excludes a modified file, mark that file with mtime=0
1034 # so status can see it.
1034 # so status can see it.
1035 mm = []
1035 mm = []
1036 for i in xrange(len(m)-1, -1, -1):
1036 for i in xrange(len(m)-1, -1, -1):
1037 if not matchfn(m[i]):
1037 if not matchfn(m[i]):
1038 mm.append(m[i])
1038 mm.append(m[i])
1039 del m[i]
1039 del m[i]
1040 repo.dirstate.update(m, 'n')
1040 repo.dirstate.update(m, 'n')
1041 repo.dirstate.update(mm, 'n', st_mtime=-1, st_size=-1)
1041 repo.dirstate.update(mm, 'n', st_mtime=-1, st_size=-1)
1042 repo.dirstate.forget(forget)
1042 repo.dirstate.forget(forget)
1043
1043
1044 if not msg:
1044 if not msg:
1045 if not message:
1045 if not message:
1046 message = "[mq]: %s\n" % patchfn
1046 message = "[mq]: %s\n" % patchfn
1047 else:
1047 else:
1048 message = "\n".join(message)
1048 message = "\n".join(message)
1049 else:
1049 else:
1050 message = msg
1050 message = msg
1051
1051
1052 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1052 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1053 n = repo.commit(filelist, message, changes[1], match=matchfn,
1053 n = repo.commit(filelist, message, changes[1], match=matchfn,
1054 force=1, wlock=wlock)
1054 force=1, wlock=wlock)
1055 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1055 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1056 self.applied_dirty = 1
1056 self.applied_dirty = 1
1057 self.removeundo(repo)
1057 self.removeundo(repo)
1058 else:
1058 else:
1059 self.printdiff(repo, patchparent, fp=patchf)
1059 self.printdiff(repo, patchparent, fp=patchf)
1060 patchf.close()
1060 patchf.close()
1061 added = repo.status()[1]
1061 added = repo.status()[1]
1062 for a in added:
1062 for a in added:
1063 f = repo.wjoin(a)
1063 f = repo.wjoin(a)
1064 try:
1064 try:
1065 os.unlink(f)
1065 os.unlink(f)
1066 except OSError, e:
1066 except OSError, e:
1067 if e.errno != errno.ENOENT:
1067 if e.errno != errno.ENOENT:
1068 raise
1068 raise
1069 try: os.removedirs(os.path.dirname(f))
1069 try: os.removedirs(os.path.dirname(f))
1070 except: pass
1070 except: pass
1071 # forget the file copies in the dirstate
1071 # forget the file copies in the dirstate
1072 # push should readd the files later on
1072 # push should readd the files later on
1073 repo.dirstate.forget(added)
1073 repo.dirstate.forget(added)
1074 self.pop(repo, force=True, wlock=wlock)
1074 self.pop(repo, force=True, wlock=wlock)
1075 self.push(repo, force=True, wlock=wlock)
1075 self.push(repo, force=True, wlock=wlock)
1076
1076
1077 def init(self, repo, create=False):
1077 def init(self, repo, create=False):
1078 if not create and os.path.isdir(self.path):
1078 if not create and os.path.isdir(self.path):
1079 raise util.Abort(_("patch queue directory already exists"))
1079 raise util.Abort(_("patch queue directory already exists"))
1080 try:
1080 try:
1081 os.mkdir(self.path)
1081 os.mkdir(self.path)
1082 except OSError, inst:
1082 except OSError, inst:
1083 if inst.errno != errno.EEXIST or not create:
1083 if inst.errno != errno.EEXIST or not create:
1084 raise
1084 raise
1085 if create:
1085 if create:
1086 return self.qrepo(create=True)
1086 return self.qrepo(create=True)
1087
1087
1088 def unapplied(self, repo, patch=None):
1088 def unapplied(self, repo, patch=None):
1089 if patch and patch not in self.series:
1089 if patch and patch not in self.series:
1090 raise util.Abort(_("patch %s is not in series file") % patch)
1090 raise util.Abort(_("patch %s is not in series file") % patch)
1091 if not patch:
1091 if not patch:
1092 start = self.series_end()
1092 start = self.series_end()
1093 else:
1093 else:
1094 start = self.series.index(patch) + 1
1094 start = self.series.index(patch) + 1
1095 unapplied = []
1095 unapplied = []
1096 for i in xrange(start, len(self.series)):
1096 for i in xrange(start, len(self.series)):
1097 pushable, reason = self.pushable(i)
1097 pushable, reason = self.pushable(i)
1098 if pushable:
1098 if pushable:
1099 unapplied.append((i, self.series[i]))
1099 unapplied.append((i, self.series[i]))
1100 self.explain_pushable(i)
1100 self.explain_pushable(i)
1101 return unapplied
1101 return unapplied
1102
1102
1103 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1103 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1104 summary=False):
1104 summary=False):
1105 def displayname(patchname):
1105 def displayname(patchname):
1106 if summary:
1106 if summary:
1107 msg = self.readheaders(patchname)[0]
1107 msg = self.readheaders(patchname)[0]
1108 msg = msg and ': ' + msg[0] or ': '
1108 msg = msg and ': ' + msg[0] or ': '
1109 else:
1109 else:
1110 msg = ''
1110 msg = ''
1111 return '%s%s' % (patchname, msg)
1111 return '%s%s' % (patchname, msg)
1112
1112
1113 applied = dict.fromkeys([p.name for p in self.applied])
1113 applied = dict.fromkeys([p.name for p in self.applied])
1114 if length is None:
1114 if length is None:
1115 length = len(self.series) - start
1115 length = len(self.series) - start
1116 if not missing:
1116 if not missing:
1117 for i in xrange(start, start+length):
1117 for i in xrange(start, start+length):
1118 patch = self.series[i]
1118 patch = self.series[i]
1119 if patch in applied:
1119 if patch in applied:
1120 stat = 'A'
1120 stat = 'A'
1121 elif self.pushable(i)[0]:
1121 elif self.pushable(i)[0]:
1122 stat = 'U'
1122 stat = 'U'
1123 else:
1123 else:
1124 stat = 'G'
1124 stat = 'G'
1125 pfx = ''
1125 pfx = ''
1126 if self.ui.verbose:
1126 if self.ui.verbose:
1127 pfx = '%d %s ' % (i, stat)
1127 pfx = '%d %s ' % (i, stat)
1128 elif status and status != stat:
1128 elif status and status != stat:
1129 continue
1129 continue
1130 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1130 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1131 else:
1131 else:
1132 msng_list = []
1132 msng_list = []
1133 for root, dirs, files in os.walk(self.path):
1133 for root, dirs, files in os.walk(self.path):
1134 d = root[len(self.path) + 1:]
1134 d = root[len(self.path) + 1:]
1135 for f in files:
1135 for f in files:
1136 fl = os.path.join(d, f)
1136 fl = os.path.join(d, f)
1137 if (fl not in self.series and
1137 if (fl not in self.series and
1138 fl not in (self.status_path, self.series_path,
1138 fl not in (self.status_path, self.series_path,
1139 self.guards_path)
1139 self.guards_path)
1140 and not fl.startswith('.')):
1140 and not fl.startswith('.')):
1141 msng_list.append(fl)
1141 msng_list.append(fl)
1142 msng_list.sort()
1142 msng_list.sort()
1143 for x in msng_list:
1143 for x in msng_list:
1144 pfx = self.ui.verbose and ('D ') or ''
1144 pfx = self.ui.verbose and ('D ') or ''
1145 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1145 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1146
1146
1147 def issaveline(self, l):
1147 def issaveline(self, l):
1148 if l.name == '.hg.patches.save.line':
1148 if l.name == '.hg.patches.save.line':
1149 return True
1149 return True
1150
1150
1151 def qrepo(self, create=False):
1151 def qrepo(self, create=False):
1152 if create or os.path.isdir(self.join(".hg")):
1152 if create or os.path.isdir(self.join(".hg")):
1153 return hg.repository(self.ui, path=self.path, create=create)
1153 return hg.repository(self.ui, path=self.path, create=create)
1154
1154
1155 def restore(self, repo, rev, delete=None, qupdate=None):
1155 def restore(self, repo, rev, delete=None, qupdate=None):
1156 c = repo.changelog.read(rev)
1156 c = repo.changelog.read(rev)
1157 desc = c[4].strip()
1157 desc = c[4].strip()
1158 lines = desc.splitlines()
1158 lines = desc.splitlines()
1159 i = 0
1159 i = 0
1160 datastart = None
1160 datastart = None
1161 series = []
1161 series = []
1162 applied = []
1162 applied = []
1163 qpp = None
1163 qpp = None
1164 for i in xrange(0, len(lines)):
1164 for i in xrange(0, len(lines)):
1165 if lines[i] == 'Patch Data:':
1165 if lines[i] == 'Patch Data:':
1166 datastart = i + 1
1166 datastart = i + 1
1167 elif lines[i].startswith('Dirstate:'):
1167 elif lines[i].startswith('Dirstate:'):
1168 l = lines[i].rstrip()
1168 l = lines[i].rstrip()
1169 l = l[10:].split(' ')
1169 l = l[10:].split(' ')
1170 qpp = [ hg.bin(x) for x in l ]
1170 qpp = [ hg.bin(x) for x in l ]
1171 elif datastart != None:
1171 elif datastart != None:
1172 l = lines[i].rstrip()
1172 l = lines[i].rstrip()
1173 se = statusentry(l)
1173 se = statusentry(l)
1174 file_ = se.name
1174 file_ = se.name
1175 if se.rev:
1175 if se.rev:
1176 applied.append(se)
1176 applied.append(se)
1177 else:
1177 else:
1178 series.append(file_)
1178 series.append(file_)
1179 if datastart == None:
1179 if datastart == None:
1180 self.ui.warn("No saved patch data found\n")
1180 self.ui.warn("No saved patch data found\n")
1181 return 1
1181 return 1
1182 self.ui.warn("restoring status: %s\n" % lines[0])
1182 self.ui.warn("restoring status: %s\n" % lines[0])
1183 self.full_series = series
1183 self.full_series = series
1184 self.applied = applied
1184 self.applied = applied
1185 self.parse_series()
1185 self.parse_series()
1186 self.series_dirty = 1
1186 self.series_dirty = 1
1187 self.applied_dirty = 1
1187 self.applied_dirty = 1
1188 heads = repo.changelog.heads()
1188 heads = repo.changelog.heads()
1189 if delete:
1189 if delete:
1190 if rev not in heads:
1190 if rev not in heads:
1191 self.ui.warn("save entry has children, leaving it alone\n")
1191 self.ui.warn("save entry has children, leaving it alone\n")
1192 else:
1192 else:
1193 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1193 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1194 pp = repo.dirstate.parents()
1194 pp = repo.dirstate.parents()
1195 if rev in pp:
1195 if rev in pp:
1196 update = True
1196 update = True
1197 else:
1197 else:
1198 update = False
1198 update = False
1199 self.strip(repo, rev, update=update, backup='strip')
1199 self.strip(repo, rev, update=update, backup='strip')
1200 if qpp:
1200 if qpp:
1201 self.ui.warn("saved queue repository parents: %s %s\n" %
1201 self.ui.warn("saved queue repository parents: %s %s\n" %
1202 (hg.short(qpp[0]), hg.short(qpp[1])))
1202 (hg.short(qpp[0]), hg.short(qpp[1])))
1203 if qupdate:
1203 if qupdate:
1204 print "queue directory updating"
1204 print "queue directory updating"
1205 r = self.qrepo()
1205 r = self.qrepo()
1206 if not r:
1206 if not r:
1207 self.ui.warn("Unable to load queue repository\n")
1207 self.ui.warn("Unable to load queue repository\n")
1208 return 1
1208 return 1
1209 hg.clean(r, qpp[0])
1209 hg.clean(r, qpp[0])
1210
1210
1211 def save(self, repo, msg=None):
1211 def save(self, repo, msg=None):
1212 if len(self.applied) == 0:
1212 if len(self.applied) == 0:
1213 self.ui.warn("save: no patches applied, exiting\n")
1213 self.ui.warn("save: no patches applied, exiting\n")
1214 return 1
1214 return 1
1215 if self.issaveline(self.applied[-1]):
1215 if self.issaveline(self.applied[-1]):
1216 self.ui.warn("status is already saved\n")
1216 self.ui.warn("status is already saved\n")
1217 return 1
1217 return 1
1218
1218
1219 ar = [ ':' + x for x in self.full_series ]
1219 ar = [ ':' + x for x in self.full_series ]
1220 if not msg:
1220 if not msg:
1221 msg = "hg patches saved state"
1221 msg = "hg patches saved state"
1222 else:
1222 else:
1223 msg = "hg patches: " + msg.rstrip('\r\n')
1223 msg = "hg patches: " + msg.rstrip('\r\n')
1224 r = self.qrepo()
1224 r = self.qrepo()
1225 if r:
1225 if r:
1226 pp = r.dirstate.parents()
1226 pp = r.dirstate.parents()
1227 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1227 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1228 msg += "\n\nPatch Data:\n"
1228 msg += "\n\nPatch Data:\n"
1229 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1229 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1230 "\n".join(ar) + '\n' or "")
1230 "\n".join(ar) + '\n' or "")
1231 n = repo.commit(None, text, user=None, force=1)
1231 n = repo.commit(None, text, user=None, force=1)
1232 if not n:
1232 if not n:
1233 self.ui.warn("repo commit failed\n")
1233 self.ui.warn("repo commit failed\n")
1234 return 1
1234 return 1
1235 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1235 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1236 self.applied_dirty = 1
1236 self.applied_dirty = 1
1237 self.removeundo(repo)
1237 self.removeundo(repo)
1238
1238
1239 def full_series_end(self):
1239 def full_series_end(self):
1240 if len(self.applied) > 0:
1240 if len(self.applied) > 0:
1241 p = self.applied[-1].name
1241 p = self.applied[-1].name
1242 end = self.find_series(p)
1242 end = self.find_series(p)
1243 if end == None:
1243 if end == None:
1244 return len(self.full_series)
1244 return len(self.full_series)
1245 return end + 1
1245 return end + 1
1246 return 0
1246 return 0
1247
1247
1248 def series_end(self, all_patches=False):
1248 def series_end(self, all_patches=False):
1249 """If all_patches is False, return the index of the next pushable patch
1249 """If all_patches is False, return the index of the next pushable patch
1250 in the series, or the series length. If all_patches is True, return the
1250 in the series, or the series length. If all_patches is True, return the
1251 index of the first patch past the last applied one.
1251 index of the first patch past the last applied one.
1252 """
1252 """
1253 end = 0
1253 end = 0
1254 def next(start):
1254 def next(start):
1255 if all_patches:
1255 if all_patches:
1256 return start
1256 return start
1257 i = start
1257 i = start
1258 while i < len(self.series):
1258 while i < len(self.series):
1259 p, reason = self.pushable(i)
1259 p, reason = self.pushable(i)
1260 if p:
1260 if p:
1261 break
1261 break
1262 self.explain_pushable(i)
1262 self.explain_pushable(i)
1263 i += 1
1263 i += 1
1264 return i
1264 return i
1265 if len(self.applied) > 0:
1265 if len(self.applied) > 0:
1266 p = self.applied[-1].name
1266 p = self.applied[-1].name
1267 try:
1267 try:
1268 end = self.series.index(p)
1268 end = self.series.index(p)
1269 except ValueError:
1269 except ValueError:
1270 return 0
1270 return 0
1271 return next(end + 1)
1271 return next(end + 1)
1272 return next(end)
1272 return next(end)
1273
1273
1274 def appliedname(self, index):
1274 def appliedname(self, index):
1275 pname = self.applied[index].name
1275 pname = self.applied[index].name
1276 if not self.ui.verbose:
1276 if not self.ui.verbose:
1277 p = pname
1277 p = pname
1278 else:
1278 else:
1279 p = str(self.series.index(pname)) + " " + pname
1279 p = str(self.series.index(pname)) + " " + pname
1280 return p
1280 return p
1281
1281
1282 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1282 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1283 force=None, git=False):
1283 force=None, git=False):
1284 def checkseries(patchname):
1284 def checkseries(patchname):
1285 if patchname in self.series:
1285 if patchname in self.series:
1286 raise util.Abort(_('patch %s is already in the series file')
1286 raise util.Abort(_('patch %s is already in the series file')
1287 % patchname)
1287 % patchname)
1288 def checkfile(patchname):
1288 def checkfile(patchname):
1289 if not force and os.path.exists(self.join(patchname)):
1289 if not force and os.path.exists(self.join(patchname)):
1290 raise util.Abort(_('patch "%s" already exists')
1290 raise util.Abort(_('patch "%s" already exists')
1291 % patchname)
1291 % patchname)
1292
1292
1293 if rev:
1293 if rev:
1294 if files:
1294 if files:
1295 raise util.Abort(_('option "-r" not valid when importing '
1295 raise util.Abort(_('option "-r" not valid when importing '
1296 'files'))
1296 'files'))
1297 rev = cmdutil.revrange(repo, rev)
1297 rev = cmdutil.revrange(repo, rev)
1298 rev.sort(lambda x, y: cmp(y, x))
1298 rev.sort(lambda x, y: cmp(y, x))
1299 if (len(files) > 1 or len(rev) > 1) and patchname:
1299 if (len(files) > 1 or len(rev) > 1) and patchname:
1300 raise util.Abort(_('option "-n" not valid when importing multiple '
1300 raise util.Abort(_('option "-n" not valid when importing multiple '
1301 'patches'))
1301 'patches'))
1302 i = 0
1302 i = 0
1303 added = []
1303 added = []
1304 if rev:
1304 if rev:
1305 # If mq patches are applied, we can only import revisions
1305 # If mq patches are applied, we can only import revisions
1306 # that form a linear path to qbase.
1306 # that form a linear path to qbase.
1307 # Otherwise, they should form a linear path to a head.
1307 # Otherwise, they should form a linear path to a head.
1308 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1308 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1309 if len(heads) > 1:
1309 if len(heads) > 1:
1310 raise util.Abort(_('revision %d is the root of more than one '
1310 raise util.Abort(_('revision %d is the root of more than one '
1311 'branch') % rev[-1])
1311 'branch') % rev[-1])
1312 if self.applied:
1312 if self.applied:
1313 base = revlog.hex(repo.changelog.node(rev[0]))
1313 base = revlog.hex(repo.changelog.node(rev[0]))
1314 if base in [n.rev for n in self.applied]:
1314 if base in [n.rev for n in self.applied]:
1315 raise util.Abort(_('revision %d is already managed')
1315 raise util.Abort(_('revision %d is already managed')
1316 % rev[0])
1316 % rev[0])
1317 if heads != [revlog.bin(self.applied[-1].rev)]:
1317 if heads != [revlog.bin(self.applied[-1].rev)]:
1318 raise util.Abort(_('revision %d is not the parent of '
1318 raise util.Abort(_('revision %d is not the parent of '
1319 'the queue') % rev[0])
1319 'the queue') % rev[0])
1320 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1320 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1321 lastparent = repo.changelog.parentrevs(base)[0]
1321 lastparent = repo.changelog.parentrevs(base)[0]
1322 else:
1322 else:
1323 if heads != [repo.changelog.node(rev[0])]:
1323 if heads != [repo.changelog.node(rev[0])]:
1324 raise util.Abort(_('revision %d has unmanaged children')
1324 raise util.Abort(_('revision %d has unmanaged children')
1325 % rev[0])
1325 % rev[0])
1326 lastparent = None
1326 lastparent = None
1327
1327
1328 if git:
1328 if git:
1329 self.diffopts().git = True
1329 self.diffopts().git = True
1330
1330
1331 for r in rev:
1331 for r in rev:
1332 p1, p2 = repo.changelog.parentrevs(r)
1332 p1, p2 = repo.changelog.parentrevs(r)
1333 n = repo.changelog.node(r)
1333 n = repo.changelog.node(r)
1334 if p2 != revlog.nullrev:
1334 if p2 != revlog.nullrev:
1335 raise util.Abort(_('cannot import merge revision %d') % r)
1335 raise util.Abort(_('cannot import merge revision %d') % r)
1336 if lastparent and lastparent != r:
1336 if lastparent and lastparent != r:
1337 raise util.Abort(_('revision %d is not the parent of %d')
1337 raise util.Abort(_('revision %d is not the parent of %d')
1338 % (r, lastparent))
1338 % (r, lastparent))
1339 lastparent = p1
1339 lastparent = p1
1340
1340
1341 if not patchname:
1341 if not patchname:
1342 patchname = normname('%d.diff' % r)
1342 patchname = normname('%d.diff' % r)
1343 checkseries(patchname)
1343 checkseries(patchname)
1344 checkfile(patchname)
1344 checkfile(patchname)
1345 self.full_series.insert(0, patchname)
1345 self.full_series.insert(0, patchname)
1346
1346
1347 patchf = self.opener(patchname, "w")
1347 patchf = self.opener(patchname, "w")
1348 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1348 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1349 patchf.close()
1349 patchf.close()
1350
1350
1351 se = statusentry(revlog.hex(n), patchname)
1351 se = statusentry(revlog.hex(n), patchname)
1352 self.applied.insert(0, se)
1352 self.applied.insert(0, se)
1353
1353
1354 added.append(patchname)
1354 added.append(patchname)
1355 patchname = None
1355 patchname = None
1356 self.parse_series()
1356 self.parse_series()
1357 self.applied_dirty = 1
1357 self.applied_dirty = 1
1358
1358
1359 for filename in files:
1359 for filename in files:
1360 if existing:
1360 if existing:
1361 if filename == '-':
1361 if filename == '-':
1362 raise util.Abort(_('-e is incompatible with import from -'))
1362 raise util.Abort(_('-e is incompatible with import from -'))
1363 if not patchname:
1363 if not patchname:
1364 patchname = normname(filename)
1364 patchname = normname(filename)
1365 if not os.path.isfile(self.join(patchname)):
1365 if not os.path.isfile(self.join(patchname)):
1366 raise util.Abort(_("patch %s does not exist") % patchname)
1366 raise util.Abort(_("patch %s does not exist") % patchname)
1367 else:
1367 else:
1368 try:
1368 try:
1369 if filename == '-':
1369 if filename == '-':
1370 if not patchname:
1370 if not patchname:
1371 raise util.Abort(_('need --name to import a patch from -'))
1371 raise util.Abort(_('need --name to import a patch from -'))
1372 text = sys.stdin.read()
1372 text = sys.stdin.read()
1373 else:
1373 else:
1374 text = file(filename).read()
1374 text = file(filename).read()
1375 except IOError:
1375 except IOError:
1376 raise util.Abort(_("unable to read %s") % patchname)
1376 raise util.Abort(_("unable to read %s") % patchname)
1377 if not patchname:
1377 if not patchname:
1378 patchname = normname(os.path.basename(filename))
1378 patchname = normname(os.path.basename(filename))
1379 checkfile(patchname)
1379 checkfile(patchname)
1380 patchf = self.opener(patchname, "w")
1380 patchf = self.opener(patchname, "w")
1381 patchf.write(text)
1381 patchf.write(text)
1382 checkseries(patchname)
1382 checkseries(patchname)
1383 index = self.full_series_end() + i
1383 index = self.full_series_end() + i
1384 self.full_series[index:index] = [patchname]
1384 self.full_series[index:index] = [patchname]
1385 self.parse_series()
1385 self.parse_series()
1386 self.ui.warn("adding %s to series file\n" % patchname)
1386 self.ui.warn("adding %s to series file\n" % patchname)
1387 i += 1
1387 i += 1
1388 added.append(patchname)
1388 added.append(patchname)
1389 patchname = None
1389 patchname = None
1390 self.series_dirty = 1
1390 self.series_dirty = 1
1391 qrepo = self.qrepo()
1391 qrepo = self.qrepo()
1392 if qrepo:
1392 if qrepo:
1393 qrepo.add(added)
1393 qrepo.add(added)
1394
1394
1395 def delete(ui, repo, *patches, **opts):
1395 def delete(ui, repo, *patches, **opts):
1396 """remove patches from queue
1396 """remove patches from queue
1397
1397
1398 The patches must not be applied, unless they are arguments to
1398 The patches must not be applied, unless they are arguments to
1399 the --rev parameter. At least one patch or revision is required.
1399 the --rev parameter. At least one patch or revision is required.
1400
1400
1401 With --rev, mq will stop managing the named revisions (converting
1401 With --rev, mq will stop managing the named revisions (converting
1402 them to regular mercurial changesets). The patches must be applied
1402 them to regular mercurial changesets). The patches must be applied
1403 and at the base of the stack. This option is useful when the patches
1403 and at the base of the stack. This option is useful when the patches
1404 have been applied upstream.
1404 have been applied upstream.
1405
1405
1406 With --keep, the patch files are preserved in the patch directory."""
1406 With --keep, the patch files are preserved in the patch directory."""
1407 q = repo.mq
1407 q = repo.mq
1408 q.delete(repo, patches, opts)
1408 q.delete(repo, patches, opts)
1409 q.save_dirty()
1409 q.save_dirty()
1410 return 0
1410 return 0
1411
1411
1412 def applied(ui, repo, patch=None, **opts):
1412 def applied(ui, repo, patch=None, **opts):
1413 """print the patches already applied"""
1413 """print the patches already applied"""
1414 q = repo.mq
1414 q = repo.mq
1415 if patch:
1415 if patch:
1416 if patch not in q.series:
1416 if patch not in q.series:
1417 raise util.Abort(_("patch %s is not in series file") % patch)
1417 raise util.Abort(_("patch %s is not in series file") % patch)
1418 end = q.series.index(patch) + 1
1418 end = q.series.index(patch) + 1
1419 else:
1419 else:
1420 end = q.series_end(True)
1420 end = q.series_end(True)
1421 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1421 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1422
1422
1423 def unapplied(ui, repo, patch=None, **opts):
1423 def unapplied(ui, repo, patch=None, **opts):
1424 """print the patches not yet applied"""
1424 """print the patches not yet applied"""
1425 q = repo.mq
1425 q = repo.mq
1426 if patch:
1426 if patch:
1427 if patch not in q.series:
1427 if patch not in q.series:
1428 raise util.Abort(_("patch %s is not in series file") % patch)
1428 raise util.Abort(_("patch %s is not in series file") % patch)
1429 start = q.series.index(patch) + 1
1429 start = q.series.index(patch) + 1
1430 else:
1430 else:
1431 start = q.series_end(True)
1431 start = q.series_end(True)
1432 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1432 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1433
1433
1434 def qimport(ui, repo, *filename, **opts):
1434 def qimport(ui, repo, *filename, **opts):
1435 """import a patch
1435 """import a patch
1436
1436
1437 The patch will have the same name as its source file unless you
1437 The patch will have the same name as its source file unless you
1438 give it a new one with --name.
1438 give it a new one with --name.
1439
1439
1440 You can register an existing patch inside the patch directory
1440 You can register an existing patch inside the patch directory
1441 with the --existing flag.
1441 with the --existing flag.
1442
1442
1443 With --force, an existing patch of the same name will be overwritten.
1443 With --force, an existing patch of the same name will be overwritten.
1444
1444
1445 An existing changeset may be placed under mq control with --rev
1445 An existing changeset may be placed under mq control with --rev
1446 (e.g. qimport --rev tip -n patch will place tip under mq control).
1446 (e.g. qimport --rev tip -n patch will place tip under mq control).
1447 With --git, patches imported with --rev will use the git diff
1447 With --git, patches imported with --rev will use the git diff
1448 format.
1448 format.
1449 """
1449 """
1450 q = repo.mq
1450 q = repo.mq
1451 q.qimport(repo, filename, patchname=opts['name'],
1451 q.qimport(repo, filename, patchname=opts['name'],
1452 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1452 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1453 git=opts['git'])
1453 git=opts['git'])
1454 q.save_dirty()
1454 q.save_dirty()
1455 return 0
1455 return 0
1456
1456
1457 def init(ui, repo, **opts):
1457 def init(ui, repo, **opts):
1458 """init a new queue repository
1458 """init a new queue repository
1459
1459
1460 The queue repository is unversioned by default. If -c is
1460 The queue repository is unversioned by default. If -c is
1461 specified, qinit will create a separate nested repository
1461 specified, qinit will create a separate nested repository
1462 for patches (qinit -c may also be run later to convert
1462 for patches (qinit -c may also be run later to convert
1463 an unversioned patch repository into a versioned one).
1463 an unversioned patch repository into a versioned one).
1464 You can use qcommit to commit changes to this queue repository."""
1464 You can use qcommit to commit changes to this queue repository."""
1465 q = repo.mq
1465 q = repo.mq
1466 r = q.init(repo, create=opts['create_repo'])
1466 r = q.init(repo, create=opts['create_repo'])
1467 q.save_dirty()
1467 q.save_dirty()
1468 if r:
1468 if r:
1469 if not os.path.exists(r.wjoin('.hgignore')):
1469 if not os.path.exists(r.wjoin('.hgignore')):
1470 fp = r.wopener('.hgignore', 'w')
1470 fp = r.wopener('.hgignore', 'w')
1471 fp.write('syntax: glob\n')
1471 fp.write('syntax: glob\n')
1472 fp.write('status\n')
1472 fp.write('status\n')
1473 fp.write('guards\n')
1473 fp.write('guards\n')
1474 fp.close()
1474 fp.close()
1475 if not os.path.exists(r.wjoin('series')):
1475 if not os.path.exists(r.wjoin('series')):
1476 r.wopener('series', 'w').close()
1476 r.wopener('series', 'w').close()
1477 r.add(['.hgignore', 'series'])
1477 r.add(['.hgignore', 'series'])
1478 commands.add(ui, r)
1478 commands.add(ui, r)
1479 return 0
1479 return 0
1480
1480
1481 def clone(ui, source, dest=None, **opts):
1481 def clone(ui, source, dest=None, **opts):
1482 '''clone main and patch repository at same time
1482 '''clone main and patch repository at same time
1483
1483
1484 If source is local, destination will have no patches applied. If
1484 If source is local, destination will have no patches applied. If
1485 source is remote, this command can not check if patches are
1485 source is remote, this command can not check if patches are
1486 applied in source, so cannot guarantee that patches are not
1486 applied in source, so cannot guarantee that patches are not
1487 applied in destination. If you clone remote repository, be sure
1487 applied in destination. If you clone remote repository, be sure
1488 before that it has no patches applied.
1488 before that it has no patches applied.
1489
1489
1490 Source patch repository is looked for in <src>/.hg/patches by
1490 Source patch repository is looked for in <src>/.hg/patches by
1491 default. Use -p <url> to change.
1491 default. Use -p <url> to change.
1492 '''
1492 '''
1493 def patchdir(repo):
1493 def patchdir(repo):
1494 url = repo.url()
1494 url = repo.url()
1495 if url.endswith('/'):
1495 if url.endswith('/'):
1496 url = url[:-1]
1496 url = url[:-1]
1497 return url + '/.hg/patches'
1497 return url + '/.hg/patches'
1498 cmdutil.setremoteconfig(ui, opts)
1498 cmdutil.setremoteconfig(ui, opts)
1499 if dest is None:
1499 if dest is None:
1500 dest = hg.defaultdest(source)
1500 dest = hg.defaultdest(source)
1501 sr = hg.repository(ui, ui.expandpath(source))
1501 sr = hg.repository(ui, ui.expandpath(source))
1502 qbase, destrev = None, None
1502 qbase, destrev = None, None
1503 if sr.local():
1503 if sr.local():
1504 if sr.mq.applied:
1504 if sr.mq.applied:
1505 qbase = revlog.bin(sr.mq.applied[0].rev)
1505 qbase = revlog.bin(sr.mq.applied[0].rev)
1506 if not hg.islocal(dest):
1506 if not hg.islocal(dest):
1507 heads = dict.fromkeys(sr.heads())
1507 heads = dict.fromkeys(sr.heads())
1508 for h in sr.heads(qbase):
1508 for h in sr.heads(qbase):
1509 del heads[h]
1509 del heads[h]
1510 destrev = heads.keys()
1510 destrev = heads.keys()
1511 destrev.append(sr.changelog.parents(qbase)[0])
1511 destrev.append(sr.changelog.parents(qbase)[0])
1512 ui.note(_('cloning main repo\n'))
1512 ui.note(_('cloning main repo\n'))
1513 sr, dr = hg.clone(ui, sr.url(), dest,
1513 sr, dr = hg.clone(ui, sr.url(), dest,
1514 pull=opts['pull'],
1514 pull=opts['pull'],
1515 rev=destrev,
1515 rev=destrev,
1516 update=False,
1516 update=False,
1517 stream=opts['uncompressed'])
1517 stream=opts['uncompressed'])
1518 ui.note(_('cloning patch repo\n'))
1518 ui.note(_('cloning patch repo\n'))
1519 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1519 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1520 pull=opts['pull'], update=not opts['noupdate'],
1520 pull=opts['pull'], update=not opts['noupdate'],
1521 stream=opts['uncompressed'])
1521 stream=opts['uncompressed'])
1522 if dr.local():
1522 if dr.local():
1523 if qbase:
1523 if qbase:
1524 ui.note(_('stripping applied patches from destination repo\n'))
1524 ui.note(_('stripping applied patches from destination repo\n'))
1525 dr.mq.strip(dr, qbase, update=False, backup=None)
1525 dr.mq.strip(dr, qbase, update=False, backup=None)
1526 if not opts['noupdate']:
1526 if not opts['noupdate']:
1527 ui.note(_('updating destination repo\n'))
1527 ui.note(_('updating destination repo\n'))
1528 hg.update(dr, dr.changelog.tip())
1528 hg.update(dr, dr.changelog.tip())
1529
1529
1530 def commit(ui, repo, *pats, **opts):
1530 def commit(ui, repo, *pats, **opts):
1531 """commit changes in the queue repository"""
1531 """commit changes in the queue repository"""
1532 q = repo.mq
1532 q = repo.mq
1533 r = q.qrepo()
1533 r = q.qrepo()
1534 if not r: raise util.Abort('no queue repository')
1534 if not r: raise util.Abort('no queue repository')
1535 commands.commit(r.ui, r, *pats, **opts)
1535 commands.commit(r.ui, r, *pats, **opts)
1536
1536
1537 def series(ui, repo, **opts):
1537 def series(ui, repo, **opts):
1538 """print the entire series file"""
1538 """print the entire series file"""
1539 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1539 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1540 return 0
1540 return 0
1541
1541
1542 def top(ui, repo, **opts):
1542 def top(ui, repo, **opts):
1543 """print the name of the current patch"""
1543 """print the name of the current patch"""
1544 q = repo.mq
1544 q = repo.mq
1545 t = q.applied and q.series_end(True) or 0
1545 t = q.applied and q.series_end(True) or 0
1546 if t:
1546 if t:
1547 return q.qseries(repo, start=t-1, length=1, status='A',
1547 return q.qseries(repo, start=t-1, length=1, status='A',
1548 summary=opts.get('summary'))
1548 summary=opts.get('summary'))
1549 else:
1549 else:
1550 ui.write("No patches applied\n")
1550 ui.write("No patches applied\n")
1551 return 1
1551 return 1
1552
1552
1553 def next(ui, repo, **opts):
1553 def next(ui, repo, **opts):
1554 """print the name of the next patch"""
1554 """print the name of the next patch"""
1555 q = repo.mq
1555 q = repo.mq
1556 end = q.series_end()
1556 end = q.series_end()
1557 if end == len(q.series):
1557 if end == len(q.series):
1558 ui.write("All patches applied\n")
1558 ui.write("All patches applied\n")
1559 return 1
1559 return 1
1560 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1560 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1561
1561
1562 def prev(ui, repo, **opts):
1562 def prev(ui, repo, **opts):
1563 """print the name of the previous patch"""
1563 """print the name of the previous patch"""
1564 q = repo.mq
1564 q = repo.mq
1565 l = len(q.applied)
1565 l = len(q.applied)
1566 if l == 1:
1566 if l == 1:
1567 ui.write("Only one patch applied\n")
1567 ui.write("Only one patch applied\n")
1568 return 1
1568 return 1
1569 if not l:
1569 if not l:
1570 ui.write("No patches applied\n")
1570 ui.write("No patches applied\n")
1571 return 1
1571 return 1
1572 return q.qseries(repo, start=l-2, length=1, status='A',
1572 return q.qseries(repo, start=l-2, length=1, status='A',
1573 summary=opts.get('summary'))
1573 summary=opts.get('summary'))
1574
1574
1575 def new(ui, repo, patch, *args, **opts):
1575 def new(ui, repo, patch, *args, **opts):
1576 """create a new patch
1576 """create a new patch
1577
1577
1578 qnew creates a new patch on top of the currently-applied patch
1578 qnew creates a new patch on top of the currently-applied patch
1579 (if any). It will refuse to run if there are any outstanding
1579 (if any). It will refuse to run if there are any outstanding
1580 changes unless -f is specified, in which case the patch will
1580 changes unless -f is specified, in which case the patch will
1581 be initialised with them. You may also use -I, -X, and/or a list of
1581 be initialised with them. You may also use -I, -X, and/or a list of
1582 files after the patch name to add only changes to matching files
1582 files after the patch name to add only changes to matching files
1583 to the new patch, leaving the rest as uncommitted modifications.
1583 to the new patch, leaving the rest as uncommitted modifications.
1584
1584
1585 -e, -m or -l set the patch header as well as the commit message.
1585 -e, -m or -l set the patch header as well as the commit message.
1586 If none is specified, the patch header is empty and the
1586 If none is specified, the patch header is empty and the
1587 commit message is '[mq]: PATCH'"""
1587 commit message is '[mq]: PATCH'"""
1588 q = repo.mq
1588 q = repo.mq
1589 message = cmdutil.logmessage(opts)
1589 message = cmdutil.logmessage(opts)
1590 if opts['edit']:
1590 if opts['edit']:
1591 message = ui.edit(message, ui.username())
1591 message = ui.edit(message, ui.username())
1592 opts['msg'] = message
1592 opts['msg'] = message
1593 q.new(repo, patch, *args, **opts)
1593 q.new(repo, patch, *args, **opts)
1594 q.save_dirty()
1594 q.save_dirty()
1595 return 0
1595 return 0
1596
1596
1597 def refresh(ui, repo, *pats, **opts):
1597 def refresh(ui, repo, *pats, **opts):
1598 """update the current patch
1598 """update the current patch
1599
1599
1600 If any file patterns are provided, the refreshed patch will contain only
1600 If any file patterns are provided, the refreshed patch will contain only
1601 the modifications that match those patterns; the remaining modifications
1601 the modifications that match those patterns; the remaining modifications
1602 will remain in the working directory.
1602 will remain in the working directory.
1603
1603
1604 hg add/remove/copy/rename work as usual, though you might want to use
1604 hg add/remove/copy/rename work as usual, though you might want to use
1605 git-style patches (--git or [diff] git=1) to track copies and renames.
1605 git-style patches (--git or [diff] git=1) to track copies and renames.
1606 """
1606 """
1607 q = repo.mq
1607 q = repo.mq
1608 message = cmdutil.logmessage(opts)
1608 message = cmdutil.logmessage(opts)
1609 if opts['edit']:
1609 if opts['edit']:
1610 if not q.applied:
1611 ui.write(_("No patches applied\n"))
1612 return 1
1610 if message:
1613 if message:
1611 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1614 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1612 patch = q.applied[-1].name
1615 patch = q.applied[-1].name
1613 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1616 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1614 message = ui.edit('\n'.join(message), user or ui.username())
1617 message = ui.edit('\n'.join(message), user or ui.username())
1615 ret = q.refresh(repo, pats, msg=message, **opts)
1618 ret = q.refresh(repo, pats, msg=message, **opts)
1616 q.save_dirty()
1619 q.save_dirty()
1617 return ret
1620 return ret
1618
1621
1619 def diff(ui, repo, *pats, **opts):
1622 def diff(ui, repo, *pats, **opts):
1620 """diff of the current patch"""
1623 """diff of the current patch"""
1621 repo.mq.diff(repo, pats, opts)
1624 repo.mq.diff(repo, pats, opts)
1622 return 0
1625 return 0
1623
1626
1624 def fold(ui, repo, *files, **opts):
1627 def fold(ui, repo, *files, **opts):
1625 """fold the named patches into the current patch
1628 """fold the named patches into the current patch
1626
1629
1627 Patches must not yet be applied. Each patch will be successively
1630 Patches must not yet be applied. Each patch will be successively
1628 applied to the current patch in the order given. If all the
1631 applied to the current patch in the order given. If all the
1629 patches apply successfully, the current patch will be refreshed
1632 patches apply successfully, the current patch will be refreshed
1630 with the new cumulative patch, and the folded patches will
1633 with the new cumulative patch, and the folded patches will
1631 be deleted. With -k/--keep, the folded patch files will not
1634 be deleted. With -k/--keep, the folded patch files will not
1632 be removed afterwards.
1635 be removed afterwards.
1633
1636
1634 The header for each folded patch will be concatenated with
1637 The header for each folded patch will be concatenated with
1635 the current patch header, separated by a line of '* * *'."""
1638 the current patch header, separated by a line of '* * *'."""
1636
1639
1637 q = repo.mq
1640 q = repo.mq
1638
1641
1639 if not files:
1642 if not files:
1640 raise util.Abort(_('qfold requires at least one patch name'))
1643 raise util.Abort(_('qfold requires at least one patch name'))
1641 if not q.check_toppatch(repo):
1644 if not q.check_toppatch(repo):
1642 raise util.Abort(_('No patches applied'))
1645 raise util.Abort(_('No patches applied'))
1643
1646
1644 message = cmdutil.logmessage(opts)
1647 message = cmdutil.logmessage(opts)
1645 if opts['edit']:
1648 if opts['edit']:
1646 if message:
1649 if message:
1647 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1650 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1648
1651
1649 parent = q.lookup('qtip')
1652 parent = q.lookup('qtip')
1650 patches = []
1653 patches = []
1651 messages = []
1654 messages = []
1652 for f in files:
1655 for f in files:
1653 p = q.lookup(f)
1656 p = q.lookup(f)
1654 if p in patches or p == parent:
1657 if p in patches or p == parent:
1655 ui.warn(_('Skipping already folded patch %s') % p)
1658 ui.warn(_('Skipping already folded patch %s') % p)
1656 if q.isapplied(p):
1659 if q.isapplied(p):
1657 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1660 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1658 patches.append(p)
1661 patches.append(p)
1659
1662
1660 for p in patches:
1663 for p in patches:
1661 if not message:
1664 if not message:
1662 messages.append(q.readheaders(p)[0])
1665 messages.append(q.readheaders(p)[0])
1663 pf = q.join(p)
1666 pf = q.join(p)
1664 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1667 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1665 if not patchsuccess:
1668 if not patchsuccess:
1666 raise util.Abort(_('Error folding patch %s') % p)
1669 raise util.Abort(_('Error folding patch %s') % p)
1667 patch.updatedir(ui, repo, files)
1670 patch.updatedir(ui, repo, files)
1668
1671
1669 if not message:
1672 if not message:
1670 message, comments, user = q.readheaders(parent)[0:3]
1673 message, comments, user = q.readheaders(parent)[0:3]
1671 for msg in messages:
1674 for msg in messages:
1672 message.append('* * *')
1675 message.append('* * *')
1673 message.extend(msg)
1676 message.extend(msg)
1674 message = '\n'.join(message)
1677 message = '\n'.join(message)
1675
1678
1676 if opts['edit']:
1679 if opts['edit']:
1677 message = ui.edit(message, user or ui.username())
1680 message = ui.edit(message, user or ui.username())
1678
1681
1679 q.refresh(repo, msg=message)
1682 q.refresh(repo, msg=message)
1680 q.delete(repo, patches, opts)
1683 q.delete(repo, patches, opts)
1681 q.save_dirty()
1684 q.save_dirty()
1682
1685
1683 def goto(ui, repo, patch, **opts):
1686 def goto(ui, repo, patch, **opts):
1684 '''push or pop patches until named patch is at top of stack'''
1687 '''push or pop patches until named patch is at top of stack'''
1685 q = repo.mq
1688 q = repo.mq
1686 patch = q.lookup(patch)
1689 patch = q.lookup(patch)
1687 if q.isapplied(patch):
1690 if q.isapplied(patch):
1688 ret = q.pop(repo, patch, force=opts['force'])
1691 ret = q.pop(repo, patch, force=opts['force'])
1689 else:
1692 else:
1690 ret = q.push(repo, patch, force=opts['force'])
1693 ret = q.push(repo, patch, force=opts['force'])
1691 q.save_dirty()
1694 q.save_dirty()
1692 return ret
1695 return ret
1693
1696
1694 def guard(ui, repo, *args, **opts):
1697 def guard(ui, repo, *args, **opts):
1695 '''set or print guards for a patch
1698 '''set or print guards for a patch
1696
1699
1697 Guards control whether a patch can be pushed. A patch with no
1700 Guards control whether a patch can be pushed. A patch with no
1698 guards is always pushed. A patch with a positive guard ("+foo") is
1701 guards is always pushed. A patch with a positive guard ("+foo") is
1699 pushed only if the qselect command has activated it. A patch with
1702 pushed only if the qselect command has activated it. A patch with
1700 a negative guard ("-foo") is never pushed if the qselect command
1703 a negative guard ("-foo") is never pushed if the qselect command
1701 has activated it.
1704 has activated it.
1702
1705
1703 With no arguments, print the currently active guards.
1706 With no arguments, print the currently active guards.
1704 With arguments, set guards for the named patch.
1707 With arguments, set guards for the named patch.
1705
1708
1706 To set a negative guard "-foo" on topmost patch ("--" is needed so
1709 To set a negative guard "-foo" on topmost patch ("--" is needed so
1707 hg will not interpret "-foo" as an option):
1710 hg will not interpret "-foo" as an option):
1708 hg qguard -- -foo
1711 hg qguard -- -foo
1709
1712
1710 To set guards on another patch:
1713 To set guards on another patch:
1711 hg qguard other.patch +2.6.17 -stable
1714 hg qguard other.patch +2.6.17 -stable
1712 '''
1715 '''
1713 def status(idx):
1716 def status(idx):
1714 guards = q.series_guards[idx] or ['unguarded']
1717 guards = q.series_guards[idx] or ['unguarded']
1715 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1718 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1716 q = repo.mq
1719 q = repo.mq
1717 patch = None
1720 patch = None
1718 args = list(args)
1721 args = list(args)
1719 if opts['list']:
1722 if opts['list']:
1720 if args or opts['none']:
1723 if args or opts['none']:
1721 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1724 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1722 for i in xrange(len(q.series)):
1725 for i in xrange(len(q.series)):
1723 status(i)
1726 status(i)
1724 return
1727 return
1725 if not args or args[0][0:1] in '-+':
1728 if not args or args[0][0:1] in '-+':
1726 if not q.applied:
1729 if not q.applied:
1727 raise util.Abort(_('no patches applied'))
1730 raise util.Abort(_('no patches applied'))
1728 patch = q.applied[-1].name
1731 patch = q.applied[-1].name
1729 if patch is None and args[0][0:1] not in '-+':
1732 if patch is None and args[0][0:1] not in '-+':
1730 patch = args.pop(0)
1733 patch = args.pop(0)
1731 if patch is None:
1734 if patch is None:
1732 raise util.Abort(_('no patch to work with'))
1735 raise util.Abort(_('no patch to work with'))
1733 if args or opts['none']:
1736 if args or opts['none']:
1734 idx = q.find_series(patch)
1737 idx = q.find_series(patch)
1735 if idx is None:
1738 if idx is None:
1736 raise util.Abort(_('no patch named %s') % patch)
1739 raise util.Abort(_('no patch named %s') % patch)
1737 q.set_guards(idx, args)
1740 q.set_guards(idx, args)
1738 q.save_dirty()
1741 q.save_dirty()
1739 else:
1742 else:
1740 status(q.series.index(q.lookup(patch)))
1743 status(q.series.index(q.lookup(patch)))
1741
1744
1742 def header(ui, repo, patch=None):
1745 def header(ui, repo, patch=None):
1743 """Print the header of the topmost or specified patch"""
1746 """Print the header of the topmost or specified patch"""
1744 q = repo.mq
1747 q = repo.mq
1745
1748
1746 if patch:
1749 if patch:
1747 patch = q.lookup(patch)
1750 patch = q.lookup(patch)
1748 else:
1751 else:
1749 if not q.applied:
1752 if not q.applied:
1750 ui.write('No patches applied\n')
1753 ui.write('No patches applied\n')
1751 return 1
1754 return 1
1752 patch = q.lookup('qtip')
1755 patch = q.lookup('qtip')
1753 message = repo.mq.readheaders(patch)[0]
1756 message = repo.mq.readheaders(patch)[0]
1754
1757
1755 ui.write('\n'.join(message) + '\n')
1758 ui.write('\n'.join(message) + '\n')
1756
1759
1757 def lastsavename(path):
1760 def lastsavename(path):
1758 (directory, base) = os.path.split(path)
1761 (directory, base) = os.path.split(path)
1759 names = os.listdir(directory)
1762 names = os.listdir(directory)
1760 namere = re.compile("%s.([0-9]+)" % base)
1763 namere = re.compile("%s.([0-9]+)" % base)
1761 maxindex = None
1764 maxindex = None
1762 maxname = None
1765 maxname = None
1763 for f in names:
1766 for f in names:
1764 m = namere.match(f)
1767 m = namere.match(f)
1765 if m:
1768 if m:
1766 index = int(m.group(1))
1769 index = int(m.group(1))
1767 if maxindex == None or index > maxindex:
1770 if maxindex == None or index > maxindex:
1768 maxindex = index
1771 maxindex = index
1769 maxname = f
1772 maxname = f
1770 if maxname:
1773 if maxname:
1771 return (os.path.join(directory, maxname), maxindex)
1774 return (os.path.join(directory, maxname), maxindex)
1772 return (None, None)
1775 return (None, None)
1773
1776
1774 def savename(path):
1777 def savename(path):
1775 (last, index) = lastsavename(path)
1778 (last, index) = lastsavename(path)
1776 if last is None:
1779 if last is None:
1777 index = 0
1780 index = 0
1778 newpath = path + ".%d" % (index + 1)
1781 newpath = path + ".%d" % (index + 1)
1779 return newpath
1782 return newpath
1780
1783
1781 def push(ui, repo, patch=None, **opts):
1784 def push(ui, repo, patch=None, **opts):
1782 """push the next patch onto the stack"""
1785 """push the next patch onto the stack"""
1783 q = repo.mq
1786 q = repo.mq
1784 mergeq = None
1787 mergeq = None
1785
1788
1786 if opts['all']:
1789 if opts['all']:
1787 if not q.series:
1790 if not q.series:
1788 ui.warn(_('no patches in series\n'))
1791 ui.warn(_('no patches in series\n'))
1789 return 0
1792 return 0
1790 patch = q.series[-1]
1793 patch = q.series[-1]
1791 if opts['merge']:
1794 if opts['merge']:
1792 if opts['name']:
1795 if opts['name']:
1793 newpath = opts['name']
1796 newpath = opts['name']
1794 else:
1797 else:
1795 newpath, i = lastsavename(q.path)
1798 newpath, i = lastsavename(q.path)
1796 if not newpath:
1799 if not newpath:
1797 ui.warn("no saved queues found, please use -n\n")
1800 ui.warn("no saved queues found, please use -n\n")
1798 return 1
1801 return 1
1799 mergeq = queue(ui, repo.join(""), newpath)
1802 mergeq = queue(ui, repo.join(""), newpath)
1800 ui.warn("merging with queue at: %s\n" % mergeq.path)
1803 ui.warn("merging with queue at: %s\n" % mergeq.path)
1801 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1804 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1802 mergeq=mergeq)
1805 mergeq=mergeq)
1803 return ret
1806 return ret
1804
1807
1805 def pop(ui, repo, patch=None, **opts):
1808 def pop(ui, repo, patch=None, **opts):
1806 """pop the current patch off the stack"""
1809 """pop the current patch off the stack"""
1807 localupdate = True
1810 localupdate = True
1808 if opts['name']:
1811 if opts['name']:
1809 q = queue(ui, repo.join(""), repo.join(opts['name']))
1812 q = queue(ui, repo.join(""), repo.join(opts['name']))
1810 ui.warn('using patch queue: %s\n' % q.path)
1813 ui.warn('using patch queue: %s\n' % q.path)
1811 localupdate = False
1814 localupdate = False
1812 else:
1815 else:
1813 q = repo.mq
1816 q = repo.mq
1814 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1817 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1815 all=opts['all'])
1818 all=opts['all'])
1816 q.save_dirty()
1819 q.save_dirty()
1817 return ret
1820 return ret
1818
1821
1819 def rename(ui, repo, patch, name=None, **opts):
1822 def rename(ui, repo, patch, name=None, **opts):
1820 """rename a patch
1823 """rename a patch
1821
1824
1822 With one argument, renames the current patch to PATCH1.
1825 With one argument, renames the current patch to PATCH1.
1823 With two arguments, renames PATCH1 to PATCH2."""
1826 With two arguments, renames PATCH1 to PATCH2."""
1824
1827
1825 q = repo.mq
1828 q = repo.mq
1826
1829
1827 if not name:
1830 if not name:
1828 name = patch
1831 name = patch
1829 patch = None
1832 patch = None
1830
1833
1831 if patch:
1834 if patch:
1832 patch = q.lookup(patch)
1835 patch = q.lookup(patch)
1833 else:
1836 else:
1834 if not q.applied:
1837 if not q.applied:
1835 ui.write(_('No patches applied\n'))
1838 ui.write(_('No patches applied\n'))
1836 return
1839 return
1837 patch = q.lookup('qtip')
1840 patch = q.lookup('qtip')
1838 absdest = q.join(name)
1841 absdest = q.join(name)
1839 if os.path.isdir(absdest):
1842 if os.path.isdir(absdest):
1840 name = normname(os.path.join(name, os.path.basename(patch)))
1843 name = normname(os.path.join(name, os.path.basename(patch)))
1841 absdest = q.join(name)
1844 absdest = q.join(name)
1842 if os.path.exists(absdest):
1845 if os.path.exists(absdest):
1843 raise util.Abort(_('%s already exists') % absdest)
1846 raise util.Abort(_('%s already exists') % absdest)
1844
1847
1845 if name in q.series:
1848 if name in q.series:
1846 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1849 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1847
1850
1848 if ui.verbose:
1851 if ui.verbose:
1849 ui.write('Renaming %s to %s\n' % (patch, name))
1852 ui.write('Renaming %s to %s\n' % (patch, name))
1850 i = q.find_series(patch)
1853 i = q.find_series(patch)
1851 guards = q.guard_re.findall(q.full_series[i])
1854 guards = q.guard_re.findall(q.full_series[i])
1852 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1855 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1853 q.parse_series()
1856 q.parse_series()
1854 q.series_dirty = 1
1857 q.series_dirty = 1
1855
1858
1856 info = q.isapplied(patch)
1859 info = q.isapplied(patch)
1857 if info:
1860 if info:
1858 q.applied[info[0]] = statusentry(info[1], name)
1861 q.applied[info[0]] = statusentry(info[1], name)
1859 q.applied_dirty = 1
1862 q.applied_dirty = 1
1860
1863
1861 util.rename(q.join(patch), absdest)
1864 util.rename(q.join(patch), absdest)
1862 r = q.qrepo()
1865 r = q.qrepo()
1863 if r:
1866 if r:
1864 wlock = r.wlock()
1867 wlock = r.wlock()
1865 if r.dirstate.state(name) == 'r':
1868 if r.dirstate.state(name) == 'r':
1866 r.undelete([name], wlock)
1869 r.undelete([name], wlock)
1867 r.copy(patch, name, wlock)
1870 r.copy(patch, name, wlock)
1868 r.remove([patch], False, wlock)
1871 r.remove([patch], False, wlock)
1869
1872
1870 q.save_dirty()
1873 q.save_dirty()
1871
1874
1872 def restore(ui, repo, rev, **opts):
1875 def restore(ui, repo, rev, **opts):
1873 """restore the queue state saved by a rev"""
1876 """restore the queue state saved by a rev"""
1874 rev = repo.lookup(rev)
1877 rev = repo.lookup(rev)
1875 q = repo.mq
1878 q = repo.mq
1876 q.restore(repo, rev, delete=opts['delete'],
1879 q.restore(repo, rev, delete=opts['delete'],
1877 qupdate=opts['update'])
1880 qupdate=opts['update'])
1878 q.save_dirty()
1881 q.save_dirty()
1879 return 0
1882 return 0
1880
1883
1881 def save(ui, repo, **opts):
1884 def save(ui, repo, **opts):
1882 """save current queue state"""
1885 """save current queue state"""
1883 q = repo.mq
1886 q = repo.mq
1884 message = cmdutil.logmessage(opts)
1887 message = cmdutil.logmessage(opts)
1885 ret = q.save(repo, msg=message)
1888 ret = q.save(repo, msg=message)
1886 if ret:
1889 if ret:
1887 return ret
1890 return ret
1888 q.save_dirty()
1891 q.save_dirty()
1889 if opts['copy']:
1892 if opts['copy']:
1890 path = q.path
1893 path = q.path
1891 if opts['name']:
1894 if opts['name']:
1892 newpath = os.path.join(q.basepath, opts['name'])
1895 newpath = os.path.join(q.basepath, opts['name'])
1893 if os.path.exists(newpath):
1896 if os.path.exists(newpath):
1894 if not os.path.isdir(newpath):
1897 if not os.path.isdir(newpath):
1895 raise util.Abort(_('destination %s exists and is not '
1898 raise util.Abort(_('destination %s exists and is not '
1896 'a directory') % newpath)
1899 'a directory') % newpath)
1897 if not opts['force']:
1900 if not opts['force']:
1898 raise util.Abort(_('destination %s exists, '
1901 raise util.Abort(_('destination %s exists, '
1899 'use -f to force') % newpath)
1902 'use -f to force') % newpath)
1900 else:
1903 else:
1901 newpath = savename(path)
1904 newpath = savename(path)
1902 ui.warn("copy %s to %s\n" % (path, newpath))
1905 ui.warn("copy %s to %s\n" % (path, newpath))
1903 util.copyfiles(path, newpath)
1906 util.copyfiles(path, newpath)
1904 if opts['empty']:
1907 if opts['empty']:
1905 try:
1908 try:
1906 os.unlink(q.join(q.status_path))
1909 os.unlink(q.join(q.status_path))
1907 except:
1910 except:
1908 pass
1911 pass
1909 return 0
1912 return 0
1910
1913
1911 def strip(ui, repo, rev, **opts):
1914 def strip(ui, repo, rev, **opts):
1912 """strip a revision and all later revs on the same branch"""
1915 """strip a revision and all later revs on the same branch"""
1913 rev = repo.lookup(rev)
1916 rev = repo.lookup(rev)
1914 backup = 'all'
1917 backup = 'all'
1915 if opts['backup']:
1918 if opts['backup']:
1916 backup = 'strip'
1919 backup = 'strip'
1917 elif opts['nobackup']:
1920 elif opts['nobackup']:
1918 backup = 'none'
1921 backup = 'none'
1919 update = repo.dirstate.parents()[0] != revlog.nullid
1922 update = repo.dirstate.parents()[0] != revlog.nullid
1920 repo.mq.strip(repo, rev, backup=backup, update=update)
1923 repo.mq.strip(repo, rev, backup=backup, update=update)
1921 return 0
1924 return 0
1922
1925
1923 def select(ui, repo, *args, **opts):
1926 def select(ui, repo, *args, **opts):
1924 '''set or print guarded patches to push
1927 '''set or print guarded patches to push
1925
1928
1926 Use the qguard command to set or print guards on patch, then use
1929 Use the qguard command to set or print guards on patch, then use
1927 qselect to tell mq which guards to use. A patch will be pushed if it
1930 qselect to tell mq which guards to use. A patch will be pushed if it
1928 has no guards or any positive guards match the currently selected guard,
1931 has no guards or any positive guards match the currently selected guard,
1929 but will not be pushed if any negative guards match the current guard.
1932 but will not be pushed if any negative guards match the current guard.
1930 For example:
1933 For example:
1931
1934
1932 qguard foo.patch -stable (negative guard)
1935 qguard foo.patch -stable (negative guard)
1933 qguard bar.patch +stable (positive guard)
1936 qguard bar.patch +stable (positive guard)
1934 qselect stable
1937 qselect stable
1935
1938
1936 This activates the "stable" guard. mq will skip foo.patch (because
1939 This activates the "stable" guard. mq will skip foo.patch (because
1937 it has a negative match) but push bar.patch (because it
1940 it has a negative match) but push bar.patch (because it
1938 has a positive match).
1941 has a positive match).
1939
1942
1940 With no arguments, prints the currently active guards.
1943 With no arguments, prints the currently active guards.
1941 With one argument, sets the active guard.
1944 With one argument, sets the active guard.
1942
1945
1943 Use -n/--none to deactivate guards (no other arguments needed).
1946 Use -n/--none to deactivate guards (no other arguments needed).
1944 When no guards are active, patches with positive guards are skipped
1947 When no guards are active, patches with positive guards are skipped
1945 and patches with negative guards are pushed.
1948 and patches with negative guards are pushed.
1946
1949
1947 qselect can change the guards on applied patches. It does not pop
1950 qselect can change the guards on applied patches. It does not pop
1948 guarded patches by default. Use --pop to pop back to the last applied
1951 guarded patches by default. Use --pop to pop back to the last applied
1949 patch that is not guarded. Use --reapply (which implies --pop) to push
1952 patch that is not guarded. Use --reapply (which implies --pop) to push
1950 back to the current patch afterwards, but skip guarded patches.
1953 back to the current patch afterwards, but skip guarded patches.
1951
1954
1952 Use -s/--series to print a list of all guards in the series file (no
1955 Use -s/--series to print a list of all guards in the series file (no
1953 other arguments needed). Use -v for more information.'''
1956 other arguments needed). Use -v for more information.'''
1954
1957
1955 q = repo.mq
1958 q = repo.mq
1956 guards = q.active()
1959 guards = q.active()
1957 if args or opts['none']:
1960 if args or opts['none']:
1958 old_unapplied = q.unapplied(repo)
1961 old_unapplied = q.unapplied(repo)
1959 old_guarded = [i for i in xrange(len(q.applied)) if
1962 old_guarded = [i for i in xrange(len(q.applied)) if
1960 not q.pushable(i)[0]]
1963 not q.pushable(i)[0]]
1961 q.set_active(args)
1964 q.set_active(args)
1962 q.save_dirty()
1965 q.save_dirty()
1963 if not args:
1966 if not args:
1964 ui.status(_('guards deactivated\n'))
1967 ui.status(_('guards deactivated\n'))
1965 if not opts['pop'] and not opts['reapply']:
1968 if not opts['pop'] and not opts['reapply']:
1966 unapplied = q.unapplied(repo)
1969 unapplied = q.unapplied(repo)
1967 guarded = [i for i in xrange(len(q.applied))
1970 guarded = [i for i in xrange(len(q.applied))
1968 if not q.pushable(i)[0]]
1971 if not q.pushable(i)[0]]
1969 if len(unapplied) != len(old_unapplied):
1972 if len(unapplied) != len(old_unapplied):
1970 ui.status(_('number of unguarded, unapplied patches has '
1973 ui.status(_('number of unguarded, unapplied patches has '
1971 'changed from %d to %d\n') %
1974 'changed from %d to %d\n') %
1972 (len(old_unapplied), len(unapplied)))
1975 (len(old_unapplied), len(unapplied)))
1973 if len(guarded) != len(old_guarded):
1976 if len(guarded) != len(old_guarded):
1974 ui.status(_('number of guarded, applied patches has changed '
1977 ui.status(_('number of guarded, applied patches has changed '
1975 'from %d to %d\n') %
1978 'from %d to %d\n') %
1976 (len(old_guarded), len(guarded)))
1979 (len(old_guarded), len(guarded)))
1977 elif opts['series']:
1980 elif opts['series']:
1978 guards = {}
1981 guards = {}
1979 noguards = 0
1982 noguards = 0
1980 for gs in q.series_guards:
1983 for gs in q.series_guards:
1981 if not gs:
1984 if not gs:
1982 noguards += 1
1985 noguards += 1
1983 for g in gs:
1986 for g in gs:
1984 guards.setdefault(g, 0)
1987 guards.setdefault(g, 0)
1985 guards[g] += 1
1988 guards[g] += 1
1986 if ui.verbose:
1989 if ui.verbose:
1987 guards['NONE'] = noguards
1990 guards['NONE'] = noguards
1988 guards = guards.items()
1991 guards = guards.items()
1989 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1992 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1990 if guards:
1993 if guards:
1991 ui.note(_('guards in series file:\n'))
1994 ui.note(_('guards in series file:\n'))
1992 for guard, count in guards:
1995 for guard, count in guards:
1993 ui.note('%2d ' % count)
1996 ui.note('%2d ' % count)
1994 ui.write(guard, '\n')
1997 ui.write(guard, '\n')
1995 else:
1998 else:
1996 ui.note(_('no guards in series file\n'))
1999 ui.note(_('no guards in series file\n'))
1997 else:
2000 else:
1998 if guards:
2001 if guards:
1999 ui.note(_('active guards:\n'))
2002 ui.note(_('active guards:\n'))
2000 for g in guards:
2003 for g in guards:
2001 ui.write(g, '\n')
2004 ui.write(g, '\n')
2002 else:
2005 else:
2003 ui.write(_('no active guards\n'))
2006 ui.write(_('no active guards\n'))
2004 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2007 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2005 popped = False
2008 popped = False
2006 if opts['pop'] or opts['reapply']:
2009 if opts['pop'] or opts['reapply']:
2007 for i in xrange(len(q.applied)):
2010 for i in xrange(len(q.applied)):
2008 pushable, reason = q.pushable(i)
2011 pushable, reason = q.pushable(i)
2009 if not pushable:
2012 if not pushable:
2010 ui.status(_('popping guarded patches\n'))
2013 ui.status(_('popping guarded patches\n'))
2011 popped = True
2014 popped = True
2012 if i == 0:
2015 if i == 0:
2013 q.pop(repo, all=True)
2016 q.pop(repo, all=True)
2014 else:
2017 else:
2015 q.pop(repo, i-1)
2018 q.pop(repo, i-1)
2016 break
2019 break
2017 if popped:
2020 if popped:
2018 try:
2021 try:
2019 if reapply:
2022 if reapply:
2020 ui.status(_('reapplying unguarded patches\n'))
2023 ui.status(_('reapplying unguarded patches\n'))
2021 q.push(repo, reapply)
2024 q.push(repo, reapply)
2022 finally:
2025 finally:
2023 q.save_dirty()
2026 q.save_dirty()
2024
2027
2025 def reposetup(ui, repo):
2028 def reposetup(ui, repo):
2026 class mqrepo(repo.__class__):
2029 class mqrepo(repo.__class__):
2027 def abort_if_wdir_patched(self, errmsg, force=False):
2030 def abort_if_wdir_patched(self, errmsg, force=False):
2028 if self.mq.applied and not force:
2031 if self.mq.applied and not force:
2029 parent = revlog.hex(self.dirstate.parents()[0])
2032 parent = revlog.hex(self.dirstate.parents()[0])
2030 if parent in [s.rev for s in self.mq.applied]:
2033 if parent in [s.rev for s in self.mq.applied]:
2031 raise util.Abort(errmsg)
2034 raise util.Abort(errmsg)
2032
2035
2033 def commit(self, *args, **opts):
2036 def commit(self, *args, **opts):
2034 if len(args) >= 6:
2037 if len(args) >= 6:
2035 force = args[5]
2038 force = args[5]
2036 else:
2039 else:
2037 force = opts.get('force')
2040 force = opts.get('force')
2038 self.abort_if_wdir_patched(
2041 self.abort_if_wdir_patched(
2039 _('cannot commit over an applied mq patch'),
2042 _('cannot commit over an applied mq patch'),
2040 force)
2043 force)
2041
2044
2042 return super(mqrepo, self).commit(*args, **opts)
2045 return super(mqrepo, self).commit(*args, **opts)
2043
2046
2044 def push(self, remote, force=False, revs=None):
2047 def push(self, remote, force=False, revs=None):
2045 if self.mq.applied and not force and not revs:
2048 if self.mq.applied and not force and not revs:
2046 raise util.Abort(_('source has mq patches applied'))
2049 raise util.Abort(_('source has mq patches applied'))
2047 return super(mqrepo, self).push(remote, force, revs)
2050 return super(mqrepo, self).push(remote, force, revs)
2048
2051
2049 def tags(self):
2052 def tags(self):
2050 if self.tagscache:
2053 if self.tagscache:
2051 return self.tagscache
2054 return self.tagscache
2052
2055
2053 tagscache = super(mqrepo, self).tags()
2056 tagscache = super(mqrepo, self).tags()
2054
2057
2055 q = self.mq
2058 q = self.mq
2056 if not q.applied:
2059 if not q.applied:
2057 return tagscache
2060 return tagscache
2058
2061
2059 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2062 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2060 mqtags.append((mqtags[-1][0], 'qtip'))
2063 mqtags.append((mqtags[-1][0], 'qtip'))
2061 mqtags.append((mqtags[0][0], 'qbase'))
2064 mqtags.append((mqtags[0][0], 'qbase'))
2062 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2065 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2063 for patch in mqtags:
2066 for patch in mqtags:
2064 if patch[1] in tagscache:
2067 if patch[1] in tagscache:
2065 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2068 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2066 else:
2069 else:
2067 tagscache[patch[1]] = patch[0]
2070 tagscache[patch[1]] = patch[0]
2068
2071
2069 return tagscache
2072 return tagscache
2070
2073
2071 def _branchtags(self):
2074 def _branchtags(self):
2072 q = self.mq
2075 q = self.mq
2073 if not q.applied:
2076 if not q.applied:
2074 return super(mqrepo, self)._branchtags()
2077 return super(mqrepo, self)._branchtags()
2075
2078
2076 self.branchcache = {} # avoid recursion in changectx
2079 self.branchcache = {} # avoid recursion in changectx
2077 cl = self.changelog
2080 cl = self.changelog
2078 partial, last, lrev = self._readbranchcache()
2081 partial, last, lrev = self._readbranchcache()
2079
2082
2080 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2083 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2081 start = lrev + 1
2084 start = lrev + 1
2082 if start < qbase:
2085 if start < qbase:
2083 # update the cache (excluding the patches) and save it
2086 # update the cache (excluding the patches) and save it
2084 self._updatebranchcache(partial, lrev+1, qbase)
2087 self._updatebranchcache(partial, lrev+1, qbase)
2085 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2088 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2086 start = qbase
2089 start = qbase
2087 # if start = qbase, the cache is as updated as it should be.
2090 # if start = qbase, the cache is as updated as it should be.
2088 # if start > qbase, the cache includes (part of) the patches.
2091 # if start > qbase, the cache includes (part of) the patches.
2089 # we might as well use it, but we won't save it.
2092 # we might as well use it, but we won't save it.
2090
2093
2091 # update the cache up to the tip
2094 # update the cache up to the tip
2092 self._updatebranchcache(partial, start, cl.count())
2095 self._updatebranchcache(partial, start, cl.count())
2093
2096
2094 return partial
2097 return partial
2095
2098
2096 if repo.local():
2099 if repo.local():
2097 repo.__class__ = mqrepo
2100 repo.__class__ = mqrepo
2098 repo.mq = queue(ui, repo.join(""))
2101 repo.mq = queue(ui, repo.join(""))
2099
2102
2100 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2103 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2101
2104
2102 cmdtable = {
2105 cmdtable = {
2103 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2106 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2104 "qclone":
2107 "qclone":
2105 (clone,
2108 (clone,
2106 [('', 'pull', None, _('use pull protocol to copy metadata')),
2109 [('', 'pull', None, _('use pull protocol to copy metadata')),
2107 ('U', 'noupdate', None, _('do not update the new working directories')),
2110 ('U', 'noupdate', None, _('do not update the new working directories')),
2108 ('', 'uncompressed', None,
2111 ('', 'uncompressed', None,
2109 _('use uncompressed transfer (fast over LAN)')),
2112 _('use uncompressed transfer (fast over LAN)')),
2110 ('e', 'ssh', '', _('specify ssh command to use')),
2113 ('e', 'ssh', '', _('specify ssh command to use')),
2111 ('p', 'patches', '', _('location of source patch repo')),
2114 ('p', 'patches', '', _('location of source patch repo')),
2112 ('', 'remotecmd', '',
2115 ('', 'remotecmd', '',
2113 _('specify hg command to run on the remote side'))],
2116 _('specify hg command to run on the remote side'))],
2114 _('hg qclone [OPTION]... SOURCE [DEST]')),
2117 _('hg qclone [OPTION]... SOURCE [DEST]')),
2115 "qcommit|qci":
2118 "qcommit|qci":
2116 (commit,
2119 (commit,
2117 commands.table["^commit|ci"][1],
2120 commands.table["^commit|ci"][1],
2118 _('hg qcommit [OPTION]... [FILE]...')),
2121 _('hg qcommit [OPTION]... [FILE]...')),
2119 "^qdiff":
2122 "^qdiff":
2120 (diff,
2123 (diff,
2121 [('g', 'git', None, _('use git extended diff format')),
2124 [('g', 'git', None, _('use git extended diff format')),
2122 ('I', 'include', [], _('include names matching the given patterns')),
2125 ('I', 'include', [], _('include names matching the given patterns')),
2123 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2126 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2124 _('hg qdiff [-I] [-X] [-g] [FILE]...')),
2127 _('hg qdiff [-I] [-X] [-g] [FILE]...')),
2125 "qdelete|qremove|qrm":
2128 "qdelete|qremove|qrm":
2126 (delete,
2129 (delete,
2127 [('k', 'keep', None, _('keep patch file')),
2130 [('k', 'keep', None, _('keep patch file')),
2128 ('r', 'rev', [], _('stop managing a revision'))],
2131 ('r', 'rev', [], _('stop managing a revision'))],
2129 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2132 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2130 'qfold':
2133 'qfold':
2131 (fold,
2134 (fold,
2132 [('e', 'edit', None, _('edit patch header')),
2135 [('e', 'edit', None, _('edit patch header')),
2133 ('k', 'keep', None, _('keep folded patch files')),
2136 ('k', 'keep', None, _('keep folded patch files')),
2134 ] + commands.commitopts,
2137 ] + commands.commitopts,
2135 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2138 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2136 'qgoto':
2139 'qgoto':
2137 (goto,
2140 (goto,
2138 [('f', 'force', None, _('overwrite any local changes'))],
2141 [('f', 'force', None, _('overwrite any local changes'))],
2139 _('hg qgoto [OPTION]... PATCH')),
2142 _('hg qgoto [OPTION]... PATCH')),
2140 'qguard':
2143 'qguard':
2141 (guard,
2144 (guard,
2142 [('l', 'list', None, _('list all patches and guards')),
2145 [('l', 'list', None, _('list all patches and guards')),
2143 ('n', 'none', None, _('drop all guards'))],
2146 ('n', 'none', None, _('drop all guards'))],
2144 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2147 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2145 'qheader': (header, [], _('hg qheader [PATCH]')),
2148 'qheader': (header, [], _('hg qheader [PATCH]')),
2146 "^qimport":
2149 "^qimport":
2147 (qimport,
2150 (qimport,
2148 [('e', 'existing', None, 'import file in patch dir'),
2151 [('e', 'existing', None, 'import file in patch dir'),
2149 ('n', 'name', '', 'patch file name'),
2152 ('n', 'name', '', 'patch file name'),
2150 ('f', 'force', None, 'overwrite existing files'),
2153 ('f', 'force', None, 'overwrite existing files'),
2151 ('r', 'rev', [], 'place existing revisions under mq control'),
2154 ('r', 'rev', [], 'place existing revisions under mq control'),
2152 ('g', 'git', None, _('use git extended diff format'))],
2155 ('g', 'git', None, _('use git extended diff format'))],
2153 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2156 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2154 "^qinit":
2157 "^qinit":
2155 (init,
2158 (init,
2156 [('c', 'create-repo', None, 'create queue repository')],
2159 [('c', 'create-repo', None, 'create queue repository')],
2157 _('hg qinit [-c]')),
2160 _('hg qinit [-c]')),
2158 "qnew":
2161 "qnew":
2159 (new,
2162 (new,
2160 [('e', 'edit', None, _('edit commit message')),
2163 [('e', 'edit', None, _('edit commit message')),
2161 ('f', 'force', None, _('import uncommitted changes into patch')),
2164 ('f', 'force', None, _('import uncommitted changes into patch')),
2162 ('I', 'include', [], _('include names matching the given patterns')),
2165 ('I', 'include', [], _('include names matching the given patterns')),
2163 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2166 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2164 ] + commands.commitopts,
2167 ] + commands.commitopts,
2165 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2168 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2166 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2169 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2167 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2170 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2168 "^qpop":
2171 "^qpop":
2169 (pop,
2172 (pop,
2170 [('a', 'all', None, _('pop all patches')),
2173 [('a', 'all', None, _('pop all patches')),
2171 ('n', 'name', '', _('queue name to pop')),
2174 ('n', 'name', '', _('queue name to pop')),
2172 ('f', 'force', None, _('forget any local changes'))],
2175 ('f', 'force', None, _('forget any local changes'))],
2173 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2176 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2174 "^qpush":
2177 "^qpush":
2175 (push,
2178 (push,
2176 [('f', 'force', None, _('apply if the patch has rejects')),
2179 [('f', 'force', None, _('apply if the patch has rejects')),
2177 ('l', 'list', None, _('list patch name in commit text')),
2180 ('l', 'list', None, _('list patch name in commit text')),
2178 ('a', 'all', None, _('apply all patches')),
2181 ('a', 'all', None, _('apply all patches')),
2179 ('m', 'merge', None, _('merge from another queue')),
2182 ('m', 'merge', None, _('merge from another queue')),
2180 ('n', 'name', '', _('merge queue name'))],
2183 ('n', 'name', '', _('merge queue name'))],
2181 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2184 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2182 "^qrefresh":
2185 "^qrefresh":
2183 (refresh,
2186 (refresh,
2184 [('e', 'edit', None, _('edit commit message')),
2187 [('e', 'edit', None, _('edit commit message')),
2185 ('g', 'git', None, _('use git extended diff format')),
2188 ('g', 'git', None, _('use git extended diff format')),
2186 ('s', 'short', None, _('refresh only files already in the patch')),
2189 ('s', 'short', None, _('refresh only files already in the patch')),
2187 ('I', 'include', [], _('include names matching the given patterns')),
2190 ('I', 'include', [], _('include names matching the given patterns')),
2188 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2191 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2189 ] + commands.commitopts,
2192 ] + commands.commitopts,
2190 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2193 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2191 'qrename|qmv':
2194 'qrename|qmv':
2192 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2195 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2193 "qrestore":
2196 "qrestore":
2194 (restore,
2197 (restore,
2195 [('d', 'delete', None, _('delete save entry')),
2198 [('d', 'delete', None, _('delete save entry')),
2196 ('u', 'update', None, _('update queue working dir'))],
2199 ('u', 'update', None, _('update queue working dir'))],
2197 _('hg qrestore [-d] [-u] REV')),
2200 _('hg qrestore [-d] [-u] REV')),
2198 "qsave":
2201 "qsave":
2199 (save,
2202 (save,
2200 [('c', 'copy', None, _('copy patch directory')),
2203 [('c', 'copy', None, _('copy patch directory')),
2201 ('n', 'name', '', _('copy directory name')),
2204 ('n', 'name', '', _('copy directory name')),
2202 ('e', 'empty', None, _('clear queue status file')),
2205 ('e', 'empty', None, _('clear queue status file')),
2203 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2206 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2204 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2207 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2205 "qselect":
2208 "qselect":
2206 (select,
2209 (select,
2207 [('n', 'none', None, _('disable all guards')),
2210 [('n', 'none', None, _('disable all guards')),
2208 ('s', 'series', None, _('list all guards in series file')),
2211 ('s', 'series', None, _('list all guards in series file')),
2209 ('', 'pop', None, _('pop to before first guarded applied patch')),
2212 ('', 'pop', None, _('pop to before first guarded applied patch')),
2210 ('', 'reapply', None, _('pop, then reapply patches'))],
2213 ('', 'reapply', None, _('pop, then reapply patches'))],
2211 _('hg qselect [OPTION]... [GUARD]...')),
2214 _('hg qselect [OPTION]... [GUARD]...')),
2212 "qseries":
2215 "qseries":
2213 (series,
2216 (series,
2214 [('m', 'missing', None, _('print patches not in series')),
2217 [('m', 'missing', None, _('print patches not in series')),
2215 ] + seriesopts,
2218 ] + seriesopts,
2216 _('hg qseries [-ms]')),
2219 _('hg qseries [-ms]')),
2217 "^strip":
2220 "^strip":
2218 (strip,
2221 (strip,
2219 [('f', 'force', None, _('force multi-head removal')),
2222 [('f', 'force', None, _('force multi-head removal')),
2220 ('b', 'backup', None, _('bundle unrelated changesets')),
2223 ('b', 'backup', None, _('bundle unrelated changesets')),
2221 ('n', 'nobackup', None, _('no backups'))],
2224 ('n', 'nobackup', None, _('no backups'))],
2222 _('hg strip [-f] [-b] [-n] REV')),
2225 _('hg strip [-f] [-b] [-n] REV')),
2223 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2226 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2224 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2227 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2225 }
2228 }
@@ -1,50 +1,55
1 #!/bin/sh
1 #!/bin/sh
2
2
3 # Environement setup for MQ
3 # Environement setup for MQ
4 echo "[extensions]" >> $HGRCPATH
4 echo "[extensions]" >> $HGRCPATH
5 echo "mq=" >> $HGRCPATH
5 echo "mq=" >> $HGRCPATH
6
6
7 #Repo init
7 #Repo init
8 hg init
8 hg init
9 hg qinit
9 hg qinit
10
10
11 echo =======================
12 echo "Should fail if no patches applied"
13 hg qrefresh
14 hg qrefresh -e
15
11 hg qnew -m "First commit message" first-patch
16 hg qnew -m "First commit message" first-patch
12 echo aaaa > file
17 echo aaaa > file
13 hg add file
18 hg add file
14 hg qrefresh
19 hg qrefresh
15 echo =======================
20 echo =======================
16 echo "Should display 'First commit message'"
21 echo "Should display 'First commit message'"
17 hg log -l1 -v | sed -n '/description/,$p'
22 hg log -l1 -v | sed -n '/description/,$p'
18 echo
23 echo
19
24
20 # Testing changing message with -m
25 # Testing changing message with -m
21 echo bbbb > file
26 echo bbbb > file
22 hg qrefresh -m "Second commit message"
27 hg qrefresh -m "Second commit message"
23 echo =======================
28 echo =======================
24 echo "Should display 'Second commit message'"
29 echo "Should display 'Second commit message'"
25 hg log -l1 -v | sed -n '/description/,$p'
30 hg log -l1 -v | sed -n '/description/,$p'
26 echo
31 echo
27
32
28
33
29 # Testing changing message with -l
34 # Testing changing message with -l
30 echo "Third commit message" > logfile
35 echo "Third commit message" > logfile
31 echo " This is the 3rd log message" >> logfile
36 echo " This is the 3rd log message" >> logfile
32 echo bbbb > file
37 echo bbbb > file
33 hg qrefresh -l logfile
38 hg qrefresh -l logfile
34 echo =======================
39 echo =======================
35 printf "Should display 'Third commit message\\\n This is the 3rd log message'\n"
40 printf "Should display 'Third commit message\\\n This is the 3rd log message'\n"
36 hg log -l1 -v | sed -n '/description/,$p'
41 hg log -l1 -v | sed -n '/description/,$p'
37 echo
42 echo
38
43
39 # Testing changing message with -l-
44 # Testing changing message with -l-
40 hg qnew -m "First commit message" second-patch
45 hg qnew -m "First commit message" second-patch
41 echo aaaa > file2
46 echo aaaa > file2
42 hg add file2
47 hg add file2
43 echo bbbb > file2
48 echo bbbb > file2
44 (echo "Fifth commit message"
49 (echo "Fifth commit message"
45 echo " This is the 5th log message" >> logfile) |\
50 echo " This is the 5th log message" >> logfile) |\
46 hg qrefresh -l-
51 hg qrefresh -l-
47 echo =======================
52 echo =======================
48 printf "Should display 'Fifth commit message\\\n This is the 5th log message'\n"
53 printf "Should display 'Fifth commit message\\\n This is the 5th log message'\n"
49 hg log -l1 -v | sed -n '/description/,$p'
54 hg log -l1 -v | sed -n '/description/,$p'
50 echo
55 echo
@@ -1,29 +1,33
1 =======================
2 Should fail if no patches applied
3 No patches applied
4 No patches applied
1 =======================
5 =======================
2 Should display 'First commit message'
6 Should display 'First commit message'
3 description:
7 description:
4 First commit message
8 First commit message
5
9
6
10
7
11
8 =======================
12 =======================
9 Should display 'Second commit message'
13 Should display 'Second commit message'
10 description:
14 description:
11 Second commit message
15 Second commit message
12
16
13
17
14
18
15 =======================
19 =======================
16 Should display 'Third commit message\n This is the 3rd log message'
20 Should display 'Third commit message\n This is the 3rd log message'
17 description:
21 description:
18 Third commit message
22 Third commit message
19 This is the 3rd log message
23 This is the 3rd log message
20
24
21
25
22
26
23 =======================
27 =======================
24 Should display 'Fifth commit message\n This is the 5th log message'
28 Should display 'Fifth commit message\n This is the 5th log message'
25 description:
29 description:
26 Fifth commit message
30 Fifth commit message
27
31
28
32
29
33
General Comments 0
You need to be logged in to leave comments. Login now