##// END OF EJS Templates
Change patch header as well as commit message with qrefresh -m or -l.
Brendan Cully -
r2745:1bac2bfe default
parent child Browse files
Show More
@@ -1,1500 +1,1510
1 # queue.py - patch queues for mercurial
1 # queue.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005 Chris Mason <mason@suse.com>
3 # Copyright 2005 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.demandload import *
32 from mercurial.demandload import *
33 demandload(globals(), "os sys re struct traceback errno bz2")
33 demandload(globals(), "os sys re struct traceback errno bz2")
34 from mercurial.i18n import gettext as _
34 from mercurial.i18n import gettext as _
35 from mercurial import ui, hg, revlog, commands, util
35 from mercurial import ui, hg, revlog, commands, util
36
36
37 versionstr = "0.45"
37 versionstr = "0.45"
38
38
39 commands.norepo += " qclone qversion"
39 commands.norepo += " qclone qversion"
40
40
41 class queue:
41 class queue:
42 def __init__(self, ui, path, patchdir=None):
42 def __init__(self, ui, path, patchdir=None):
43 self.basepath = path
43 self.basepath = path
44 if patchdir:
44 if patchdir:
45 self.path = patchdir
45 self.path = patchdir
46 else:
46 else:
47 self.path = os.path.join(path, "patches")
47 self.path = os.path.join(path, "patches")
48 self.opener = util.opener(self.path)
48 self.opener = util.opener(self.path)
49 self.ui = ui
49 self.ui = ui
50 self.applied = []
50 self.applied = []
51 self.full_series = []
51 self.full_series = []
52 self.applied_dirty = 0
52 self.applied_dirty = 0
53 self.series_dirty = 0
53 self.series_dirty = 0
54 self.series_path = "series"
54 self.series_path = "series"
55 self.status_path = "status"
55 self.status_path = "status"
56
56
57 if os.path.exists(os.path.join(self.path, self.series_path)):
57 if os.path.exists(os.path.join(self.path, self.series_path)):
58 self.full_series = self.opener(self.series_path).read().splitlines()
58 self.full_series = self.opener(self.series_path).read().splitlines()
59 self.read_series(self.full_series)
59 self.read_series(self.full_series)
60
60
61 if os.path.exists(os.path.join(self.path, self.status_path)):
61 if os.path.exists(os.path.join(self.path, self.status_path)):
62 self.applied = self.opener(self.status_path).read().splitlines()
62 self.applied = self.opener(self.status_path).read().splitlines()
63
63
64 def find_series(self, patch):
64 def find_series(self, patch):
65 pre = re.compile("(\s*)([^#]+)")
65 pre = re.compile("(\s*)([^#]+)")
66 index = 0
66 index = 0
67 for l in self.full_series:
67 for l in self.full_series:
68 m = pre.match(l)
68 m = pre.match(l)
69 if m:
69 if m:
70 s = m.group(2)
70 s = m.group(2)
71 s = s.rstrip()
71 s = s.rstrip()
72 if s == patch:
72 if s == patch:
73 return index
73 return index
74 index += 1
74 index += 1
75 return None
75 return None
76
76
77 def read_series(self, list):
77 def read_series(self, list):
78 def matcher(list):
78 def matcher(list):
79 pre = re.compile("(\s*)([^#]+)")
79 pre = re.compile("(\s*)([^#]+)")
80 for l in list:
80 for l in list:
81 m = pre.match(l)
81 m = pre.match(l)
82 if m:
82 if m:
83 s = m.group(2)
83 s = m.group(2)
84 s = s.rstrip()
84 s = s.rstrip()
85 if len(s) > 0:
85 if len(s) > 0:
86 yield s
86 yield s
87 self.series = []
87 self.series = []
88 self.series = [ x for x in matcher(list) ]
88 self.series = [ x for x in matcher(list) ]
89
89
90 def save_dirty(self):
90 def save_dirty(self):
91 if self.applied_dirty:
91 if self.applied_dirty:
92 if len(self.applied) > 0:
92 if len(self.applied) > 0:
93 nl = "\n"
93 nl = "\n"
94 else:
94 else:
95 nl = ""
95 nl = ""
96 f = self.opener(self.status_path, "w")
96 f = self.opener(self.status_path, "w")
97 f.write("\n".join(self.applied) + nl)
97 f.write("\n".join(self.applied) + nl)
98 if self.series_dirty:
98 if self.series_dirty:
99 if len(self.full_series) > 0:
99 if len(self.full_series) > 0:
100 nl = "\n"
100 nl = "\n"
101 else:
101 else:
102 nl = ""
102 nl = ""
103 f = self.opener(self.series_path, "w")
103 f = self.opener(self.series_path, "w")
104 f.write("\n".join(self.full_series) + nl)
104 f.write("\n".join(self.full_series) + nl)
105
105
106 def readheaders(self, patch):
106 def readheaders(self, patch):
107 def eatdiff(lines):
107 def eatdiff(lines):
108 while lines:
108 while lines:
109 l = lines[-1]
109 l = lines[-1]
110 if (l.startswith("diff -") or
110 if (l.startswith("diff -") or
111 l.startswith("Index:") or
111 l.startswith("Index:") or
112 l.startswith("===========")):
112 l.startswith("===========")):
113 del lines[-1]
113 del lines[-1]
114 else:
114 else:
115 break
115 break
116 def eatempty(lines):
116 def eatempty(lines):
117 while lines:
117 while lines:
118 l = lines[-1]
118 l = lines[-1]
119 if re.match('\s*$', l):
119 if re.match('\s*$', l):
120 del lines[-1]
120 del lines[-1]
121 else:
121 else:
122 break
122 break
123
123
124 pf = os.path.join(self.path, patch)
124 pf = os.path.join(self.path, patch)
125 message = []
125 message = []
126 comments = []
126 comments = []
127 user = None
127 user = None
128 date = None
128 date = None
129 format = None
129 format = None
130 subject = None
130 subject = None
131 diffstart = 0
131 diffstart = 0
132
132
133 for line in file(pf):
133 for line in file(pf):
134 line = line.rstrip()
134 line = line.rstrip()
135 if diffstart:
135 if diffstart:
136 if line.startswith('+++ '):
136 if line.startswith('+++ '):
137 diffstart = 2
137 diffstart = 2
138 break
138 break
139 if line.startswith("--- "):
139 if line.startswith("--- "):
140 diffstart = 1
140 diffstart = 1
141 continue
141 continue
142 elif format == "hgpatch":
142 elif format == "hgpatch":
143 # parse values when importing the result of an hg export
143 # parse values when importing the result of an hg export
144 if line.startswith("# User "):
144 if line.startswith("# User "):
145 user = line[7:]
145 user = line[7:]
146 elif line.startswith("# Date "):
146 elif line.startswith("# Date "):
147 date = line[7:]
147 date = line[7:]
148 elif not line.startswith("# ") and line:
148 elif not line.startswith("# ") and line:
149 message.append(line)
149 message.append(line)
150 format = None
150 format = None
151 elif line == '# HG changeset patch':
151 elif line == '# HG changeset patch':
152 format = "hgpatch"
152 format = "hgpatch"
153 elif (format != "tagdone" and (line.startswith("Subject: ") or
153 elif (format != "tagdone" and (line.startswith("Subject: ") or
154 line.startswith("subject: "))):
154 line.startswith("subject: "))):
155 subject = line[9:]
155 subject = line[9:]
156 format = "tag"
156 format = "tag"
157 elif (format != "tagdone" and (line.startswith("From: ") or
157 elif (format != "tagdone" and (line.startswith("From: ") or
158 line.startswith("from: "))):
158 line.startswith("from: "))):
159 user = line[6:]
159 user = line[6:]
160 format = "tag"
160 format = "tag"
161 elif format == "tag" and line == "":
161 elif format == "tag" and line == "":
162 # when looking for tags (subject: from: etc) they
162 # when looking for tags (subject: from: etc) they
163 # end once you find a blank line in the source
163 # end once you find a blank line in the source
164 format = "tagdone"
164 format = "tagdone"
165 elif message or line:
165 elif message or line:
166 message.append(line)
166 message.append(line)
167 comments.append(line)
167 comments.append(line)
168
168
169 eatdiff(message)
169 eatdiff(message)
170 eatdiff(comments)
170 eatdiff(comments)
171 eatempty(message)
171 eatempty(message)
172 eatempty(comments)
172 eatempty(comments)
173
173
174 # make sure message isn't empty
174 # make sure message isn't empty
175 if format and format.startswith("tag") and subject:
175 if format and format.startswith("tag") and subject:
176 message.insert(0, "")
176 message.insert(0, "")
177 message.insert(0, subject)
177 message.insert(0, subject)
178 return (message, comments, user, date, diffstart > 1)
178 return (message, comments, user, date, diffstart > 1)
179
179
180 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
180 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
181 # first try just applying the patch
181 # first try just applying the patch
182 (err, n) = self.apply(repo, [ patch ], update_status=False,
182 (err, n) = self.apply(repo, [ patch ], update_status=False,
183 strict=True, merge=rev, wlock=wlock)
183 strict=True, merge=rev, wlock=wlock)
184
184
185 if err == 0:
185 if err == 0:
186 return (err, n)
186 return (err, n)
187
187
188 if n is None:
188 if n is None:
189 raise util.Abort(_("apply failed for patch %s") % patch)
189 raise util.Abort(_("apply failed for patch %s") % patch)
190
190
191 self.ui.warn("patch didn't work out, merging %s\n" % patch)
191 self.ui.warn("patch didn't work out, merging %s\n" % patch)
192
192
193 # apply failed, strip away that rev and merge.
193 # apply failed, strip away that rev and merge.
194 repo.update(head, allow=False, force=True, wlock=wlock)
194 repo.update(head, allow=False, force=True, wlock=wlock)
195 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
195 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
196
196
197 c = repo.changelog.read(rev)
197 c = repo.changelog.read(rev)
198 ret = repo.update(rev, allow=True, wlock=wlock)
198 ret = repo.update(rev, allow=True, wlock=wlock)
199 if ret:
199 if ret:
200 raise util.Abort(_("update returned %d") % ret)
200 raise util.Abort(_("update returned %d") % ret)
201 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
201 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
202 if n == None:
202 if n == None:
203 raise util.Abort(_("repo commit failed"))
203 raise util.Abort(_("repo commit failed"))
204 try:
204 try:
205 message, comments, user, date, patchfound = mergeq.readheaders(patch)
205 message, comments, user, date, patchfound = mergeq.readheaders(patch)
206 except:
206 except:
207 raise util.Abort(_("unable to read %s") % patch)
207 raise util.Abort(_("unable to read %s") % patch)
208
208
209 patchf = self.opener(patch, "w")
209 patchf = self.opener(patch, "w")
210 if comments:
210 if comments:
211 comments = "\n".join(comments) + '\n\n'
211 comments = "\n".join(comments) + '\n\n'
212 patchf.write(comments)
212 patchf.write(comments)
213 commands.dodiff(patchf, self.ui, repo, head, n)
213 commands.dodiff(patchf, self.ui, repo, head, n)
214 patchf.close()
214 patchf.close()
215 return (0, n)
215 return (0, n)
216
216
217 def qparents(self, repo, rev=None):
217 def qparents(self, repo, rev=None):
218 if rev is None:
218 if rev is None:
219 (p1, p2) = repo.dirstate.parents()
219 (p1, p2) = repo.dirstate.parents()
220 if p2 == revlog.nullid:
220 if p2 == revlog.nullid:
221 return p1
221 return p1
222 if len(self.applied) == 0:
222 if len(self.applied) == 0:
223 return None
223 return None
224 (top, patch) = self.applied[-1].split(':')
224 (top, patch) = self.applied[-1].split(':')
225 top = revlog.bin(top)
225 top = revlog.bin(top)
226 return top
226 return top
227 pp = repo.changelog.parents(rev)
227 pp = repo.changelog.parents(rev)
228 if pp[1] != revlog.nullid:
228 if pp[1] != revlog.nullid:
229 arevs = [ x.split(':')[0] for x in self.applied ]
229 arevs = [ x.split(':')[0] for x in self.applied ]
230 p0 = revlog.hex(pp[0])
230 p0 = revlog.hex(pp[0])
231 p1 = revlog.hex(pp[1])
231 p1 = revlog.hex(pp[1])
232 if p0 in arevs:
232 if p0 in arevs:
233 return pp[0]
233 return pp[0]
234 if p1 in arevs:
234 if p1 in arevs:
235 return pp[1]
235 return pp[1]
236 return pp[0]
236 return pp[0]
237
237
238 def mergepatch(self, repo, mergeq, series, wlock):
238 def mergepatch(self, repo, mergeq, series, wlock):
239 if len(self.applied) == 0:
239 if len(self.applied) == 0:
240 # each of the patches merged in will have two parents. This
240 # each of the patches merged in will have two parents. This
241 # can confuse the qrefresh, qdiff, and strip code because it
241 # can confuse the qrefresh, qdiff, and strip code because it
242 # needs to know which parent is actually in the patch queue.
242 # needs to know which parent is actually in the patch queue.
243 # so, we insert a merge marker with only one parent. This way
243 # so, we insert a merge marker with only one parent. This way
244 # the first patch in the queue is never a merge patch
244 # the first patch in the queue is never a merge patch
245 #
245 #
246 pname = ".hg.patches.merge.marker"
246 pname = ".hg.patches.merge.marker"
247 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
247 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
248 wlock=wlock)
248 wlock=wlock)
249 self.applied.append(revlog.hex(n) + ":" + pname)
249 self.applied.append(revlog.hex(n) + ":" + pname)
250 self.applied_dirty = 1
250 self.applied_dirty = 1
251
251
252 head = self.qparents(repo)
252 head = self.qparents(repo)
253
253
254 for patch in series:
254 for patch in series:
255 patch = mergeq.lookup(patch, strict=True)
255 patch = mergeq.lookup(patch, strict=True)
256 if not patch:
256 if not patch:
257 self.ui.warn("patch %s does not exist\n" % patch)
257 self.ui.warn("patch %s does not exist\n" % patch)
258 return (1, None)
258 return (1, None)
259
259
260 info = mergeq.isapplied(patch)
260 info = mergeq.isapplied(patch)
261 if not info:
261 if not info:
262 self.ui.warn("patch %s is not applied\n" % patch)
262 self.ui.warn("patch %s is not applied\n" % patch)
263 return (1, None)
263 return (1, None)
264 rev = revlog.bin(info[1])
264 rev = revlog.bin(info[1])
265 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
265 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
266 if head:
266 if head:
267 self.applied.append(revlog.hex(head) + ":" + patch)
267 self.applied.append(revlog.hex(head) + ":" + patch)
268 self.applied_dirty = 1
268 self.applied_dirty = 1
269 if err:
269 if err:
270 return (err, head)
270 return (err, head)
271 return (0, head)
271 return (0, head)
272
272
273 def apply(self, repo, series, list=False, update_status=True,
273 def apply(self, repo, series, list=False, update_status=True,
274 strict=False, patchdir=None, merge=None, wlock=None):
274 strict=False, patchdir=None, merge=None, wlock=None):
275 # TODO unify with commands.py
275 # TODO unify with commands.py
276 if not patchdir:
276 if not patchdir:
277 patchdir = self.path
277 patchdir = self.path
278 err = 0
278 err = 0
279 if not wlock:
279 if not wlock:
280 wlock = repo.wlock()
280 wlock = repo.wlock()
281 lock = repo.lock()
281 lock = repo.lock()
282 tr = repo.transaction()
282 tr = repo.transaction()
283 n = None
283 n = None
284 for patch in series:
284 for patch in series:
285 self.ui.warn("applying %s\n" % patch)
285 self.ui.warn("applying %s\n" % patch)
286 pf = os.path.join(patchdir, patch)
286 pf = os.path.join(patchdir, patch)
287
287
288 try:
288 try:
289 message, comments, user, date, patchfound = self.readheaders(patch)
289 message, comments, user, date, patchfound = self.readheaders(patch)
290 except:
290 except:
291 self.ui.warn("Unable to read %s\n" % pf)
291 self.ui.warn("Unable to read %s\n" % pf)
292 err = 1
292 err = 1
293 break
293 break
294
294
295 if not message:
295 if not message:
296 message = "imported patch %s\n" % patch
296 message = "imported patch %s\n" % patch
297 else:
297 else:
298 if list:
298 if list:
299 message.append("\nimported patch %s" % patch)
299 message.append("\nimported patch %s" % patch)
300 message = '\n'.join(message)
300 message = '\n'.join(message)
301
301
302 try:
302 try:
303 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
303 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
304 f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
304 f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
305 (pp, repo.root, pf))
305 (pp, repo.root, pf))
306 except:
306 except:
307 self.ui.warn("patch failed, unable to continue (try -v)\n")
307 self.ui.warn("patch failed, unable to continue (try -v)\n")
308 err = 1
308 err = 1
309 break
309 break
310 files = []
310 files = []
311 fuzz = False
311 fuzz = False
312 for l in f:
312 for l in f:
313 l = l.rstrip('\r\n');
313 l = l.rstrip('\r\n');
314 if self.ui.verbose:
314 if self.ui.verbose:
315 self.ui.warn(l + "\n")
315 self.ui.warn(l + "\n")
316 if l[:14] == 'patching file ':
316 if l[:14] == 'patching file ':
317 pf = os.path.normpath(l[14:])
317 pf = os.path.normpath(l[14:])
318 # when patch finds a space in the file name, it puts
318 # when patch finds a space in the file name, it puts
319 # single quotes around the filename. strip them off
319 # single quotes around the filename. strip them off
320 if pf[0] == "'" and pf[-1] == "'":
320 if pf[0] == "'" and pf[-1] == "'":
321 pf = pf[1:-1]
321 pf = pf[1:-1]
322 if pf not in files:
322 if pf not in files:
323 files.append(pf)
323 files.append(pf)
324 printed_file = False
324 printed_file = False
325 file_str = l
325 file_str = l
326 elif l.find('with fuzz') >= 0:
326 elif l.find('with fuzz') >= 0:
327 if not printed_file:
327 if not printed_file:
328 self.ui.warn(file_str + '\n')
328 self.ui.warn(file_str + '\n')
329 printed_file = True
329 printed_file = True
330 self.ui.warn(l + '\n')
330 self.ui.warn(l + '\n')
331 fuzz = True
331 fuzz = True
332 elif l.find('saving rejects to file') >= 0:
332 elif l.find('saving rejects to file') >= 0:
333 self.ui.warn(l + '\n')
333 self.ui.warn(l + '\n')
334 elif l.find('FAILED') >= 0:
334 elif l.find('FAILED') >= 0:
335 if not printed_file:
335 if not printed_file:
336 self.ui.warn(file_str + '\n')
336 self.ui.warn(file_str + '\n')
337 printed_file = True
337 printed_file = True
338 self.ui.warn(l + '\n')
338 self.ui.warn(l + '\n')
339 patcherr = f.close()
339 patcherr = f.close()
340
340
341 if merge and len(files) > 0:
341 if merge and len(files) > 0:
342 # Mark as merged and update dirstate parent info
342 # Mark as merged and update dirstate parent info
343 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
343 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
344 p1, p2 = repo.dirstate.parents()
344 p1, p2 = repo.dirstate.parents()
345 repo.dirstate.setparents(p1, merge)
345 repo.dirstate.setparents(p1, merge)
346 if len(files) > 0:
346 if len(files) > 0:
347 cwd = repo.getcwd()
347 cwd = repo.getcwd()
348 cfiles = files
348 cfiles = files
349 if cwd:
349 if cwd:
350 cfiles = [util.pathto(cwd, f) for f in files]
350 cfiles = [util.pathto(cwd, f) for f in files]
351 commands.addremove_lock(self.ui, repo, cfiles,
351 commands.addremove_lock(self.ui, repo, cfiles,
352 opts={}, wlock=wlock)
352 opts={}, wlock=wlock)
353 n = repo.commit(files, message, user, date, force=1, lock=lock,
353 n = repo.commit(files, message, user, date, force=1, lock=lock,
354 wlock=wlock)
354 wlock=wlock)
355
355
356 if n == None:
356 if n == None:
357 raise util.Abort(_("repo commit failed"))
357 raise util.Abort(_("repo commit failed"))
358
358
359 if update_status:
359 if update_status:
360 self.applied.append(revlog.hex(n) + ":" + patch)
360 self.applied.append(revlog.hex(n) + ":" + patch)
361
361
362 if patcherr:
362 if patcherr:
363 if not patchfound:
363 if not patchfound:
364 self.ui.warn("patch %s is empty\n" % patch)
364 self.ui.warn("patch %s is empty\n" % patch)
365 err = 0
365 err = 0
366 else:
366 else:
367 self.ui.warn("patch failed, rejects left in working dir\n")
367 self.ui.warn("patch failed, rejects left in working dir\n")
368 err = 1
368 err = 1
369 break
369 break
370
370
371 if fuzz and strict:
371 if fuzz and strict:
372 self.ui.warn("fuzz found when applying patch, stopping\n")
372 self.ui.warn("fuzz found when applying patch, stopping\n")
373 err = 1
373 err = 1
374 break
374 break
375 tr.close()
375 tr.close()
376 return (err, n)
376 return (err, n)
377
377
378 def delete(self, repo, patch):
378 def delete(self, repo, patch):
379 patch = self.lookup(patch, strict=True)
379 patch = self.lookup(patch, strict=True)
380 info = self.isapplied(patch)
380 info = self.isapplied(patch)
381 if info:
381 if info:
382 raise util.Abort(_("cannot delete applied patch %s") % patch)
382 raise util.Abort(_("cannot delete applied patch %s") % patch)
383 if patch not in self.series:
383 if patch not in self.series:
384 raise util.Abort(_("patch %s not in series file") % patch)
384 raise util.Abort(_("patch %s not in series file") % patch)
385 i = self.find_series(patch)
385 i = self.find_series(patch)
386 del self.full_series[i]
386 del self.full_series[i]
387 self.read_series(self.full_series)
387 self.read_series(self.full_series)
388 self.series_dirty = 1
388 self.series_dirty = 1
389
389
390 def check_toppatch(self, repo):
390 def check_toppatch(self, repo):
391 if len(self.applied) > 0:
391 if len(self.applied) > 0:
392 (top, patch) = self.applied[-1].split(':')
392 (top, patch) = self.applied[-1].split(':')
393 top = revlog.bin(top)
393 top = revlog.bin(top)
394 pp = repo.dirstate.parents()
394 pp = repo.dirstate.parents()
395 if top not in pp:
395 if top not in pp:
396 raise util.Abort(_("queue top not at same revision as working directory"))
396 raise util.Abort(_("queue top not at same revision as working directory"))
397 return top
397 return top
398 return None
398 return None
399 def check_localchanges(self, repo):
399 def check_localchanges(self, repo):
400 (c, a, r, d, u) = repo.changes(None, None)
400 (c, a, r, d, u) = repo.changes(None, None)
401 if c or a or d or r:
401 if c or a or d or r:
402 raise util.Abort(_("local changes found, refresh first"))
402 raise util.Abort(_("local changes found, refresh first"))
403 def new(self, repo, patch, msg=None, force=None):
403 def new(self, repo, patch, msg=None, force=None):
404 if os.path.exists(os.path.join(self.path, patch)):
404 if os.path.exists(os.path.join(self.path, patch)):
405 raise util.Abort(_('patch "%s" already exists') % patch)
405 raise util.Abort(_('patch "%s" already exists') % patch)
406 commitfiles = []
406 commitfiles = []
407 (c, a, r, d, u) = repo.changes(None, None)
407 (c, a, r, d, u) = repo.changes(None, None)
408 if c or a or d or r:
408 if c or a or d or r:
409 if not force:
409 if not force:
410 raise util.Abort(_("local changes found, refresh first"))
410 raise util.Abort(_("local changes found, refresh first"))
411 commitfiles = c + a + r
411 commitfiles = c + a + r
412 self.check_toppatch(repo)
412 self.check_toppatch(repo)
413 wlock = repo.wlock()
413 wlock = repo.wlock()
414 insert = self.full_series_end()
414 insert = self.full_series_end()
415 if msg:
415 if msg:
416 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
416 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
417 wlock=wlock)
417 wlock=wlock)
418 else:
418 else:
419 n = repo.commit(commitfiles,
419 n = repo.commit(commitfiles,
420 "New patch: %s" % patch, force=True, wlock=wlock)
420 "New patch: %s" % patch, force=True, wlock=wlock)
421 if n == None:
421 if n == None:
422 raise util.Abort(_("repo commit failed"))
422 raise util.Abort(_("repo commit failed"))
423 self.full_series[insert:insert] = [patch]
423 self.full_series[insert:insert] = [patch]
424 self.applied.append(revlog.hex(n) + ":" + patch)
424 self.applied.append(revlog.hex(n) + ":" + patch)
425 self.read_series(self.full_series)
425 self.read_series(self.full_series)
426 self.series_dirty = 1
426 self.series_dirty = 1
427 self.applied_dirty = 1
427 self.applied_dirty = 1
428 p = self.opener(patch, "w")
428 p = self.opener(patch, "w")
429 if msg:
429 if msg:
430 msg = msg + "\n"
430 msg = msg + "\n"
431 p.write(msg)
431 p.write(msg)
432 p.close()
432 p.close()
433 wlock = None
433 wlock = None
434 r = self.qrepo()
434 r = self.qrepo()
435 if r: r.add([patch])
435 if r: r.add([patch])
436 if commitfiles:
436 if commitfiles:
437 self.refresh(repo, msg=None, short=True)
437 self.refresh(repo, msg=None, short=True)
438
438
439 def strip(self, repo, rev, update=True, backup="all", wlock=None):
439 def strip(self, repo, rev, update=True, backup="all", wlock=None):
440 def limitheads(chlog, stop):
440 def limitheads(chlog, stop):
441 """return the list of all nodes that have no children"""
441 """return the list of all nodes that have no children"""
442 p = {}
442 p = {}
443 h = []
443 h = []
444 stoprev = 0
444 stoprev = 0
445 if stop in chlog.nodemap:
445 if stop in chlog.nodemap:
446 stoprev = chlog.rev(stop)
446 stoprev = chlog.rev(stop)
447
447
448 for r in range(chlog.count() - 1, -1, -1):
448 for r in range(chlog.count() - 1, -1, -1):
449 n = chlog.node(r)
449 n = chlog.node(r)
450 if n not in p:
450 if n not in p:
451 h.append(n)
451 h.append(n)
452 if n == stop:
452 if n == stop:
453 break
453 break
454 if r < stoprev:
454 if r < stoprev:
455 break
455 break
456 for pn in chlog.parents(n):
456 for pn in chlog.parents(n):
457 p[pn] = 1
457 p[pn] = 1
458 return h
458 return h
459
459
460 def bundle(cg):
460 def bundle(cg):
461 backupdir = repo.join("strip-backup")
461 backupdir = repo.join("strip-backup")
462 if not os.path.isdir(backupdir):
462 if not os.path.isdir(backupdir):
463 os.mkdir(backupdir)
463 os.mkdir(backupdir)
464 name = os.path.join(backupdir, "%s" % revlog.short(rev))
464 name = os.path.join(backupdir, "%s" % revlog.short(rev))
465 name = savename(name)
465 name = savename(name)
466 self.ui.warn("saving bundle to %s\n" % name)
466 self.ui.warn("saving bundle to %s\n" % name)
467 # TODO, exclusive open
467 # TODO, exclusive open
468 f = open(name, "wb")
468 f = open(name, "wb")
469 try:
469 try:
470 f.write("HG10")
470 f.write("HG10")
471 z = bz2.BZ2Compressor(9)
471 z = bz2.BZ2Compressor(9)
472 while 1:
472 while 1:
473 chunk = cg.read(4096)
473 chunk = cg.read(4096)
474 if not chunk:
474 if not chunk:
475 break
475 break
476 f.write(z.compress(chunk))
476 f.write(z.compress(chunk))
477 f.write(z.flush())
477 f.write(z.flush())
478 except:
478 except:
479 os.unlink(name)
479 os.unlink(name)
480 raise
480 raise
481 f.close()
481 f.close()
482 return name
482 return name
483
483
484 def stripall(rev, revnum):
484 def stripall(rev, revnum):
485 cl = repo.changelog
485 cl = repo.changelog
486 c = cl.read(rev)
486 c = cl.read(rev)
487 mm = repo.manifest.read(c[0])
487 mm = repo.manifest.read(c[0])
488 seen = {}
488 seen = {}
489
489
490 for x in xrange(revnum, cl.count()):
490 for x in xrange(revnum, cl.count()):
491 c = cl.read(cl.node(x))
491 c = cl.read(cl.node(x))
492 for f in c[3]:
492 for f in c[3]:
493 if f in seen:
493 if f in seen:
494 continue
494 continue
495 seen[f] = 1
495 seen[f] = 1
496 if f in mm:
496 if f in mm:
497 filerev = mm[f]
497 filerev = mm[f]
498 else:
498 else:
499 filerev = 0
499 filerev = 0
500 seen[f] = filerev
500 seen[f] = filerev
501 # we go in two steps here so the strip loop happens in a
501 # we go in two steps here so the strip loop happens in a
502 # sensible order. When stripping many files, this helps keep
502 # sensible order. When stripping many files, this helps keep
503 # our disk access patterns under control.
503 # our disk access patterns under control.
504 list = seen.keys()
504 list = seen.keys()
505 list.sort()
505 list.sort()
506 for f in list:
506 for f in list:
507 ff = repo.file(f)
507 ff = repo.file(f)
508 filerev = seen[f]
508 filerev = seen[f]
509 if filerev != 0:
509 if filerev != 0:
510 if filerev in ff.nodemap:
510 if filerev in ff.nodemap:
511 filerev = ff.rev(filerev)
511 filerev = ff.rev(filerev)
512 else:
512 else:
513 filerev = 0
513 filerev = 0
514 ff.strip(filerev, revnum)
514 ff.strip(filerev, revnum)
515
515
516 if not wlock:
516 if not wlock:
517 wlock = repo.wlock()
517 wlock = repo.wlock()
518 lock = repo.lock()
518 lock = repo.lock()
519 chlog = repo.changelog
519 chlog = repo.changelog
520 # TODO delete the undo files, and handle undo of merge sets
520 # TODO delete the undo files, and handle undo of merge sets
521 pp = chlog.parents(rev)
521 pp = chlog.parents(rev)
522 revnum = chlog.rev(rev)
522 revnum = chlog.rev(rev)
523
523
524 if update:
524 if update:
525 (c, a, r, d, u) = repo.changes(None, None)
525 (c, a, r, d, u) = repo.changes(None, None)
526 if c or a or d or r:
526 if c or a or d or r:
527 raise util.Abort(_("local changes found"))
527 raise util.Abort(_("local changes found"))
528 urev = self.qparents(repo, rev)
528 urev = self.qparents(repo, rev)
529 repo.update(urev, allow=False, force=True, wlock=wlock)
529 repo.update(urev, allow=False, force=True, wlock=wlock)
530 repo.dirstate.write()
530 repo.dirstate.write()
531
531
532 # save is a list of all the branches we are truncating away
532 # save is a list of all the branches we are truncating away
533 # that we actually want to keep. changegroup will be used
533 # that we actually want to keep. changegroup will be used
534 # to preserve them and add them back after the truncate
534 # to preserve them and add them back after the truncate
535 saveheads = []
535 saveheads = []
536 savebases = {}
536 savebases = {}
537
537
538 tip = chlog.tip()
538 tip = chlog.tip()
539 heads = limitheads(chlog, rev)
539 heads = limitheads(chlog, rev)
540 seen = {}
540 seen = {}
541
541
542 # search through all the heads, finding those where the revision
542 # search through all the heads, finding those where the revision
543 # we want to strip away is an ancestor. Also look for merges
543 # we want to strip away is an ancestor. Also look for merges
544 # that might be turned into new heads by the strip.
544 # that might be turned into new heads by the strip.
545 while heads:
545 while heads:
546 h = heads.pop()
546 h = heads.pop()
547 n = h
547 n = h
548 while True:
548 while True:
549 seen[n] = 1
549 seen[n] = 1
550 pp = chlog.parents(n)
550 pp = chlog.parents(n)
551 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
551 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
552 if pp[1] not in seen:
552 if pp[1] not in seen:
553 heads.append(pp[1])
553 heads.append(pp[1])
554 if pp[0] == revlog.nullid:
554 if pp[0] == revlog.nullid:
555 break
555 break
556 if chlog.rev(pp[0]) < revnum:
556 if chlog.rev(pp[0]) < revnum:
557 break
557 break
558 n = pp[0]
558 n = pp[0]
559 if n == rev:
559 if n == rev:
560 break
560 break
561 r = chlog.reachable(h, rev)
561 r = chlog.reachable(h, rev)
562 if rev not in r:
562 if rev not in r:
563 saveheads.append(h)
563 saveheads.append(h)
564 for x in r:
564 for x in r:
565 if chlog.rev(x) > revnum:
565 if chlog.rev(x) > revnum:
566 savebases[x] = 1
566 savebases[x] = 1
567
567
568 # create a changegroup for all the branches we need to keep
568 # create a changegroup for all the branches we need to keep
569 if backup is "all":
569 if backup is "all":
570 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
570 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
571 bundle(backupch)
571 bundle(backupch)
572 if saveheads:
572 if saveheads:
573 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
573 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
574 chgrpfile = bundle(backupch)
574 chgrpfile = bundle(backupch)
575
575
576 stripall(rev, revnum)
576 stripall(rev, revnum)
577
577
578 change = chlog.read(rev)
578 change = chlog.read(rev)
579 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
579 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
580 chlog.strip(revnum, revnum)
580 chlog.strip(revnum, revnum)
581 if saveheads:
581 if saveheads:
582 self.ui.status("adding branch\n")
582 self.ui.status("adding branch\n")
583 commands.unbundle(self.ui, repo, chgrpfile, update=False)
583 commands.unbundle(self.ui, repo, chgrpfile, update=False)
584 if backup is not "strip":
584 if backup is not "strip":
585 os.unlink(chgrpfile)
585 os.unlink(chgrpfile)
586
586
587 def isapplied(self, patch):
587 def isapplied(self, patch):
588 """returns (index, rev, patch)"""
588 """returns (index, rev, patch)"""
589 for i in xrange(len(self.applied)):
589 for i in xrange(len(self.applied)):
590 p = self.applied[i]
590 p = self.applied[i]
591 a = p.split(':')
591 a = p.split(':')
592 if a[1] == patch:
592 if a[1] == patch:
593 return (i, a[0], a[1])
593 return (i, a[0], a[1])
594 return None
594 return None
595
595
596 # if the exact patch name does not exist, we try a few
596 # if the exact patch name does not exist, we try a few
597 # variations. If strict is passed, we try only #1
597 # variations. If strict is passed, we try only #1
598 #
598 #
599 # 1) a number to indicate an offset in the series file
599 # 1) a number to indicate an offset in the series file
600 # 2) a unique substring of the patch name was given
600 # 2) a unique substring of the patch name was given
601 # 3) patchname[-+]num to indicate an offset in the series file
601 # 3) patchname[-+]num to indicate an offset in the series file
602 def lookup(self, patch, strict=False):
602 def lookup(self, patch, strict=False):
603 def partial_name(s):
603 def partial_name(s):
604 count = 0
604 count = 0
605 if s in self.series:
605 if s in self.series:
606 return s
606 return s
607 for x in self.series:
607 for x in self.series:
608 if s in x:
608 if s in x:
609 count += 1
609 count += 1
610 last = x
610 last = x
611 if count > 1:
611 if count > 1:
612 return None
612 return None
613 if count:
613 if count:
614 return last
614 return last
615 if len(self.series) > 0 and len(self.applied) > 0:
615 if len(self.series) > 0 and len(self.applied) > 0:
616 if s == 'qtip':
616 if s == 'qtip':
617 return self.series[self.series_end()-1]
617 return self.series[self.series_end()-1]
618 if s == 'qbase':
618 if s == 'qbase':
619 return self.series[0]
619 return self.series[0]
620 return None
620 return None
621 if patch == None:
621 if patch == None:
622 return None
622 return None
623
623
624 # we don't want to return a partial match until we make
624 # we don't want to return a partial match until we make
625 # sure the file name passed in does not exist (checked below)
625 # sure the file name passed in does not exist (checked below)
626 res = partial_name(patch)
626 res = partial_name(patch)
627 if res and res == patch:
627 if res and res == patch:
628 return res
628 return res
629
629
630 if not os.path.isfile(os.path.join(self.path, patch)):
630 if not os.path.isfile(os.path.join(self.path, patch)):
631 try:
631 try:
632 sno = int(patch)
632 sno = int(patch)
633 except(ValueError, OverflowError):
633 except(ValueError, OverflowError):
634 pass
634 pass
635 else:
635 else:
636 if sno < len(self.series):
636 if sno < len(self.series):
637 patch = self.series[sno]
637 patch = self.series[sno]
638 return patch
638 return patch
639 if not strict:
639 if not strict:
640 # return any partial match made above
640 # return any partial match made above
641 if res:
641 if res:
642 return res
642 return res
643 minus = patch.rsplit('-', 1)
643 minus = patch.rsplit('-', 1)
644 if len(minus) > 1:
644 if len(minus) > 1:
645 res = partial_name(minus[0])
645 res = partial_name(minus[0])
646 if res:
646 if res:
647 i = self.series.index(res)
647 i = self.series.index(res)
648 try:
648 try:
649 off = int(minus[1] or 1)
649 off = int(minus[1] or 1)
650 except(ValueError, OverflowError):
650 except(ValueError, OverflowError):
651 pass
651 pass
652 else:
652 else:
653 if i - off >= 0:
653 if i - off >= 0:
654 return self.series[i - off]
654 return self.series[i - off]
655 plus = patch.rsplit('+', 1)
655 plus = patch.rsplit('+', 1)
656 if len(plus) > 1:
656 if len(plus) > 1:
657 res = partial_name(plus[0])
657 res = partial_name(plus[0])
658 if res:
658 if res:
659 i = self.series.index(res)
659 i = self.series.index(res)
660 try:
660 try:
661 off = int(plus[1] or 1)
661 off = int(plus[1] or 1)
662 except(ValueError, OverflowError):
662 except(ValueError, OverflowError):
663 pass
663 pass
664 else:
664 else:
665 if i + off < len(self.series):
665 if i + off < len(self.series):
666 return self.series[i + off]
666 return self.series[i + off]
667 raise util.Abort(_("patch %s not in series") % patch)
667 raise util.Abort(_("patch %s not in series") % patch)
668
668
669 def push(self, repo, patch=None, force=False, list=False,
669 def push(self, repo, patch=None, force=False, list=False,
670 mergeq=None, wlock=None):
670 mergeq=None, wlock=None):
671 if not wlock:
671 if not wlock:
672 wlock = repo.wlock()
672 wlock = repo.wlock()
673 patch = self.lookup(patch)
673 patch = self.lookup(patch)
674 if patch and self.isapplied(patch):
674 if patch and self.isapplied(patch):
675 self.ui.warn(_("patch %s is already applied\n") % patch)
675 self.ui.warn(_("patch %s is already applied\n") % patch)
676 sys.exit(1)
676 sys.exit(1)
677 if self.series_end() == len(self.series):
677 if self.series_end() == len(self.series):
678 self.ui.warn(_("patch series fully applied\n"))
678 self.ui.warn(_("patch series fully applied\n"))
679 sys.exit(1)
679 sys.exit(1)
680 if not force:
680 if not force:
681 self.check_localchanges(repo)
681 self.check_localchanges(repo)
682
682
683 self.applied_dirty = 1;
683 self.applied_dirty = 1;
684 start = self.series_end()
684 start = self.series_end()
685 if start > 0:
685 if start > 0:
686 self.check_toppatch(repo)
686 self.check_toppatch(repo)
687 if not patch:
687 if not patch:
688 patch = self.series[start]
688 patch = self.series[start]
689 end = start + 1
689 end = start + 1
690 else:
690 else:
691 end = self.series.index(patch, start) + 1
691 end = self.series.index(patch, start) + 1
692 s = self.series[start:end]
692 s = self.series[start:end]
693 if mergeq:
693 if mergeq:
694 ret = self.mergepatch(repo, mergeq, s, wlock)
694 ret = self.mergepatch(repo, mergeq, s, wlock)
695 else:
695 else:
696 ret = self.apply(repo, s, list, wlock=wlock)
696 ret = self.apply(repo, s, list, wlock=wlock)
697 top = self.applied[-1].split(':')[1]
697 top = self.applied[-1].split(':')[1]
698 if ret[0]:
698 if ret[0]:
699 self.ui.write("Errors during apply, please fix and refresh %s\n" %
699 self.ui.write("Errors during apply, please fix and refresh %s\n" %
700 top)
700 top)
701 else:
701 else:
702 self.ui.write("Now at: %s\n" % top)
702 self.ui.write("Now at: %s\n" % top)
703 return ret[0]
703 return ret[0]
704
704
705 def pop(self, repo, patch=None, force=False, update=True, all=False,
705 def pop(self, repo, patch=None, force=False, update=True, all=False,
706 wlock=None):
706 wlock=None):
707 def getfile(f, rev):
707 def getfile(f, rev):
708 t = repo.file(f).read(rev)
708 t = repo.file(f).read(rev)
709 try:
709 try:
710 repo.wfile(f, "w").write(t)
710 repo.wfile(f, "w").write(t)
711 except IOError:
711 except IOError:
712 try:
712 try:
713 os.makedirs(os.path.dirname(repo.wjoin(f)))
713 os.makedirs(os.path.dirname(repo.wjoin(f)))
714 except OSError, err:
714 except OSError, err:
715 if err.errno != errno.EEXIST: raise
715 if err.errno != errno.EEXIST: raise
716 repo.wfile(f, "w").write(t)
716 repo.wfile(f, "w").write(t)
717
717
718 if not wlock:
718 if not wlock:
719 wlock = repo.wlock()
719 wlock = repo.wlock()
720 if patch:
720 if patch:
721 # index, rev, patch
721 # index, rev, patch
722 info = self.isapplied(patch)
722 info = self.isapplied(patch)
723 if not info:
723 if not info:
724 patch = self.lookup(patch)
724 patch = self.lookup(patch)
725 info = self.isapplied(patch)
725 info = self.isapplied(patch)
726 if not info:
726 if not info:
727 raise util.Abort(_("patch %s is not applied") % patch)
727 raise util.Abort(_("patch %s is not applied") % patch)
728 if len(self.applied) == 0:
728 if len(self.applied) == 0:
729 self.ui.warn(_("no patches applied\n"))
729 self.ui.warn(_("no patches applied\n"))
730 sys.exit(1)
730 sys.exit(1)
731
731
732 if not update:
732 if not update:
733 parents = repo.dirstate.parents()
733 parents = repo.dirstate.parents()
734 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
734 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
735 for p in parents:
735 for p in parents:
736 if p in rr:
736 if p in rr:
737 self.ui.warn("qpop: forcing dirstate update\n")
737 self.ui.warn("qpop: forcing dirstate update\n")
738 update = True
738 update = True
739
739
740 if not force and update:
740 if not force and update:
741 self.check_localchanges(repo)
741 self.check_localchanges(repo)
742
742
743 self.applied_dirty = 1;
743 self.applied_dirty = 1;
744 end = len(self.applied)
744 end = len(self.applied)
745 if not patch:
745 if not patch:
746 if all:
746 if all:
747 popi = 0
747 popi = 0
748 else:
748 else:
749 popi = len(self.applied) - 1
749 popi = len(self.applied) - 1
750 else:
750 else:
751 popi = info[0] + 1
751 popi = info[0] + 1
752 if popi >= end:
752 if popi >= end:
753 self.ui.warn("qpop: %s is already at the top\n" % patch)
753 self.ui.warn("qpop: %s is already at the top\n" % patch)
754 return
754 return
755 info = [ popi ] + self.applied[popi].split(':')
755 info = [ popi ] + self.applied[popi].split(':')
756
756
757 start = info[0]
757 start = info[0]
758 rev = revlog.bin(info[1])
758 rev = revlog.bin(info[1])
759
759
760 # we know there are no local changes, so we can make a simplified
760 # we know there are no local changes, so we can make a simplified
761 # form of hg.update.
761 # form of hg.update.
762 if update:
762 if update:
763 top = self.check_toppatch(repo)
763 top = self.check_toppatch(repo)
764 qp = self.qparents(repo, rev)
764 qp = self.qparents(repo, rev)
765 changes = repo.changelog.read(qp)
765 changes = repo.changelog.read(qp)
766 mf1 = repo.manifest.readflags(changes[0])
766 mf1 = repo.manifest.readflags(changes[0])
767 mmap = repo.manifest.read(changes[0])
767 mmap = repo.manifest.read(changes[0])
768 (c, a, r, d, u) = repo.changes(qp, top)
768 (c, a, r, d, u) = repo.changes(qp, top)
769 if d:
769 if d:
770 raise util.Abort("deletions found between repo revs")
770 raise util.Abort("deletions found between repo revs")
771 for f in c:
771 for f in c:
772 getfile(f, mmap[f])
772 getfile(f, mmap[f])
773 for f in r:
773 for f in r:
774 getfile(f, mmap[f])
774 getfile(f, mmap[f])
775 util.set_exec(repo.wjoin(f), mf1[f])
775 util.set_exec(repo.wjoin(f), mf1[f])
776 repo.dirstate.update(c + r, 'n')
776 repo.dirstate.update(c + r, 'n')
777 for f in a:
777 for f in a:
778 try: os.unlink(repo.wjoin(f))
778 try: os.unlink(repo.wjoin(f))
779 except: raise
779 except: raise
780 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
780 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
781 except: pass
781 except: pass
782 if a:
782 if a:
783 repo.dirstate.forget(a)
783 repo.dirstate.forget(a)
784 repo.dirstate.setparents(qp, revlog.nullid)
784 repo.dirstate.setparents(qp, revlog.nullid)
785 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
785 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
786 del self.applied[start:end]
786 del self.applied[start:end]
787 if len(self.applied):
787 if len(self.applied):
788 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
788 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
789 else:
789 else:
790 self.ui.write("Patch queue now empty\n")
790 self.ui.write("Patch queue now empty\n")
791
791
792 def diff(self, repo, files):
792 def diff(self, repo, files):
793 top = self.check_toppatch(repo)
793 top = self.check_toppatch(repo)
794 if not top:
794 if not top:
795 self.ui.write("No patches applied\n")
795 self.ui.write("No patches applied\n")
796 return
796 return
797 qp = self.qparents(repo, top)
797 qp = self.qparents(repo, top)
798 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
798 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
799
799
800 def refresh(self, repo, msg=None, short=False):
800 def refresh(self, repo, msg=None, short=False):
801 if len(self.applied) == 0:
801 if len(self.applied) == 0:
802 self.ui.write("No patches applied\n")
802 self.ui.write("No patches applied\n")
803 return
803 return
804 wlock = repo.wlock()
804 wlock = repo.wlock()
805 self.check_toppatch(repo)
805 self.check_toppatch(repo)
806 qp = self.qparents(repo)
806 qp = self.qparents(repo)
807 (top, patch) = self.applied[-1].split(':')
807 (top, patch) = self.applied[-1].split(':')
808 top = revlog.bin(top)
808 top = revlog.bin(top)
809 cparents = repo.changelog.parents(top)
809 cparents = repo.changelog.parents(top)
810 patchparent = self.qparents(repo, top)
810 patchparent = self.qparents(repo, top)
811 message, comments, user, date, patchfound = self.readheaders(patch)
811 message, comments, user, date, patchfound = self.readheaders(patch)
812
812
813 patchf = self.opener(patch, "w")
813 patchf = self.opener(patch, "w")
814 msg = msg.rstrip()
815 if msg:
816 if comments:
817 # Remove existing message.
818 ci = 0
819 for mi in range(len(message)):
820 while message[mi] != comments[ci]:
821 ci += 1
822 del comments[ci]
823 comments.append(msg)
814 if comments:
824 if comments:
815 comments = "\n".join(comments) + '\n\n'
825 comments = "\n".join(comments) + '\n\n'
816 patchf.write(comments)
826 patchf.write(comments)
817
827
818 tip = repo.changelog.tip()
828 tip = repo.changelog.tip()
819 if top == tip:
829 if top == tip:
820 # if the top of our patch queue is also the tip, there is an
830 # if the top of our patch queue is also the tip, there is an
821 # optimization here. We update the dirstate in place and strip
831 # optimization here. We update the dirstate in place and strip
822 # off the tip commit. Then just commit the current directory
832 # off the tip commit. Then just commit the current directory
823 # tree. We can also send repo.commit the list of files
833 # tree. We can also send repo.commit the list of files
824 # changed to speed up the diff
834 # changed to speed up the diff
825 #
835 #
826 # in short mode, we only diff the files included in the
836 # in short mode, we only diff the files included in the
827 # patch already
837 # patch already
828 #
838 #
829 # this should really read:
839 # this should really read:
830 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
840 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
831 # but we do it backwards to take advantage of manifest/chlog
841 # but we do it backwards to take advantage of manifest/chlog
832 # caching against the next repo.changes call
842 # caching against the next repo.changes call
833 #
843 #
834 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
844 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
835 if short:
845 if short:
836 filelist = cc + aa + dd
846 filelist = cc + aa + dd
837 else:
847 else:
838 filelist = None
848 filelist = None
839 (c, a, r, d, u) = repo.changes(None, None, filelist)
849 (c, a, r, d, u) = repo.changes(None, None, filelist)
840
850
841 # we might end up with files that were added between tip and
851 # we might end up with files that were added between tip and
842 # the dirstate parent, but then changed in the local dirstate.
852 # the dirstate parent, but then changed in the local dirstate.
843 # in this case, we want them to only show up in the added section
853 # in this case, we want them to only show up in the added section
844 for x in c:
854 for x in c:
845 if x not in aa:
855 if x not in aa:
846 cc.append(x)
856 cc.append(x)
847 # we might end up with files added by the local dirstate that
857 # we might end up with files added by the local dirstate that
848 # were deleted by the patch. In this case, they should only
858 # were deleted by the patch. In this case, they should only
849 # show up in the changed section.
859 # show up in the changed section.
850 for x in a:
860 for x in a:
851 if x in dd:
861 if x in dd:
852 del dd[dd.index(x)]
862 del dd[dd.index(x)]
853 cc.append(x)
863 cc.append(x)
854 else:
864 else:
855 aa.append(x)
865 aa.append(x)
856 # make sure any files deleted in the local dirstate
866 # make sure any files deleted in the local dirstate
857 # are not in the add or change column of the patch
867 # are not in the add or change column of the patch
858 forget = []
868 forget = []
859 for x in d + r:
869 for x in d + r:
860 if x in aa:
870 if x in aa:
861 del aa[aa.index(x)]
871 del aa[aa.index(x)]
862 forget.append(x)
872 forget.append(x)
863 continue
873 continue
864 elif x in cc:
874 elif x in cc:
865 del cc[cc.index(x)]
875 del cc[cc.index(x)]
866 dd.append(x)
876 dd.append(x)
867
877
868 c = list(util.unique(cc))
878 c = list(util.unique(cc))
869 r = list(util.unique(dd))
879 r = list(util.unique(dd))
870 a = list(util.unique(aa))
880 a = list(util.unique(aa))
871 filelist = list(util.unique(c + r + a ))
881 filelist = list(util.unique(c + r + a ))
872 commands.dodiff(patchf, self.ui, repo, patchparent, None,
882 commands.dodiff(patchf, self.ui, repo, patchparent, None,
873 filelist, changes=(c, a, r, [], u))
883 filelist, changes=(c, a, r, [], u))
874 patchf.close()
884 patchf.close()
875
885
876 changes = repo.changelog.read(tip)
886 changes = repo.changelog.read(tip)
877 repo.dirstate.setparents(*cparents)
887 repo.dirstate.setparents(*cparents)
878 repo.dirstate.update(a, 'a')
888 repo.dirstate.update(a, 'a')
879 repo.dirstate.update(r, 'r')
889 repo.dirstate.update(r, 'r')
880 repo.dirstate.update(c, 'n')
890 repo.dirstate.update(c, 'n')
881 repo.dirstate.forget(forget)
891 repo.dirstate.forget(forget)
882
892
883 if not msg:
893 if not msg:
884 if not message:
894 if not message:
885 message = "patch queue: %s\n" % patch
895 message = "patch queue: %s\n" % patch
886 else:
896 else:
887 message = "\n".join(message)
897 message = "\n".join(message)
888 else:
898 else:
889 message = msg
899 message = msg
890
900
891 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
901 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
892 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
902 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
893 self.applied[-1] = revlog.hex(n) + ':' + patch
903 self.applied[-1] = revlog.hex(n) + ':' + patch
894 self.applied_dirty = 1
904 self.applied_dirty = 1
895 else:
905 else:
896 commands.dodiff(patchf, self.ui, repo, patchparent, None)
906 commands.dodiff(patchf, self.ui, repo, patchparent, None)
897 patchf.close()
907 patchf.close()
898 self.pop(repo, force=True, wlock=wlock)
908 self.pop(repo, force=True, wlock=wlock)
899 self.push(repo, force=True, wlock=wlock)
909 self.push(repo, force=True, wlock=wlock)
900
910
901 def init(self, repo, create=False):
911 def init(self, repo, create=False):
902 if os.path.isdir(self.path):
912 if os.path.isdir(self.path):
903 raise util.Abort(_("patch queue directory already exists"))
913 raise util.Abort(_("patch queue directory already exists"))
904 os.mkdir(self.path)
914 os.mkdir(self.path)
905 if create:
915 if create:
906 return self.qrepo(create=True)
916 return self.qrepo(create=True)
907
917
908 def unapplied(self, repo, patch=None):
918 def unapplied(self, repo, patch=None):
909 if patch and patch not in self.series:
919 if patch and patch not in self.series:
910 raise util.Abort(_("patch %s is not in series file") % patch)
920 raise util.Abort(_("patch %s is not in series file") % patch)
911 if not patch:
921 if not patch:
912 start = self.series_end()
922 start = self.series_end()
913 else:
923 else:
914 start = self.series.index(patch) + 1
924 start = self.series.index(patch) + 1
915 for p in self.series[start:]:
925 for p in self.series[start:]:
916 if self.ui.verbose:
926 if self.ui.verbose:
917 self.ui.write("%d " % self.series.index(p))
927 self.ui.write("%d " % self.series.index(p))
918 self.ui.write("%s\n" % p)
928 self.ui.write("%s\n" % p)
919
929
920 def qseries(self, repo, missing=None):
930 def qseries(self, repo, missing=None):
921 start = self.series_end()
931 start = self.series_end()
922 if not missing:
932 if not missing:
923 for p in self.series[:start]:
933 for p in self.series[:start]:
924 if self.ui.verbose:
934 if self.ui.verbose:
925 self.ui.write("%d A " % self.series.index(p))
935 self.ui.write("%d A " % self.series.index(p))
926 self.ui.write("%s\n" % p)
936 self.ui.write("%s\n" % p)
927 for p in self.series[start:]:
937 for p in self.series[start:]:
928 if self.ui.verbose:
938 if self.ui.verbose:
929 self.ui.write("%d U " % self.series.index(p))
939 self.ui.write("%d U " % self.series.index(p))
930 self.ui.write("%s\n" % p)
940 self.ui.write("%s\n" % p)
931 else:
941 else:
932 list = []
942 list = []
933 for root, dirs, files in os.walk(self.path):
943 for root, dirs, files in os.walk(self.path):
934 d = root[len(self.path) + 1:]
944 d = root[len(self.path) + 1:]
935 for f in files:
945 for f in files:
936 fl = os.path.join(d, f)
946 fl = os.path.join(d, f)
937 if (fl not in self.series and
947 if (fl not in self.series and
938 fl not in (self.status_path, self.series_path)
948 fl not in (self.status_path, self.series_path)
939 and not fl.startswith('.')):
949 and not fl.startswith('.')):
940 list.append(fl)
950 list.append(fl)
941 list.sort()
951 list.sort()
942 if list:
952 if list:
943 for x in list:
953 for x in list:
944 if self.ui.verbose:
954 if self.ui.verbose:
945 self.ui.write("D ")
955 self.ui.write("D ")
946 self.ui.write("%s\n" % x)
956 self.ui.write("%s\n" % x)
947
957
948 def issaveline(self, l):
958 def issaveline(self, l):
949 name = l.split(':')[1]
959 name = l.split(':')[1]
950 if name == '.hg.patches.save.line':
960 if name == '.hg.patches.save.line':
951 return True
961 return True
952
962
953 def qrepo(self, create=False):
963 def qrepo(self, create=False):
954 if create or os.path.isdir(os.path.join(self.path, ".hg")):
964 if create or os.path.isdir(os.path.join(self.path, ".hg")):
955 return hg.repository(self.ui, path=self.path, create=create)
965 return hg.repository(self.ui, path=self.path, create=create)
956
966
957 def restore(self, repo, rev, delete=None, qupdate=None):
967 def restore(self, repo, rev, delete=None, qupdate=None):
958 c = repo.changelog.read(rev)
968 c = repo.changelog.read(rev)
959 desc = c[4].strip()
969 desc = c[4].strip()
960 lines = desc.splitlines()
970 lines = desc.splitlines()
961 i = 0
971 i = 0
962 datastart = None
972 datastart = None
963 series = []
973 series = []
964 applied = []
974 applied = []
965 qpp = None
975 qpp = None
966 for i in xrange(0, len(lines)):
976 for i in xrange(0, len(lines)):
967 if lines[i] == 'Patch Data:':
977 if lines[i] == 'Patch Data:':
968 datastart = i + 1
978 datastart = i + 1
969 elif lines[i].startswith('Dirstate:'):
979 elif lines[i].startswith('Dirstate:'):
970 l = lines[i].rstrip()
980 l = lines[i].rstrip()
971 l = l[10:].split(' ')
981 l = l[10:].split(' ')
972 qpp = [ hg.bin(x) for x in l ]
982 qpp = [ hg.bin(x) for x in l ]
973 elif datastart != None:
983 elif datastart != None:
974 l = lines[i].rstrip()
984 l = lines[i].rstrip()
975 index = l.index(':')
985 index = l.index(':')
976 id = l[:index]
986 id = l[:index]
977 file = l[index + 1:]
987 file = l[index + 1:]
978 if id:
988 if id:
979 applied.append(l)
989 applied.append(l)
980 series.append(file)
990 series.append(file)
981 if datastart == None:
991 if datastart == None:
982 self.ui.warn("No saved patch data found\n")
992 self.ui.warn("No saved patch data found\n")
983 return 1
993 return 1
984 self.ui.warn("restoring status: %s\n" % lines[0])
994 self.ui.warn("restoring status: %s\n" % lines[0])
985 self.full_series = series
995 self.full_series = series
986 self.applied = applied
996 self.applied = applied
987 self.read_series(self.full_series)
997 self.read_series(self.full_series)
988 self.series_dirty = 1
998 self.series_dirty = 1
989 self.applied_dirty = 1
999 self.applied_dirty = 1
990 heads = repo.changelog.heads()
1000 heads = repo.changelog.heads()
991 if delete:
1001 if delete:
992 if rev not in heads:
1002 if rev not in heads:
993 self.ui.warn("save entry has children, leaving it alone\n")
1003 self.ui.warn("save entry has children, leaving it alone\n")
994 else:
1004 else:
995 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1005 self.ui.warn("removing save entry %s\n" % hg.short(rev))
996 pp = repo.dirstate.parents()
1006 pp = repo.dirstate.parents()
997 if rev in pp:
1007 if rev in pp:
998 update = True
1008 update = True
999 else:
1009 else:
1000 update = False
1010 update = False
1001 self.strip(repo, rev, update=update, backup='strip')
1011 self.strip(repo, rev, update=update, backup='strip')
1002 if qpp:
1012 if qpp:
1003 self.ui.warn("saved queue repository parents: %s %s\n" %
1013 self.ui.warn("saved queue repository parents: %s %s\n" %
1004 (hg.short(qpp[0]), hg.short(qpp[1])))
1014 (hg.short(qpp[0]), hg.short(qpp[1])))
1005 if qupdate:
1015 if qupdate:
1006 print "queue directory updating"
1016 print "queue directory updating"
1007 r = self.qrepo()
1017 r = self.qrepo()
1008 if not r:
1018 if not r:
1009 self.ui.warn("Unable to load queue repository\n")
1019 self.ui.warn("Unable to load queue repository\n")
1010 return 1
1020 return 1
1011 r.update(qpp[0], allow=False, force=True)
1021 r.update(qpp[0], allow=False, force=True)
1012
1022
1013 def save(self, repo, msg=None):
1023 def save(self, repo, msg=None):
1014 if len(self.applied) == 0:
1024 if len(self.applied) == 0:
1015 self.ui.warn("save: no patches applied, exiting\n")
1025 self.ui.warn("save: no patches applied, exiting\n")
1016 return 1
1026 return 1
1017 if self.issaveline(self.applied[-1]):
1027 if self.issaveline(self.applied[-1]):
1018 self.ui.warn("status is already saved\n")
1028 self.ui.warn("status is already saved\n")
1019 return 1
1029 return 1
1020
1030
1021 ar = [ ':' + x for x in self.full_series ]
1031 ar = [ ':' + x for x in self.full_series ]
1022 if not msg:
1032 if not msg:
1023 msg = "hg patches saved state"
1033 msg = "hg patches saved state"
1024 else:
1034 else:
1025 msg = "hg patches: " + msg.rstrip('\r\n')
1035 msg = "hg patches: " + msg.rstrip('\r\n')
1026 r = self.qrepo()
1036 r = self.qrepo()
1027 if r:
1037 if r:
1028 pp = r.dirstate.parents()
1038 pp = r.dirstate.parents()
1029 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1039 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1030 msg += "\n\nPatch Data:\n"
1040 msg += "\n\nPatch Data:\n"
1031 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1041 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1032 + '\n' or "")
1042 + '\n' or "")
1033 n = repo.commit(None, text, user=None, force=1)
1043 n = repo.commit(None, text, user=None, force=1)
1034 if not n:
1044 if not n:
1035 self.ui.warn("repo commit failed\n")
1045 self.ui.warn("repo commit failed\n")
1036 return 1
1046 return 1
1037 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1047 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1038 self.applied_dirty = 1
1048 self.applied_dirty = 1
1039
1049
1040 def full_series_end(self):
1050 def full_series_end(self):
1041 if len(self.applied) > 0:
1051 if len(self.applied) > 0:
1042 (top, p) = self.applied[-1].split(':')
1052 (top, p) = self.applied[-1].split(':')
1043 end = self.find_series(p)
1053 end = self.find_series(p)
1044 if end == None:
1054 if end == None:
1045 return len(self.full_series)
1055 return len(self.full_series)
1046 return end + 1
1056 return end + 1
1047 return 0
1057 return 0
1048
1058
1049 def series_end(self):
1059 def series_end(self):
1050 end = 0
1060 end = 0
1051 if len(self.applied) > 0:
1061 if len(self.applied) > 0:
1052 (top, p) = self.applied[-1].split(':')
1062 (top, p) = self.applied[-1].split(':')
1053 try:
1063 try:
1054 end = self.series.index(p)
1064 end = self.series.index(p)
1055 except ValueError:
1065 except ValueError:
1056 return 0
1066 return 0
1057 return end + 1
1067 return end + 1
1058 return end
1068 return end
1059
1069
1060 def qapplied(self, repo, patch=None):
1070 def qapplied(self, repo, patch=None):
1061 if patch and patch not in self.series:
1071 if patch and patch not in self.series:
1062 raise util.Abort(_("patch %s is not in series file") % patch)
1072 raise util.Abort(_("patch %s is not in series file") % patch)
1063 if not patch:
1073 if not patch:
1064 end = len(self.applied)
1074 end = len(self.applied)
1065 else:
1075 else:
1066 end = self.series.index(patch) + 1
1076 end = self.series.index(patch) + 1
1067 for x in xrange(end):
1077 for x in xrange(end):
1068 p = self.appliedname(x)
1078 p = self.appliedname(x)
1069 self.ui.write("%s\n" % p)
1079 self.ui.write("%s\n" % p)
1070
1080
1071 def appliedname(self, index):
1081 def appliedname(self, index):
1072 p = self.applied[index]
1082 p = self.applied[index]
1073 pname = p.split(':')[1]
1083 pname = p.split(':')[1]
1074 if not self.ui.verbose:
1084 if not self.ui.verbose:
1075 p = pname
1085 p = pname
1076 else:
1086 else:
1077 p = str(self.series.index(pname)) + " " + p
1087 p = str(self.series.index(pname)) + " " + p
1078 return p
1088 return p
1079
1089
1080 def top(self, repo):
1090 def top(self, repo):
1081 if len(self.applied):
1091 if len(self.applied):
1082 p = self.appliedname(-1)
1092 p = self.appliedname(-1)
1083 self.ui.write(p + '\n')
1093 self.ui.write(p + '\n')
1084 else:
1094 else:
1085 self.ui.write("No patches applied\n")
1095 self.ui.write("No patches applied\n")
1086
1096
1087 def next(self, repo):
1097 def next(self, repo):
1088 end = self.series_end()
1098 end = self.series_end()
1089 if end == len(self.series):
1099 if end == len(self.series):
1090 self.ui.write("All patches applied\n")
1100 self.ui.write("All patches applied\n")
1091 else:
1101 else:
1092 p = self.series[end]
1102 p = self.series[end]
1093 if self.ui.verbose:
1103 if self.ui.verbose:
1094 self.ui.write("%d " % self.series.index(p))
1104 self.ui.write("%d " % self.series.index(p))
1095 self.ui.write(p + '\n')
1105 self.ui.write(p + '\n')
1096
1106
1097 def prev(self, repo):
1107 def prev(self, repo):
1098 if len(self.applied) > 1:
1108 if len(self.applied) > 1:
1099 p = self.appliedname(-2)
1109 p = self.appliedname(-2)
1100 self.ui.write(p + '\n')
1110 self.ui.write(p + '\n')
1101 elif len(self.applied) == 1:
1111 elif len(self.applied) == 1:
1102 self.ui.write("Only one patch applied\n")
1112 self.ui.write("Only one patch applied\n")
1103 else:
1113 else:
1104 self.ui.write("No patches applied\n")
1114 self.ui.write("No patches applied\n")
1105
1115
1106 def qimport(self, repo, files, patch=None, existing=None, force=None):
1116 def qimport(self, repo, files, patch=None, existing=None, force=None):
1107 if len(files) > 1 and patch:
1117 if len(files) > 1 and patch:
1108 raise util.Abort(_('option "-n" not valid when importing multiple '
1118 raise util.Abort(_('option "-n" not valid when importing multiple '
1109 'files'))
1119 'files'))
1110 i = 0
1120 i = 0
1111 added = []
1121 added = []
1112 for filename in files:
1122 for filename in files:
1113 if existing:
1123 if existing:
1114 if not patch:
1124 if not patch:
1115 patch = filename
1125 patch = filename
1116 if not os.path.isfile(os.path.join(self.path, patch)):
1126 if not os.path.isfile(os.path.join(self.path, patch)):
1117 raise util.Abort(_("patch %s does not exist") % patch)
1127 raise util.Abort(_("patch %s does not exist") % patch)
1118 else:
1128 else:
1119 try:
1129 try:
1120 text = file(filename).read()
1130 text = file(filename).read()
1121 except IOError:
1131 except IOError:
1122 raise util.Abort(_("unable to read %s") % patch)
1132 raise util.Abort(_("unable to read %s") % patch)
1123 if not patch:
1133 if not patch:
1124 patch = os.path.split(filename)[1]
1134 patch = os.path.split(filename)[1]
1125 if not force and os.path.exists(os.path.join(self.path, patch)):
1135 if not force and os.path.exists(os.path.join(self.path, patch)):
1126 raise util.Abort(_('patch "%s" already exists') % patch)
1136 raise util.Abort(_('patch "%s" already exists') % patch)
1127 patchf = self.opener(patch, "w")
1137 patchf = self.opener(patch, "w")
1128 patchf.write(text)
1138 patchf.write(text)
1129 if patch in self.series:
1139 if patch in self.series:
1130 raise util.Abort(_('patch %s is already in the series file')
1140 raise util.Abort(_('patch %s is already in the series file')
1131 % patch)
1141 % patch)
1132 index = self.full_series_end() + i
1142 index = self.full_series_end() + i
1133 self.full_series[index:index] = [patch]
1143 self.full_series[index:index] = [patch]
1134 self.read_series(self.full_series)
1144 self.read_series(self.full_series)
1135 self.ui.warn("adding %s to series file\n" % patch)
1145 self.ui.warn("adding %s to series file\n" % patch)
1136 i += 1
1146 i += 1
1137 added.append(patch)
1147 added.append(patch)
1138 patch = None
1148 patch = None
1139 self.series_dirty = 1
1149 self.series_dirty = 1
1140 qrepo = self.qrepo()
1150 qrepo = self.qrepo()
1141 if qrepo:
1151 if qrepo:
1142 qrepo.add(added)
1152 qrepo.add(added)
1143
1153
1144 def delete(ui, repo, patch, **opts):
1154 def delete(ui, repo, patch, **opts):
1145 """remove a patch from the series file"""
1155 """remove a patch from the series file"""
1146 q = repo.mq
1156 q = repo.mq
1147 q.delete(repo, patch)
1157 q.delete(repo, patch)
1148 q.save_dirty()
1158 q.save_dirty()
1149 return 0
1159 return 0
1150
1160
1151 def applied(ui, repo, patch=None, **opts):
1161 def applied(ui, repo, patch=None, **opts):
1152 """print the patches already applied"""
1162 """print the patches already applied"""
1153 repo.mq.qapplied(repo, patch)
1163 repo.mq.qapplied(repo, patch)
1154 return 0
1164 return 0
1155
1165
1156 def unapplied(ui, repo, patch=None, **opts):
1166 def unapplied(ui, repo, patch=None, **opts):
1157 """print the patches not yet applied"""
1167 """print the patches not yet applied"""
1158 repo.mq.unapplied(repo, patch)
1168 repo.mq.unapplied(repo, patch)
1159 return 0
1169 return 0
1160
1170
1161 def qimport(ui, repo, *filename, **opts):
1171 def qimport(ui, repo, *filename, **opts):
1162 """import a patch"""
1172 """import a patch"""
1163 q = repo.mq
1173 q = repo.mq
1164 q.qimport(repo, filename, patch=opts['name'],
1174 q.qimport(repo, filename, patch=opts['name'],
1165 existing=opts['existing'], force=opts['force'])
1175 existing=opts['existing'], force=opts['force'])
1166 q.save_dirty()
1176 q.save_dirty()
1167 return 0
1177 return 0
1168
1178
1169 def init(ui, repo, **opts):
1179 def init(ui, repo, **opts):
1170 """init a new queue repository"""
1180 """init a new queue repository"""
1171 q = repo.mq
1181 q = repo.mq
1172 r = q.init(repo, create=opts['create_repo'])
1182 r = q.init(repo, create=opts['create_repo'])
1173 q.save_dirty()
1183 q.save_dirty()
1174 if r:
1184 if r:
1175 fp = r.wopener('.hgignore', 'w')
1185 fp = r.wopener('.hgignore', 'w')
1176 print >> fp, 'syntax: glob'
1186 print >> fp, 'syntax: glob'
1177 print >> fp, 'status'
1187 print >> fp, 'status'
1178 fp.close()
1188 fp.close()
1179 r.wopener('series', 'w').close()
1189 r.wopener('series', 'w').close()
1180 r.add(['.hgignore', 'series'])
1190 r.add(['.hgignore', 'series'])
1181 return 0
1191 return 0
1182
1192
1183 def clone(ui, source, dest=None, **opts):
1193 def clone(ui, source, dest=None, **opts):
1184 '''clone main and patch repository at same time
1194 '''clone main and patch repository at same time
1185
1195
1186 If source is local, destination will have no patches applied. If
1196 If source is local, destination will have no patches applied. If
1187 source is remote, this command can not check if patches are
1197 source is remote, this command can not check if patches are
1188 applied in source, so cannot guarantee that patches are not
1198 applied in source, so cannot guarantee that patches are not
1189 applied in destination. If you clone remote repository, be sure
1199 applied in destination. If you clone remote repository, be sure
1190 before that it has no patches applied.
1200 before that it has no patches applied.
1191
1201
1192 Source patch repository is looked for in <src>/.hg/patches by
1202 Source patch repository is looked for in <src>/.hg/patches by
1193 default. Use -p <url> to change.
1203 default. Use -p <url> to change.
1194 '''
1204 '''
1195 commands.setremoteconfig(**opts)
1205 commands.setremoteconfig(**opts)
1196 if dest is None:
1206 if dest is None:
1197 dest = hg.defaultdest(source)
1207 dest = hg.defaultdest(source)
1198 sr = hg.repository(ui, ui.expandpath(source))
1208 sr = hg.repository(ui, ui.expandpath(source))
1199 qbase, destrev = None, None
1209 qbase, destrev = None, None
1200 if sr.local():
1210 if sr.local():
1201 reposetup(ui, sr)
1211 reposetup(ui, sr)
1202 if sr.mq.applied:
1212 if sr.mq.applied:
1203 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1213 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1204 if not hg.islocal(dest):
1214 if not hg.islocal(dest):
1205 destrev = sr.parents(qbase)[0]
1215 destrev = sr.parents(qbase)[0]
1206 ui.note(_('cloning main repo\n'))
1216 ui.note(_('cloning main repo\n'))
1207 sr, dr = hg.clone(ui, sr, dest,
1217 sr, dr = hg.clone(ui, sr, dest,
1208 pull=opts['pull'],
1218 pull=opts['pull'],
1209 rev=destrev,
1219 rev=destrev,
1210 update=False,
1220 update=False,
1211 stream=opts['uncompressed'])
1221 stream=opts['uncompressed'])
1212 ui.note(_('cloning patch repo\n'))
1222 ui.note(_('cloning patch repo\n'))
1213 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1223 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1214 dr.url() + '/.hg/patches',
1224 dr.url() + '/.hg/patches',
1215 pull=opts['pull'],
1225 pull=opts['pull'],
1216 update=not opts['noupdate'],
1226 update=not opts['noupdate'],
1217 stream=opts['uncompressed'])
1227 stream=opts['uncompressed'])
1218 if dr.local():
1228 if dr.local():
1219 if qbase:
1229 if qbase:
1220 ui.note(_('stripping applied patches from destination repo\n'))
1230 ui.note(_('stripping applied patches from destination repo\n'))
1221 reposetup(ui, dr)
1231 reposetup(ui, dr)
1222 dr.mq.strip(dr, qbase, update=False, backup=None)
1232 dr.mq.strip(dr, qbase, update=False, backup=None)
1223 if not opts['noupdate']:
1233 if not opts['noupdate']:
1224 ui.note(_('updating destination repo\n'))
1234 ui.note(_('updating destination repo\n'))
1225 dr.update(dr.changelog.tip())
1235 dr.update(dr.changelog.tip())
1226
1236
1227 def commit(ui, repo, *pats, **opts):
1237 def commit(ui, repo, *pats, **opts):
1228 """commit changes in the queue repository"""
1238 """commit changes in the queue repository"""
1229 q = repo.mq
1239 q = repo.mq
1230 r = q.qrepo()
1240 r = q.qrepo()
1231 if not r: raise util.Abort('no queue repository')
1241 if not r: raise util.Abort('no queue repository')
1232 commands.commit(r.ui, r, *pats, **opts)
1242 commands.commit(r.ui, r, *pats, **opts)
1233
1243
1234 def series(ui, repo, **opts):
1244 def series(ui, repo, **opts):
1235 """print the entire series file"""
1245 """print the entire series file"""
1236 repo.mq.qseries(repo, missing=opts['missing'])
1246 repo.mq.qseries(repo, missing=opts['missing'])
1237 return 0
1247 return 0
1238
1248
1239 def top(ui, repo, **opts):
1249 def top(ui, repo, **opts):
1240 """print the name of the current patch"""
1250 """print the name of the current patch"""
1241 repo.mq.top(repo)
1251 repo.mq.top(repo)
1242 return 0
1252 return 0
1243
1253
1244 def next(ui, repo, **opts):
1254 def next(ui, repo, **opts):
1245 """print the name of the next patch"""
1255 """print the name of the next patch"""
1246 repo.mq.next(repo)
1256 repo.mq.next(repo)
1247 return 0
1257 return 0
1248
1258
1249 def prev(ui, repo, **opts):
1259 def prev(ui, repo, **opts):
1250 """print the name of the previous patch"""
1260 """print the name of the previous patch"""
1251 repo.mq.prev(repo)
1261 repo.mq.prev(repo)
1252 return 0
1262 return 0
1253
1263
1254 def new(ui, repo, patch, **opts):
1264 def new(ui, repo, patch, **opts):
1255 """create a new patch"""
1265 """create a new patch"""
1256 q = repo.mq
1266 q = repo.mq
1257 message=commands.logmessage(**opts)
1267 message=commands.logmessage(**opts)
1258 q.new(repo, patch, msg=message, force=opts['force'])
1268 q.new(repo, patch, msg=message, force=opts['force'])
1259 q.save_dirty()
1269 q.save_dirty()
1260 return 0
1270 return 0
1261
1271
1262 def refresh(ui, repo, **opts):
1272 def refresh(ui, repo, **opts):
1263 """update the current patch"""
1273 """update the current patch"""
1264 q = repo.mq
1274 q = repo.mq
1265 message=commands.logmessage(**opts)
1275 message=commands.logmessage(**opts)
1266 q.refresh(repo, msg=message, short=opts['short'])
1276 q.refresh(repo, msg=message, short=opts['short'])
1267 q.save_dirty()
1277 q.save_dirty()
1268 return 0
1278 return 0
1269
1279
1270 def diff(ui, repo, *files, **opts):
1280 def diff(ui, repo, *files, **opts):
1271 """diff of the current patch"""
1281 """diff of the current patch"""
1272 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1282 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1273 repo.mq.diff(repo, list(files))
1283 repo.mq.diff(repo, list(files))
1274 return 0
1284 return 0
1275
1285
1276 def lastsavename(path):
1286 def lastsavename(path):
1277 (dir, base) = os.path.split(path)
1287 (dir, base) = os.path.split(path)
1278 names = os.listdir(dir)
1288 names = os.listdir(dir)
1279 namere = re.compile("%s.([0-9]+)" % base)
1289 namere = re.compile("%s.([0-9]+)" % base)
1280 max = None
1290 max = None
1281 maxname = None
1291 maxname = None
1282 for f in names:
1292 for f in names:
1283 m = namere.match(f)
1293 m = namere.match(f)
1284 if m:
1294 if m:
1285 index = int(m.group(1))
1295 index = int(m.group(1))
1286 if max == None or index > max:
1296 if max == None or index > max:
1287 max = index
1297 max = index
1288 maxname = f
1298 maxname = f
1289 if maxname:
1299 if maxname:
1290 return (os.path.join(dir, maxname), max)
1300 return (os.path.join(dir, maxname), max)
1291 return (None, None)
1301 return (None, None)
1292
1302
1293 def savename(path):
1303 def savename(path):
1294 (last, index) = lastsavename(path)
1304 (last, index) = lastsavename(path)
1295 if last is None:
1305 if last is None:
1296 index = 0
1306 index = 0
1297 newpath = path + ".%d" % (index + 1)
1307 newpath = path + ".%d" % (index + 1)
1298 return newpath
1308 return newpath
1299
1309
1300 def push(ui, repo, patch=None, **opts):
1310 def push(ui, repo, patch=None, **opts):
1301 """push the next patch onto the stack"""
1311 """push the next patch onto the stack"""
1302 q = repo.mq
1312 q = repo.mq
1303 mergeq = None
1313 mergeq = None
1304
1314
1305 if opts['all']:
1315 if opts['all']:
1306 patch = q.series[-1]
1316 patch = q.series[-1]
1307 if opts['merge']:
1317 if opts['merge']:
1308 if opts['name']:
1318 if opts['name']:
1309 newpath = opts['name']
1319 newpath = opts['name']
1310 else:
1320 else:
1311 newpath, i = lastsavename(q.path)
1321 newpath, i = lastsavename(q.path)
1312 if not newpath:
1322 if not newpath:
1313 ui.warn("no saved queues found, please use -n\n")
1323 ui.warn("no saved queues found, please use -n\n")
1314 return 1
1324 return 1
1315 mergeq = queue(ui, repo.join(""), newpath)
1325 mergeq = queue(ui, repo.join(""), newpath)
1316 ui.warn("merging with queue at: %s\n" % mergeq.path)
1326 ui.warn("merging with queue at: %s\n" % mergeq.path)
1317 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1327 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1318 mergeq=mergeq)
1328 mergeq=mergeq)
1319 q.save_dirty()
1329 q.save_dirty()
1320 return ret
1330 return ret
1321
1331
1322 def pop(ui, repo, patch=None, **opts):
1332 def pop(ui, repo, patch=None, **opts):
1323 """pop the current patch off the stack"""
1333 """pop the current patch off the stack"""
1324 localupdate = True
1334 localupdate = True
1325 if opts['name']:
1335 if opts['name']:
1326 q = queue(ui, repo.join(""), repo.join(opts['name']))
1336 q = queue(ui, repo.join(""), repo.join(opts['name']))
1327 ui.warn('using patch queue: %s\n' % q.path)
1337 ui.warn('using patch queue: %s\n' % q.path)
1328 localupdate = False
1338 localupdate = False
1329 else:
1339 else:
1330 q = repo.mq
1340 q = repo.mq
1331 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1341 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1332 q.save_dirty()
1342 q.save_dirty()
1333 return 0
1343 return 0
1334
1344
1335 def restore(ui, repo, rev, **opts):
1345 def restore(ui, repo, rev, **opts):
1336 """restore the queue state saved by a rev"""
1346 """restore the queue state saved by a rev"""
1337 rev = repo.lookup(rev)
1347 rev = repo.lookup(rev)
1338 q = repo.mq
1348 q = repo.mq
1339 q.restore(repo, rev, delete=opts['delete'],
1349 q.restore(repo, rev, delete=opts['delete'],
1340 qupdate=opts['update'])
1350 qupdate=opts['update'])
1341 q.save_dirty()
1351 q.save_dirty()
1342 return 0
1352 return 0
1343
1353
1344 def save(ui, repo, **opts):
1354 def save(ui, repo, **opts):
1345 """save current queue state"""
1355 """save current queue state"""
1346 q = repo.mq
1356 q = repo.mq
1347 message=commands.logmessage(**opts)
1357 message=commands.logmessage(**opts)
1348 ret = q.save(repo, msg=message)
1358 ret = q.save(repo, msg=message)
1349 if ret:
1359 if ret:
1350 return ret
1360 return ret
1351 q.save_dirty()
1361 q.save_dirty()
1352 if opts['copy']:
1362 if opts['copy']:
1353 path = q.path
1363 path = q.path
1354 if opts['name']:
1364 if opts['name']:
1355 newpath = os.path.join(q.basepath, opts['name'])
1365 newpath = os.path.join(q.basepath, opts['name'])
1356 if os.path.exists(newpath):
1366 if os.path.exists(newpath):
1357 if not os.path.isdir(newpath):
1367 if not os.path.isdir(newpath):
1358 raise util.Abort(_('destination %s exists and is not '
1368 raise util.Abort(_('destination %s exists and is not '
1359 'a directory') % newpath)
1369 'a directory') % newpath)
1360 if not opts['force']:
1370 if not opts['force']:
1361 raise util.Abort(_('destination %s exists, '
1371 raise util.Abort(_('destination %s exists, '
1362 'use -f to force') % newpath)
1372 'use -f to force') % newpath)
1363 else:
1373 else:
1364 newpath = savename(path)
1374 newpath = savename(path)
1365 ui.warn("copy %s to %s\n" % (path, newpath))
1375 ui.warn("copy %s to %s\n" % (path, newpath))
1366 util.copyfiles(path, newpath)
1376 util.copyfiles(path, newpath)
1367 if opts['empty']:
1377 if opts['empty']:
1368 try:
1378 try:
1369 os.unlink(os.path.join(q.path, q.status_path))
1379 os.unlink(os.path.join(q.path, q.status_path))
1370 except:
1380 except:
1371 pass
1381 pass
1372 return 0
1382 return 0
1373
1383
1374 def strip(ui, repo, rev, **opts):
1384 def strip(ui, repo, rev, **opts):
1375 """strip a revision and all later revs on the same branch"""
1385 """strip a revision and all later revs on the same branch"""
1376 rev = repo.lookup(rev)
1386 rev = repo.lookup(rev)
1377 backup = 'all'
1387 backup = 'all'
1378 if opts['backup']:
1388 if opts['backup']:
1379 backup = 'strip'
1389 backup = 'strip'
1380 elif opts['nobackup']:
1390 elif opts['nobackup']:
1381 backup = 'none'
1391 backup = 'none'
1382 repo.mq.strip(repo, rev, backup=backup)
1392 repo.mq.strip(repo, rev, backup=backup)
1383 return 0
1393 return 0
1384
1394
1385 def version(ui, q=None):
1395 def version(ui, q=None):
1386 """print the version number"""
1396 """print the version number"""
1387 ui.write("mq version %s\n" % versionstr)
1397 ui.write("mq version %s\n" % versionstr)
1388 return 0
1398 return 0
1389
1399
1390 def reposetup(ui, repo):
1400 def reposetup(ui, repo):
1391 class MqRepo(repo.__class__):
1401 class MqRepo(repo.__class__):
1392 def tags(self):
1402 def tags(self):
1393 if self.tagscache:
1403 if self.tagscache:
1394 return self.tagscache
1404 return self.tagscache
1395
1405
1396 tagscache = super(MqRepo, self).tags()
1406 tagscache = super(MqRepo, self).tags()
1397
1407
1398 q = self.mq
1408 q = self.mq
1399 if not q.applied:
1409 if not q.applied:
1400 return tagscache
1410 return tagscache
1401
1411
1402 mqtags = [patch.split(':') for patch in q.applied]
1412 mqtags = [patch.split(':') for patch in q.applied]
1403 mqtags.append((mqtags[-1][0], 'qtip'))
1413 mqtags.append((mqtags[-1][0], 'qtip'))
1404 mqtags.append((mqtags[0][0], 'qbase'))
1414 mqtags.append((mqtags[0][0], 'qbase'))
1405 for patch in mqtags:
1415 for patch in mqtags:
1406 if patch[1] in tagscache:
1416 if patch[1] in tagscache:
1407 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1417 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1408 else:
1418 else:
1409 tagscache[patch[1]] = revlog.bin(patch[0])
1419 tagscache[patch[1]] = revlog.bin(patch[0])
1410
1420
1411 return tagscache
1421 return tagscache
1412
1422
1413 repo.__class__ = MqRepo
1423 repo.__class__ = MqRepo
1414 repo.mq = queue(ui, repo.join(""))
1424 repo.mq = queue(ui, repo.join(""))
1415
1425
1416 cmdtable = {
1426 cmdtable = {
1417 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1427 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1418 "qclone": (clone,
1428 "qclone": (clone,
1419 [('', 'pull', None, _('use pull protocol to copy metadata')),
1429 [('', 'pull', None, _('use pull protocol to copy metadata')),
1420 ('U', 'noupdate', None, _('do not update the new working directories')),
1430 ('U', 'noupdate', None, _('do not update the new working directories')),
1421 ('', 'uncompressed', None,
1431 ('', 'uncompressed', None,
1422 _('use uncompressed transfer (fast over LAN)')),
1432 _('use uncompressed transfer (fast over LAN)')),
1423 ('e', 'ssh', '', _('specify ssh command to use')),
1433 ('e', 'ssh', '', _('specify ssh command to use')),
1424 ('p', 'patches', '', _('location of source patch repo')),
1434 ('p', 'patches', '', _('location of source patch repo')),
1425 ('', 'remotecmd', '',
1435 ('', 'remotecmd', '',
1426 _('specify hg command to run on the remote side'))],
1436 _('specify hg command to run on the remote side'))],
1427 'hg qclone [OPTION]... SOURCE [DEST]'),
1437 'hg qclone [OPTION]... SOURCE [DEST]'),
1428 "qcommit|qci":
1438 "qcommit|qci":
1429 (commit,
1439 (commit,
1430 commands.table["^commit|ci"][1],
1440 commands.table["^commit|ci"][1],
1431 'hg qcommit [OPTION]... [FILE]...'),
1441 'hg qcommit [OPTION]... [FILE]...'),
1432 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1442 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1433 "qdelete": (delete, [], 'hg qdelete PATCH'),
1443 "qdelete": (delete, [], 'hg qdelete PATCH'),
1434 "^qimport":
1444 "^qimport":
1435 (qimport,
1445 (qimport,
1436 [('e', 'existing', None, 'import file in patch dir'),
1446 [('e', 'existing', None, 'import file in patch dir'),
1437 ('n', 'name', '', 'patch file name'),
1447 ('n', 'name', '', 'patch file name'),
1438 ('f', 'force', None, 'overwrite existing files')],
1448 ('f', 'force', None, 'overwrite existing files')],
1439 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1449 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1440 "^qinit":
1450 "^qinit":
1441 (init,
1451 (init,
1442 [('c', 'create-repo', None, 'create queue repository')],
1452 [('c', 'create-repo', None, 'create queue repository')],
1443 'hg qinit [-c]'),
1453 'hg qinit [-c]'),
1444 "qnew":
1454 "qnew":
1445 (new,
1455 (new,
1446 [('m', 'message', '', _('use <text> as commit message')),
1456 [('m', 'message', '', _('use <text> as commit message')),
1447 ('l', 'logfile', '', _('read the commit message from <file>')),
1457 ('l', 'logfile', '', _('read the commit message from <file>')),
1448 ('f', 'force', None, 'force')],
1458 ('f', 'force', None, 'force')],
1449 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1459 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1450 "qnext": (next, [], 'hg qnext'),
1460 "qnext": (next, [], 'hg qnext'),
1451 "qprev": (prev, [], 'hg qprev'),
1461 "qprev": (prev, [], 'hg qprev'),
1452 "^qpop":
1462 "^qpop":
1453 (pop,
1463 (pop,
1454 [('a', 'all', None, 'pop all patches'),
1464 [('a', 'all', None, 'pop all patches'),
1455 ('n', 'name', '', 'queue name to pop'),
1465 ('n', 'name', '', 'queue name to pop'),
1456 ('f', 'force', None, 'forget any local changes')],
1466 ('f', 'force', None, 'forget any local changes')],
1457 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1467 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1458 "^qpush":
1468 "^qpush":
1459 (push,
1469 (push,
1460 [('f', 'force', None, 'apply if the patch has rejects'),
1470 [('f', 'force', None, 'apply if the patch has rejects'),
1461 ('l', 'list', None, 'list patch name in commit text'),
1471 ('l', 'list', None, 'list patch name in commit text'),
1462 ('a', 'all', None, 'apply all patches'),
1472 ('a', 'all', None, 'apply all patches'),
1463 ('m', 'merge', None, 'merge from another queue'),
1473 ('m', 'merge', None, 'merge from another queue'),
1464 ('n', 'name', '', 'merge queue name')],
1474 ('n', 'name', '', 'merge queue name')],
1465 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1475 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1466 "^qrefresh":
1476 "^qrefresh":
1467 (refresh,
1477 (refresh,
1468 [('m', 'message', '', _('change commit message with <text>')),
1478 [('m', 'message', '', _('change commit message with <text>')),
1469 ('l', 'logfile', '', _('change commit message with <file> content')),
1479 ('l', 'logfile', '', _('change commit message with <file> content')),
1470 ('s', 'short', None, 'short refresh')],
1480 ('s', 'short', None, 'short refresh')],
1471 'hg qrefresh [-m TEXT] [-l FILE] [-s]'),
1481 'hg qrefresh [-m TEXT] [-l FILE] [-s]'),
1472 "qrestore":
1482 "qrestore":
1473 (restore,
1483 (restore,
1474 [('d', 'delete', None, 'delete save entry'),
1484 [('d', 'delete', None, 'delete save entry'),
1475 ('u', 'update', None, 'update queue working dir')],
1485 ('u', 'update', None, 'update queue working dir')],
1476 'hg qrestore [-d] [-u] REV'),
1486 'hg qrestore [-d] [-u] REV'),
1477 "qsave":
1487 "qsave":
1478 (save,
1488 (save,
1479 [('m', 'message', '', _('use <text> as commit message')),
1489 [('m', 'message', '', _('use <text> as commit message')),
1480 ('l', 'logfile', '', _('read the commit message from <file>')),
1490 ('l', 'logfile', '', _('read the commit message from <file>')),
1481 ('c', 'copy', None, 'copy patch directory'),
1491 ('c', 'copy', None, 'copy patch directory'),
1482 ('n', 'name', '', 'copy directory name'),
1492 ('n', 'name', '', 'copy directory name'),
1483 ('e', 'empty', None, 'clear queue status file'),
1493 ('e', 'empty', None, 'clear queue status file'),
1484 ('f', 'force', None, 'force copy')],
1494 ('f', 'force', None, 'force copy')],
1485 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1495 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1486 "qseries":
1496 "qseries":
1487 (series,
1497 (series,
1488 [('m', 'missing', None, 'print patches not in series')],
1498 [('m', 'missing', None, 'print patches not in series')],
1489 'hg qseries [-m]'),
1499 'hg qseries [-m]'),
1490 "^strip":
1500 "^strip":
1491 (strip,
1501 (strip,
1492 [('f', 'force', None, 'force multi-head removal'),
1502 [('f', 'force', None, 'force multi-head removal'),
1493 ('b', 'backup', None, 'bundle unrelated changesets'),
1503 ('b', 'backup', None, 'bundle unrelated changesets'),
1494 ('n', 'nobackup', None, 'no backups')],
1504 ('n', 'nobackup', None, 'no backups')],
1495 'hg strip [-f] [-b] [-n] REV'),
1505 'hg strip [-f] [-b] [-n] REV'),
1496 "qtop": (top, [], 'hg qtop'),
1506 "qtop": (top, [], 'hg qtop'),
1497 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1507 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1498 "qversion": (version, [], 'hg qversion')
1508 "qversion": (version, [], 'hg qversion')
1499 }
1509 }
1500
1510
General Comments 0
You need to be logged in to leave comments. Login now