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