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