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