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