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