##// END OF EJS Templates
mq: hg qnew -f should refresh the new patch...
Chris Mason -
r2511:041d8f0a default
parent child Browse files
Show More
@@ -1,1314 +1,1322 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 commitfiles = []
390 self.check_localchanges(repo)
390 (c, a, r, d, u) = repo.changes(None, None)
391 if c or a or d or r:
392 if not force:
393 raise util.Abort(_("Local changes found, refresh first"))
394 else:
395 commitfiles = c + a + r
391 self.check_toppatch(repo)
396 self.check_toppatch(repo)
392 wlock = repo.wlock()
397 wlock = repo.wlock()
393 insert = self.series_end()
398 insert = self.series_end()
394 if msg:
399 if msg:
395 n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock)
400 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
401 wlock=wlock)
396 else:
402 else:
397 n = repo.commit([],
403 n = repo.commit(commitfiles,
398 "New patch: %s" % patch, force=True, wlock=wlock)
404 "New patch: %s" % patch, force=True, wlock=wlock)
399 if n == None:
405 if n == None:
400 self.ui.warn("repo commit failed\n")
406 self.ui.warn("repo commit failed\n")
401 sys.exit(1)
407 sys.exit(1)
402 self.full_series[insert:insert] = [patch]
408 self.full_series[insert:insert] = [patch]
403 self.applied.append(revlog.hex(n) + ":" + patch)
409 self.applied.append(revlog.hex(n) + ":" + patch)
404 self.read_series(self.full_series)
410 self.read_series(self.full_series)
405 self.series_dirty = 1
411 self.series_dirty = 1
406 self.applied_dirty = 1
412 self.applied_dirty = 1
407 p = self.opener(patch, "w")
413 p = self.opener(patch, "w")
408 if msg:
414 if msg:
409 msg = msg + "\n"
415 msg = msg + "\n"
410 p.write(msg)
416 p.write(msg)
411 p.close()
417 p.close()
412 wlock = None
418 wlock = None
413 r = self.qrepo()
419 r = self.qrepo()
414 if r: r.add([patch])
420 if r: r.add([patch])
421 if commitfiles:
422 self.refresh(repo, short=True)
415
423
416 def strip(self, repo, rev, update=True, backup="all", wlock=None):
424 def strip(self, repo, rev, update=True, backup="all", wlock=None):
417 def limitheads(chlog, stop):
425 def limitheads(chlog, stop):
418 """return the list of all nodes that have no children"""
426 """return the list of all nodes that have no children"""
419 p = {}
427 p = {}
420 h = []
428 h = []
421 stoprev = 0
429 stoprev = 0
422 if stop in chlog.nodemap:
430 if stop in chlog.nodemap:
423 stoprev = chlog.rev(stop)
431 stoprev = chlog.rev(stop)
424
432
425 for r in range(chlog.count() - 1, -1, -1):
433 for r in range(chlog.count() - 1, -1, -1):
426 n = chlog.node(r)
434 n = chlog.node(r)
427 if n not in p:
435 if n not in p:
428 h.append(n)
436 h.append(n)
429 if n == stop:
437 if n == stop:
430 break
438 break
431 if r < stoprev:
439 if r < stoprev:
432 break
440 break
433 for pn in chlog.parents(n):
441 for pn in chlog.parents(n):
434 p[pn] = 1
442 p[pn] = 1
435 return h
443 return h
436
444
437 def bundle(cg):
445 def bundle(cg):
438 backupdir = repo.join("strip-backup")
446 backupdir = repo.join("strip-backup")
439 if not os.path.isdir(backupdir):
447 if not os.path.isdir(backupdir):
440 os.mkdir(backupdir)
448 os.mkdir(backupdir)
441 name = os.path.join(backupdir, "%s" % revlog.short(rev))
449 name = os.path.join(backupdir, "%s" % revlog.short(rev))
442 name = savename(name)
450 name = savename(name)
443 self.ui.warn("saving bundle to %s\n" % name)
451 self.ui.warn("saving bundle to %s\n" % name)
444 # TODO, exclusive open
452 # TODO, exclusive open
445 f = open(name, "wb")
453 f = open(name, "wb")
446 try:
454 try:
447 f.write("HG10")
455 f.write("HG10")
448 z = bz2.BZ2Compressor(9)
456 z = bz2.BZ2Compressor(9)
449 while 1:
457 while 1:
450 chunk = cg.read(4096)
458 chunk = cg.read(4096)
451 if not chunk:
459 if not chunk:
452 break
460 break
453 f.write(z.compress(chunk))
461 f.write(z.compress(chunk))
454 f.write(z.flush())
462 f.write(z.flush())
455 except:
463 except:
456 os.unlink(name)
464 os.unlink(name)
457 raise
465 raise
458 f.close()
466 f.close()
459 return name
467 return name
460
468
461 def stripall(rev, revnum):
469 def stripall(rev, revnum):
462 cl = repo.changelog
470 cl = repo.changelog
463 c = cl.read(rev)
471 c = cl.read(rev)
464 mm = repo.manifest.read(c[0])
472 mm = repo.manifest.read(c[0])
465 seen = {}
473 seen = {}
466
474
467 for x in xrange(revnum, cl.count()):
475 for x in xrange(revnum, cl.count()):
468 c = cl.read(cl.node(x))
476 c = cl.read(cl.node(x))
469 for f in c[3]:
477 for f in c[3]:
470 if f in seen:
478 if f in seen:
471 continue
479 continue
472 seen[f] = 1
480 seen[f] = 1
473 if f in mm:
481 if f in mm:
474 filerev = mm[f]
482 filerev = mm[f]
475 else:
483 else:
476 filerev = 0
484 filerev = 0
477 seen[f] = filerev
485 seen[f] = filerev
478 # we go in two steps here so the strip loop happens in a
486 # we go in two steps here so the strip loop happens in a
479 # sensible order. When stripping many files, this helps keep
487 # sensible order. When stripping many files, this helps keep
480 # our disk access patterns under control.
488 # our disk access patterns under control.
481 list = seen.keys()
489 list = seen.keys()
482 list.sort()
490 list.sort()
483 for f in list:
491 for f in list:
484 ff = repo.file(f)
492 ff = repo.file(f)
485 filerev = seen[f]
493 filerev = seen[f]
486 if filerev != 0:
494 if filerev != 0:
487 if filerev in ff.nodemap:
495 if filerev in ff.nodemap:
488 filerev = ff.rev(filerev)
496 filerev = ff.rev(filerev)
489 else:
497 else:
490 filerev = 0
498 filerev = 0
491 ff.strip(filerev, revnum)
499 ff.strip(filerev, revnum)
492
500
493 if not wlock:
501 if not wlock:
494 wlock = repo.wlock()
502 wlock = repo.wlock()
495 lock = repo.lock()
503 lock = repo.lock()
496 chlog = repo.changelog
504 chlog = repo.changelog
497 # TODO delete the undo files, and handle undo of merge sets
505 # TODO delete the undo files, and handle undo of merge sets
498 pp = chlog.parents(rev)
506 pp = chlog.parents(rev)
499 revnum = chlog.rev(rev)
507 revnum = chlog.rev(rev)
500
508
501 if update:
509 if update:
502 urev = self.qparents(repo, rev)
510 urev = self.qparents(repo, rev)
503 repo.update(urev, allow=False, force=True, wlock=wlock)
511 repo.update(urev, allow=False, force=True, wlock=wlock)
504 repo.dirstate.write()
512 repo.dirstate.write()
505
513
506 # save is a list of all the branches we are truncating away
514 # save is a list of all the branches we are truncating away
507 # that we actually want to keep. changegroup will be used
515 # that we actually want to keep. changegroup will be used
508 # to preserve them and add them back after the truncate
516 # to preserve them and add them back after the truncate
509 saveheads = []
517 saveheads = []
510 savebases = {}
518 savebases = {}
511
519
512 tip = chlog.tip()
520 tip = chlog.tip()
513 heads = limitheads(chlog, rev)
521 heads = limitheads(chlog, rev)
514 seen = {}
522 seen = {}
515
523
516 # search through all the heads, finding those where the revision
524 # search through all the heads, finding those where the revision
517 # we want to strip away is an ancestor. Also look for merges
525 # we want to strip away is an ancestor. Also look for merges
518 # that might be turned into new heads by the strip.
526 # that might be turned into new heads by the strip.
519 while heads:
527 while heads:
520 h = heads.pop()
528 h = heads.pop()
521 n = h
529 n = h
522 while True:
530 while True:
523 seen[n] = 1
531 seen[n] = 1
524 pp = chlog.parents(n)
532 pp = chlog.parents(n)
525 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
533 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
526 if pp[1] not in seen:
534 if pp[1] not in seen:
527 heads.append(pp[1])
535 heads.append(pp[1])
528 if pp[0] == revlog.nullid:
536 if pp[0] == revlog.nullid:
529 break
537 break
530 if chlog.rev(pp[0]) < revnum:
538 if chlog.rev(pp[0]) < revnum:
531 break
539 break
532 n = pp[0]
540 n = pp[0]
533 if n == rev:
541 if n == rev:
534 break
542 break
535 r = chlog.reachable(h, rev)
543 r = chlog.reachable(h, rev)
536 if rev not in r:
544 if rev not in r:
537 saveheads.append(h)
545 saveheads.append(h)
538 for x in r:
546 for x in r:
539 if chlog.rev(x) > revnum:
547 if chlog.rev(x) > revnum:
540 savebases[x] = 1
548 savebases[x] = 1
541
549
542 # create a changegroup for all the branches we need to keep
550 # create a changegroup for all the branches we need to keep
543 if backup is "all":
551 if backup is "all":
544 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
552 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
545 bundle(backupch)
553 bundle(backupch)
546 if saveheads:
554 if saveheads:
547 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
555 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
548 chgrpfile = bundle(backupch)
556 chgrpfile = bundle(backupch)
549
557
550 stripall(rev, revnum)
558 stripall(rev, revnum)
551
559
552 change = chlog.read(rev)
560 change = chlog.read(rev)
553 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
561 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
554 chlog.strip(revnum, revnum)
562 chlog.strip(revnum, revnum)
555 if saveheads:
563 if saveheads:
556 self.ui.status("adding branch\n")
564 self.ui.status("adding branch\n")
557 commands.unbundle(self.ui, repo, chgrpfile, update=False)
565 commands.unbundle(self.ui, repo, chgrpfile, update=False)
558 if backup is not "strip":
566 if backup is not "strip":
559 os.unlink(chgrpfile)
567 os.unlink(chgrpfile)
560
568
561 def isapplied(self, patch):
569 def isapplied(self, patch):
562 """returns (index, rev, patch)"""
570 """returns (index, rev, patch)"""
563 for i in xrange(len(self.applied)):
571 for i in xrange(len(self.applied)):
564 p = self.applied[i]
572 p = self.applied[i]
565 a = p.split(':')
573 a = p.split(':')
566 if a[1] == patch:
574 if a[1] == patch:
567 return (i, a[0], a[1])
575 return (i, a[0], a[1])
568 return None
576 return None
569
577
570 def lookup(self, patch):
578 def lookup(self, patch):
571 if patch == None:
579 if patch == None:
572 return None
580 return None
573 if patch in self.series:
581 if patch in self.series:
574 return patch
582 return patch
575 if not os.path.isfile(os.path.join(self.path, patch)):
583 if not os.path.isfile(os.path.join(self.path, patch)):
576 try:
584 try:
577 sno = int(patch)
585 sno = int(patch)
578 except(ValueError, OverflowError):
586 except(ValueError, OverflowError):
579 self.ui.warn("patch %s not in series\n" % patch)
587 self.ui.warn("patch %s not in series\n" % patch)
580 sys.exit(1)
588 sys.exit(1)
581 if sno >= len(self.series):
589 if sno >= len(self.series):
582 self.ui.warn("patch number %d is out of range\n" % sno)
590 self.ui.warn("patch number %d is out of range\n" % sno)
583 sys.exit(1)
591 sys.exit(1)
584 patch = self.series[sno]
592 patch = self.series[sno]
585 else:
593 else:
586 self.ui.warn("patch %s not in series\n" % patch)
594 self.ui.warn("patch %s not in series\n" % patch)
587 sys.exit(1)
595 sys.exit(1)
588 return patch
596 return patch
589
597
590 def push(self, repo, patch=None, force=False, list=False,
598 def push(self, repo, patch=None, force=False, list=False,
591 mergeq=None, wlock=None):
599 mergeq=None, wlock=None):
592 if not wlock:
600 if not wlock:
593 wlock = repo.wlock()
601 wlock = repo.wlock()
594 patch = self.lookup(patch)
602 patch = self.lookup(patch)
595 if patch and self.isapplied(patch):
603 if patch and self.isapplied(patch):
596 self.ui.warn("patch %s is already applied\n" % patch)
604 self.ui.warn("patch %s is already applied\n" % patch)
597 sys.exit(1)
605 sys.exit(1)
598 if self.series_end() == len(self.series):
606 if self.series_end() == len(self.series):
599 self.ui.warn("File series fully applied\n")
607 self.ui.warn("File series fully applied\n")
600 sys.exit(1)
608 sys.exit(1)
601 if not force:
609 if not force:
602 self.check_localchanges(repo)
610 self.check_localchanges(repo)
603
611
604 self.applied_dirty = 1;
612 self.applied_dirty = 1;
605 start = self.series_end()
613 start = self.series_end()
606 if start > 0:
614 if start > 0:
607 self.check_toppatch(repo)
615 self.check_toppatch(repo)
608 if not patch:
616 if not patch:
609 patch = self.series[start]
617 patch = self.series[start]
610 end = start + 1
618 end = start + 1
611 else:
619 else:
612 end = self.series.index(patch, start) + 1
620 end = self.series.index(patch, start) + 1
613 s = self.series[start:end]
621 s = self.series[start:end]
614 if mergeq:
622 if mergeq:
615 ret = self.mergepatch(repo, mergeq, s, wlock)
623 ret = self.mergepatch(repo, mergeq, s, wlock)
616 else:
624 else:
617 ret = self.apply(repo, s, list, wlock=wlock)
625 ret = self.apply(repo, s, list, wlock=wlock)
618 top = self.applied[-1].split(':')[1]
626 top = self.applied[-1].split(':')[1]
619 if ret[0]:
627 if ret[0]:
620 self.ui.write("Errors during apply, please fix and refresh %s\n" %
628 self.ui.write("Errors during apply, please fix and refresh %s\n" %
621 top)
629 top)
622 else:
630 else:
623 self.ui.write("Now at: %s\n" % top)
631 self.ui.write("Now at: %s\n" % top)
624 return ret[0]
632 return ret[0]
625
633
626 def pop(self, repo, patch=None, force=False, update=True, wlock=None):
634 def pop(self, repo, patch=None, force=False, update=True, wlock=None):
627 def getfile(f, rev):
635 def getfile(f, rev):
628 t = repo.file(f).read(rev)
636 t = repo.file(f).read(rev)
629 try:
637 try:
630 repo.wfile(f, "w").write(t)
638 repo.wfile(f, "w").write(t)
631 except IOError:
639 except IOError:
632 try:
640 try:
633 os.makedirs(os.path.dirname(repo.wjoin(f)))
641 os.makedirs(os.path.dirname(repo.wjoin(f)))
634 except OSError, err:
642 except OSError, err:
635 if err.errno != errno.EEXIST: raise
643 if err.errno != errno.EEXIST: raise
636 repo.wfile(f, "w").write(t)
644 repo.wfile(f, "w").write(t)
637
645
638 if not wlock:
646 if not wlock:
639 wlock = repo.wlock()
647 wlock = repo.wlock()
640 if patch:
648 if patch:
641 # index, rev, patch
649 # index, rev, patch
642 info = self.isapplied(patch)
650 info = self.isapplied(patch)
643 if not info:
651 if not info:
644 patch = self.lookup(patch)
652 patch = self.lookup(patch)
645 info = self.isapplied(patch)
653 info = self.isapplied(patch)
646 if not info:
654 if not info:
647 self.ui.warn("patch %s is not applied\n" % patch)
655 self.ui.warn("patch %s is not applied\n" % patch)
648 sys.exit(1)
656 sys.exit(1)
649 if len(self.applied) == 0:
657 if len(self.applied) == 0:
650 self.ui.warn("No patches applied\n")
658 self.ui.warn("No patches applied\n")
651 sys.exit(1)
659 sys.exit(1)
652
660
653 if not update:
661 if not update:
654 parents = repo.dirstate.parents()
662 parents = repo.dirstate.parents()
655 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
663 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
656 for p in parents:
664 for p in parents:
657 if p in rr:
665 if p in rr:
658 self.ui.warn("qpop: forcing dirstate update\n")
666 self.ui.warn("qpop: forcing dirstate update\n")
659 update = True
667 update = True
660
668
661 if not force and update:
669 if not force and update:
662 self.check_localchanges(repo)
670 self.check_localchanges(repo)
663
671
664 self.applied_dirty = 1;
672 self.applied_dirty = 1;
665 end = len(self.applied)
673 end = len(self.applied)
666 if not patch:
674 if not patch:
667 info = [len(self.applied) - 1] + self.applied[-1].split(':')
675 info = [len(self.applied) - 1] + self.applied[-1].split(':')
668 start = info[0]
676 start = info[0]
669 rev = revlog.bin(info[1])
677 rev = revlog.bin(info[1])
670
678
671 # we know there are no local changes, so we can make a simplified
679 # we know there are no local changes, so we can make a simplified
672 # form of hg.update.
680 # form of hg.update.
673 if update:
681 if update:
674 top = self.check_toppatch(repo)
682 top = self.check_toppatch(repo)
675 qp = self.qparents(repo, rev)
683 qp = self.qparents(repo, rev)
676 changes = repo.changelog.read(qp)
684 changes = repo.changelog.read(qp)
677 mf1 = repo.manifest.readflags(changes[0])
685 mf1 = repo.manifest.readflags(changes[0])
678 mmap = repo.manifest.read(changes[0])
686 mmap = repo.manifest.read(changes[0])
679 (c, a, r, d, u) = repo.changes(qp, top)
687 (c, a, r, d, u) = repo.changes(qp, top)
680 if d:
688 if d:
681 raise util.Abort("deletions found between repo revs")
689 raise util.Abort("deletions found between repo revs")
682 for f in c:
690 for f in c:
683 getfile(f, mmap[f])
691 getfile(f, mmap[f])
684 for f in r:
692 for f in r:
685 getfile(f, mmap[f])
693 getfile(f, mmap[f])
686 util.set_exec(repo.wjoin(f), mf1[f])
694 util.set_exec(repo.wjoin(f), mf1[f])
687 repo.dirstate.update(c + r, 'n')
695 repo.dirstate.update(c + r, 'n')
688 for f in a:
696 for f in a:
689 try: os.unlink(repo.wjoin(f))
697 try: os.unlink(repo.wjoin(f))
690 except: raise
698 except: raise
691 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
699 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
692 except: pass
700 except: pass
693 if a:
701 if a:
694 repo.dirstate.forget(a)
702 repo.dirstate.forget(a)
695 repo.dirstate.setparents(qp, revlog.nullid)
703 repo.dirstate.setparents(qp, revlog.nullid)
696 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
704 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
697 del self.applied[start:end]
705 del self.applied[start:end]
698 if len(self.applied):
706 if len(self.applied):
699 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
707 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
700 else:
708 else:
701 self.ui.write("Patch queue now empty\n")
709 self.ui.write("Patch queue now empty\n")
702
710
703 def diff(self, repo, files):
711 def diff(self, repo, files):
704 top = self.check_toppatch(repo)
712 top = self.check_toppatch(repo)
705 if not top:
713 if not top:
706 self.ui.write("No patches applied\n")
714 self.ui.write("No patches applied\n")
707 return
715 return
708 qp = self.qparents(repo, top)
716 qp = self.qparents(repo, top)
709 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
717 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
710
718
711 def refresh(self, repo, short=False):
719 def refresh(self, repo, short=False):
712 if len(self.applied) == 0:
720 if len(self.applied) == 0:
713 self.ui.write("No patches applied\n")
721 self.ui.write("No patches applied\n")
714 return
722 return
715 wlock = repo.wlock()
723 wlock = repo.wlock()
716 self.check_toppatch(repo)
724 self.check_toppatch(repo)
717 qp = self.qparents(repo)
725 qp = self.qparents(repo)
718 (top, patch) = self.applied[-1].split(':')
726 (top, patch) = self.applied[-1].split(':')
719 top = revlog.bin(top)
727 top = revlog.bin(top)
720 cparents = repo.changelog.parents(top)
728 cparents = repo.changelog.parents(top)
721 patchparent = self.qparents(repo, top)
729 patchparent = self.qparents(repo, top)
722 message, comments, user, date, patchfound = self.readheaders(patch)
730 message, comments, user, date, patchfound = self.readheaders(patch)
723
731
724 patchf = self.opener(patch, "w")
732 patchf = self.opener(patch, "w")
725 if comments:
733 if comments:
726 comments = "\n".join(comments) + '\n\n'
734 comments = "\n".join(comments) + '\n\n'
727 patchf.write(comments)
735 patchf.write(comments)
728
736
729 tip = repo.changelog.tip()
737 tip = repo.changelog.tip()
730 if top == tip:
738 if top == tip:
731 # if the top of our patch queue is also the tip, there is an
739 # 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
740 # optimization here. We update the dirstate in place and strip
733 # off the tip commit. Then just commit the current directory
741 # off the tip commit. Then just commit the current directory
734 # tree. We can also send repo.commit the list of files
742 # tree. We can also send repo.commit the list of files
735 # changed to speed up the diff
743 # changed to speed up the diff
736 #
744 #
737 # in short mode, we only diff the files included in the
745 # in short mode, we only diff the files included in the
738 # patch already
746 # patch already
739 #
747 #
740 # this should really read:
748 # this should really read:
741 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
749 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
742 # but we do it backwards to take advantage of manifest/chlog
750 # but we do it backwards to take advantage of manifest/chlog
743 # caching against the next repo.changes call
751 # caching against the next repo.changes call
744 #
752 #
745 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
753 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
746 if short:
754 if short:
747 filelist = cc + aa + dd
755 filelist = cc + aa + dd
748 else:
756 else:
749 filelist = None
757 filelist = None
750 (c, a, r, d, u) = repo.changes(None, None, filelist)
758 (c, a, r, d, u) = repo.changes(None, None, filelist)
751
759
752 # we might end up with files that were added between tip and
760 # we might end up with files that were added between tip and
753 # the dirstate parent, but then changed in the local dirstate.
761 # 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
762 # in this case, we want them to only show up in the added section
755 for x in c:
763 for x in c:
756 if x not in aa:
764 if x not in aa:
757 cc.append(x)
765 cc.append(x)
758 # we might end up with files added by the local dirstate that
766 # we might end up with files added by the local dirstate that
759 # were deleted by the patch. In this case, they should only
767 # were deleted by the patch. In this case, they should only
760 # show up in the changed section.
768 # show up in the changed section.
761 for x in a:
769 for x in a:
762 if x in dd:
770 if x in dd:
763 del dd[dd.index(x)]
771 del dd[dd.index(x)]
764 cc.append(x)
772 cc.append(x)
765 else:
773 else:
766 aa.append(x)
774 aa.append(x)
767 # make sure any files deleted in the local dirstate
775 # make sure any files deleted in the local dirstate
768 # are not in the add or change column of the patch
776 # are not in the add or change column of the patch
769 forget = []
777 forget = []
770 for x in d + r:
778 for x in d + r:
771 if x in aa:
779 if x in aa:
772 del aa[aa.index(x)]
780 del aa[aa.index(x)]
773 forget.append(x)
781 forget.append(x)
774 continue
782 continue
775 elif x in cc:
783 elif x in cc:
776 del cc[cc.index(x)]
784 del cc[cc.index(x)]
777 dd.append(x)
785 dd.append(x)
778
786
779 c = list(util.unique(cc))
787 c = list(util.unique(cc))
780 r = list(util.unique(dd))
788 r = list(util.unique(dd))
781 a = list(util.unique(aa))
789 a = list(util.unique(aa))
782 filelist = list(util.unique(c + r + a ))
790 filelist = list(util.unique(c + r + a ))
783 commands.dodiff(patchf, self.ui, repo, patchparent, None,
791 commands.dodiff(patchf, self.ui, repo, patchparent, None,
784 filelist, changes=(c, a, r, [], u))
792 filelist, changes=(c, a, r, [], u))
785 patchf.close()
793 patchf.close()
786
794
787 changes = repo.changelog.read(tip)
795 changes = repo.changelog.read(tip)
788 repo.dirstate.setparents(*cparents)
796 repo.dirstate.setparents(*cparents)
789 repo.dirstate.update(a, 'a')
797 repo.dirstate.update(a, 'a')
790 repo.dirstate.update(r, 'r')
798 repo.dirstate.update(r, 'r')
791 repo.dirstate.update(c, 'n')
799 repo.dirstate.update(c, 'n')
792 repo.dirstate.forget(forget)
800 repo.dirstate.forget(forget)
793
801
794 if not message:
802 if not message:
795 message = "patch queue: %s\n" % patch
803 message = "patch queue: %s\n" % patch
796 else:
804 else:
797 message = "\n".join(message)
805 message = "\n".join(message)
798 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
806 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
799 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
807 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
800 self.applied[-1] = revlog.hex(n) + ':' + patch
808 self.applied[-1] = revlog.hex(n) + ':' + patch
801 self.applied_dirty = 1
809 self.applied_dirty = 1
802 else:
810 else:
803 commands.dodiff(patchf, self.ui, repo, patchparent, None)
811 commands.dodiff(patchf, self.ui, repo, patchparent, None)
804 patchf.close()
812 patchf.close()
805 self.pop(repo, force=True, wlock=wlock)
813 self.pop(repo, force=True, wlock=wlock)
806 self.push(repo, force=True, wlock=wlock)
814 self.push(repo, force=True, wlock=wlock)
807
815
808 def init(self, repo, create=False):
816 def init(self, repo, create=False):
809 if os.path.isdir(self.path):
817 if os.path.isdir(self.path):
810 raise util.Abort("patch queue directory already exists")
818 raise util.Abort("patch queue directory already exists")
811 os.mkdir(self.path)
819 os.mkdir(self.path)
812 if create:
820 if create:
813 return self.qrepo(create=True)
821 return self.qrepo(create=True)
814
822
815 def unapplied(self, repo, patch=None):
823 def unapplied(self, repo, patch=None):
816 if patch and patch not in self.series:
824 if patch and patch not in self.series:
817 self.ui.warn("%s not in the series file\n" % patch)
825 self.ui.warn("%s not in the series file\n" % patch)
818 sys.exit(1)
826 sys.exit(1)
819 if not patch:
827 if not patch:
820 start = self.series_end()
828 start = self.series_end()
821 else:
829 else:
822 start = self.series.index(patch) + 1
830 start = self.series.index(patch) + 1
823 for p in self.series[start:]:
831 for p in self.series[start:]:
824 self.ui.write("%s\n" % p)
832 self.ui.write("%s\n" % p)
825
833
826 def qseries(self, repo, missing=None):
834 def qseries(self, repo, missing=None):
827 start = self.series_end()
835 start = self.series_end()
828 if not missing:
836 if not missing:
829 for p in self.series[:start]:
837 for p in self.series[:start]:
830 if self.ui.verbose:
838 if self.ui.verbose:
831 self.ui.write("%d A " % self.series.index(p))
839 self.ui.write("%d A " % self.series.index(p))
832 self.ui.write("%s\n" % p)
840 self.ui.write("%s\n" % p)
833 for p in self.series[start:]:
841 for p in self.series[start:]:
834 if self.ui.verbose:
842 if self.ui.verbose:
835 self.ui.write("%d U " % self.series.index(p))
843 self.ui.write("%d U " % self.series.index(p))
836 self.ui.write("%s\n" % p)
844 self.ui.write("%s\n" % p)
837 else:
845 else:
838 list = []
846 list = []
839 for root, dirs, files in os.walk(self.path):
847 for root, dirs, files in os.walk(self.path):
840 d = root[len(self.path) + 1:]
848 d = root[len(self.path) + 1:]
841 for f in files:
849 for f in files:
842 fl = os.path.join(d, f)
850 fl = os.path.join(d, f)
843 if (fl not in self.series and
851 if (fl not in self.series and
844 fl not in (self.status_path, self.series_path)
852 fl not in (self.status_path, self.series_path)
845 and not fl.startswith('.')):
853 and not fl.startswith('.')):
846 list.append(fl)
854 list.append(fl)
847 list.sort()
855 list.sort()
848 if list:
856 if list:
849 for x in list:
857 for x in list:
850 if self.ui.verbose:
858 if self.ui.verbose:
851 self.ui.write("D ")
859 self.ui.write("D ")
852 self.ui.write("%s\n" % x)
860 self.ui.write("%s\n" % x)
853
861
854 def issaveline(self, l):
862 def issaveline(self, l):
855 name = l.split(':')[1]
863 name = l.split(':')[1]
856 if name == '.hg.patches.save.line':
864 if name == '.hg.patches.save.line':
857 return True
865 return True
858
866
859 def qrepo(self, create=False):
867 def qrepo(self, create=False):
860 if create or os.path.isdir(os.path.join(self.path, ".hg")):
868 if create or os.path.isdir(os.path.join(self.path, ".hg")):
861 return hg.repository(self.ui, path=self.path, create=create)
869 return hg.repository(self.ui, path=self.path, create=create)
862
870
863 def restore(self, repo, rev, delete=None, qupdate=None):
871 def restore(self, repo, rev, delete=None, qupdate=None):
864 c = repo.changelog.read(rev)
872 c = repo.changelog.read(rev)
865 desc = c[4].strip()
873 desc = c[4].strip()
866 lines = desc.splitlines()
874 lines = desc.splitlines()
867 i = 0
875 i = 0
868 datastart = None
876 datastart = None
869 series = []
877 series = []
870 applied = []
878 applied = []
871 qpp = None
879 qpp = None
872 for i in xrange(0, len(lines)):
880 for i in xrange(0, len(lines)):
873 if lines[i] == 'Patch Data:':
881 if lines[i] == 'Patch Data:':
874 datastart = i + 1
882 datastart = i + 1
875 elif lines[i].startswith('Dirstate:'):
883 elif lines[i].startswith('Dirstate:'):
876 l = lines[i].rstrip()
884 l = lines[i].rstrip()
877 l = l[10:].split(' ')
885 l = l[10:].split(' ')
878 qpp = [ hg.bin(x) for x in l ]
886 qpp = [ hg.bin(x) for x in l ]
879 elif datastart != None:
887 elif datastart != None:
880 l = lines[i].rstrip()
888 l = lines[i].rstrip()
881 index = l.index(':')
889 index = l.index(':')
882 id = l[:index]
890 id = l[:index]
883 file = l[index + 1:]
891 file = l[index + 1:]
884 if id:
892 if id:
885 applied.append(l)
893 applied.append(l)
886 series.append(file)
894 series.append(file)
887 if datastart == None:
895 if datastart == None:
888 self.ui.warn("No saved patch data found\n")
896 self.ui.warn("No saved patch data found\n")
889 return 1
897 return 1
890 self.ui.warn("restoring status: %s\n" % lines[0])
898 self.ui.warn("restoring status: %s\n" % lines[0])
891 self.full_series = series
899 self.full_series = series
892 self.applied = applied
900 self.applied = applied
893 self.read_series(self.full_series)
901 self.read_series(self.full_series)
894 self.series_dirty = 1
902 self.series_dirty = 1
895 self.applied_dirty = 1
903 self.applied_dirty = 1
896 heads = repo.changelog.heads()
904 heads = repo.changelog.heads()
897 if delete:
905 if delete:
898 if rev not in heads:
906 if rev not in heads:
899 self.ui.warn("save entry has children, leaving it alone\n")
907 self.ui.warn("save entry has children, leaving it alone\n")
900 else:
908 else:
901 self.ui.warn("removing save entry %s\n" % hg.short(rev))
909 self.ui.warn("removing save entry %s\n" % hg.short(rev))
902 pp = repo.dirstate.parents()
910 pp = repo.dirstate.parents()
903 if rev in pp:
911 if rev in pp:
904 update = True
912 update = True
905 else:
913 else:
906 update = False
914 update = False
907 self.strip(repo, rev, update=update, backup='strip')
915 self.strip(repo, rev, update=update, backup='strip')
908 if qpp:
916 if qpp:
909 self.ui.warn("saved queue repository parents: %s %s\n" %
917 self.ui.warn("saved queue repository parents: %s %s\n" %
910 (hg.short(qpp[0]), hg.short(qpp[1])))
918 (hg.short(qpp[0]), hg.short(qpp[1])))
911 if qupdate:
919 if qupdate:
912 print "queue directory updating"
920 print "queue directory updating"
913 r = self.qrepo()
921 r = self.qrepo()
914 if not r:
922 if not r:
915 self.ui.warn("Unable to load queue repository\n")
923 self.ui.warn("Unable to load queue repository\n")
916 return 1
924 return 1
917 r.update(qpp[0], allow=False, force=True)
925 r.update(qpp[0], allow=False, force=True)
918
926
919 def save(self, repo, msg=None):
927 def save(self, repo, msg=None):
920 if len(self.applied) == 0:
928 if len(self.applied) == 0:
921 self.ui.warn("save: no patches applied, exiting\n")
929 self.ui.warn("save: no patches applied, exiting\n")
922 return 1
930 return 1
923 if self.issaveline(self.applied[-1]):
931 if self.issaveline(self.applied[-1]):
924 self.ui.warn("status is already saved\n")
932 self.ui.warn("status is already saved\n")
925 return 1
933 return 1
926
934
927 ar = [ ':' + x for x in self.full_series ]
935 ar = [ ':' + x for x in self.full_series ]
928 if not msg:
936 if not msg:
929 msg = "hg patches saved state"
937 msg = "hg patches saved state"
930 else:
938 else:
931 msg = "hg patches: " + msg.rstrip('\r\n')
939 msg = "hg patches: " + msg.rstrip('\r\n')
932 r = self.qrepo()
940 r = self.qrepo()
933 if r:
941 if r:
934 pp = r.dirstate.parents()
942 pp = r.dirstate.parents()
935 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
943 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
936 msg += "\n\nPatch Data:\n"
944 msg += "\n\nPatch Data:\n"
937 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
945 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
938 + '\n' or "")
946 + '\n' or "")
939 n = repo.commit(None, text, user=None, force=1)
947 n = repo.commit(None, text, user=None, force=1)
940 if not n:
948 if not n:
941 self.ui.warn("repo commit failed\n")
949 self.ui.warn("repo commit failed\n")
942 return 1
950 return 1
943 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
951 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
944 self.applied_dirty = 1
952 self.applied_dirty = 1
945
953
946 def series_end(self):
954 def series_end(self):
947 end = 0
955 end = 0
948 if len(self.applied) > 0:
956 if len(self.applied) > 0:
949 (top, p) = self.applied[-1].split(':')
957 (top, p) = self.applied[-1].split(':')
950 try:
958 try:
951 end = self.series.index(p)
959 end = self.series.index(p)
952 except ValueError:
960 except ValueError:
953 return 0
961 return 0
954 return end + 1
962 return end + 1
955 return end
963 return end
956
964
957 def qapplied(self, repo, patch=None):
965 def qapplied(self, repo, patch=None):
958 if patch and patch not in self.series:
966 if patch and patch not in self.series:
959 self.ui.warn("%s not in the series file\n" % patch)
967 self.ui.warn("%s not in the series file\n" % patch)
960 sys.exit(1)
968 sys.exit(1)
961 if not patch:
969 if not patch:
962 end = len(self.applied)
970 end = len(self.applied)
963 else:
971 else:
964 end = self.series.index(patch) + 1
972 end = self.series.index(patch) + 1
965 for x in xrange(end):
973 for x in xrange(end):
966 p = self.appliedname(x)
974 p = self.appliedname(x)
967 self.ui.write("%s\n" % p)
975 self.ui.write("%s\n" % p)
968
976
969 def appliedname(self, index):
977 def appliedname(self, index):
970 p = self.applied[index]
978 p = self.applied[index]
971 if not self.ui.verbose:
979 if not self.ui.verbose:
972 p = p.split(':')[1]
980 p = p.split(':')[1]
973 return p
981 return p
974
982
975 def top(self, repo):
983 def top(self, repo):
976 if len(self.applied):
984 if len(self.applied):
977 p = self.appliedname(-1)
985 p = self.appliedname(-1)
978 self.ui.write(p + '\n')
986 self.ui.write(p + '\n')
979 else:
987 else:
980 self.ui.write("No patches applied\n")
988 self.ui.write("No patches applied\n")
981
989
982 def next(self, repo):
990 def next(self, repo):
983 end = self.series_end()
991 end = self.series_end()
984 if end == len(self.series):
992 if end == len(self.series):
985 self.ui.write("All patches applied\n")
993 self.ui.write("All patches applied\n")
986 else:
994 else:
987 self.ui.write(self.series[end] + '\n')
995 self.ui.write(self.series[end] + '\n')
988
996
989 def prev(self, repo):
997 def prev(self, repo):
990 if len(self.applied) > 1:
998 if len(self.applied) > 1:
991 p = self.appliedname(-2)
999 p = self.appliedname(-2)
992 self.ui.write(p + '\n')
1000 self.ui.write(p + '\n')
993 elif len(self.applied) == 1:
1001 elif len(self.applied) == 1:
994 self.ui.write("Only one patch applied\n")
1002 self.ui.write("Only one patch applied\n")
995 else:
1003 else:
996 self.ui.write("No patches applied\n")
1004 self.ui.write("No patches applied\n")
997
1005
998 def qimport(self, repo, files, patch=None, existing=None, force=None):
1006 def qimport(self, repo, files, patch=None, existing=None, force=None):
999 if len(files) > 1 and patch:
1007 if len(files) > 1 and patch:
1000 self.ui.warn("-n option not valid when importing multiple files\n")
1008 self.ui.warn("-n option not valid when importing multiple files\n")
1001 sys.exit(1)
1009 sys.exit(1)
1002 i = 0
1010 i = 0
1003 added = []
1011 added = []
1004 for filename in files:
1012 for filename in files:
1005 if existing:
1013 if existing:
1006 if not patch:
1014 if not patch:
1007 patch = filename
1015 patch = filename
1008 if not os.path.isfile(os.path.join(self.path, patch)):
1016 if not os.path.isfile(os.path.join(self.path, patch)):
1009 self.ui.warn("patch %s does not exist\n" % patch)
1017 self.ui.warn("patch %s does not exist\n" % patch)
1010 sys.exit(1)
1018 sys.exit(1)
1011 else:
1019 else:
1012 try:
1020 try:
1013 text = file(filename).read()
1021 text = file(filename).read()
1014 except IOError:
1022 except IOError:
1015 self.ui.warn("Unable to read %s\n" % patch)
1023 self.ui.warn("Unable to read %s\n" % patch)
1016 sys.exit(1)
1024 sys.exit(1)
1017 if not patch:
1025 if not patch:
1018 patch = os.path.split(filename)[1]
1026 patch = os.path.split(filename)[1]
1019 if not force and os.path.isfile(os.path.join(self.path, patch)):
1027 if not force and os.path.isfile(os.path.join(self.path, patch)):
1020 self.ui.warn("patch %s already exists\n" % patch)
1028 self.ui.warn("patch %s already exists\n" % patch)
1021 sys.exit(1)
1029 sys.exit(1)
1022 patchf = self.opener(patch, "w")
1030 patchf = self.opener(patch, "w")
1023 patchf.write(text)
1031 patchf.write(text)
1024 if patch in self.series:
1032 if patch in self.series:
1025 self.ui.warn("patch %s is already in the series file\n" % patch)
1033 self.ui.warn("patch %s is already in the series file\n" % patch)
1026 sys.exit(1)
1034 sys.exit(1)
1027 index = self.series_end() + i
1035 index = self.series_end() + i
1028 self.full_series[index:index] = [patch]
1036 self.full_series[index:index] = [patch]
1029 self.read_series(self.full_series)
1037 self.read_series(self.full_series)
1030 self.ui.warn("adding %s to series file\n" % patch)
1038 self.ui.warn("adding %s to series file\n" % patch)
1031 i += 1
1039 i += 1
1032 added.append(patch)
1040 added.append(patch)
1033 patch = None
1041 patch = None
1034 self.series_dirty = 1
1042 self.series_dirty = 1
1035 qrepo = self.qrepo()
1043 qrepo = self.qrepo()
1036 if qrepo:
1044 if qrepo:
1037 qrepo.add(added)
1045 qrepo.add(added)
1038
1046
1039 def delete(ui, repo, patch, **opts):
1047 def delete(ui, repo, patch, **opts):
1040 """remove a patch from the series file"""
1048 """remove a patch from the series file"""
1041 q = repomap[repo]
1049 q = repomap[repo]
1042 q.delete(repo, patch)
1050 q.delete(repo, patch)
1043 q.save_dirty()
1051 q.save_dirty()
1044 return 0
1052 return 0
1045
1053
1046 def applied(ui, repo, patch=None, **opts):
1054 def applied(ui, repo, patch=None, **opts):
1047 """print the patches already applied"""
1055 """print the patches already applied"""
1048 repomap[repo].qapplied(repo, patch)
1056 repomap[repo].qapplied(repo, patch)
1049 return 0
1057 return 0
1050
1058
1051 def unapplied(ui, repo, patch=None, **opts):
1059 def unapplied(ui, repo, patch=None, **opts):
1052 """print the patches not yet applied"""
1060 """print the patches not yet applied"""
1053 repomap[repo].unapplied(repo, patch)
1061 repomap[repo].unapplied(repo, patch)
1054 return 0
1062 return 0
1055
1063
1056 def qimport(ui, repo, *filename, **opts):
1064 def qimport(ui, repo, *filename, **opts):
1057 """import a patch"""
1065 """import a patch"""
1058 q = repomap[repo]
1066 q = repomap[repo]
1059 q.qimport(repo, filename, patch=opts['name'],
1067 q.qimport(repo, filename, patch=opts['name'],
1060 existing=opts['existing'], force=opts['force'])
1068 existing=opts['existing'], force=opts['force'])
1061 q.save_dirty()
1069 q.save_dirty()
1062 return 0
1070 return 0
1063
1071
1064 def init(ui, repo, **opts):
1072 def init(ui, repo, **opts):
1065 """init a new queue repository"""
1073 """init a new queue repository"""
1066 q = repomap[repo]
1074 q = repomap[repo]
1067 r = q.init(repo, create=opts['create_repo'])
1075 r = q.init(repo, create=opts['create_repo'])
1068 q.save_dirty()
1076 q.save_dirty()
1069 if r:
1077 if r:
1070 fp = r.wopener('.hgignore', 'w')
1078 fp = r.wopener('.hgignore', 'w')
1071 print >> fp, 'syntax: glob'
1079 print >> fp, 'syntax: glob'
1072 print >> fp, 'status'
1080 print >> fp, 'status'
1073 fp.close()
1081 fp.close()
1074 r.wopener('series', 'w').close()
1082 r.wopener('series', 'w').close()
1075 r.add(['.hgignore', 'series'])
1083 r.add(['.hgignore', 'series'])
1076 return 0
1084 return 0
1077
1085
1078 def commit(ui, repo, *pats, **opts):
1086 def commit(ui, repo, *pats, **opts):
1079 q = repomap[repo]
1087 q = repomap[repo]
1080 r = q.qrepo()
1088 r = q.qrepo()
1081 if not r: raise util.Abort('no queue repository')
1089 if not r: raise util.Abort('no queue repository')
1082 commands.commit(r.ui, r, *pats, **opts)
1090 commands.commit(r.ui, r, *pats, **opts)
1083
1091
1084 def series(ui, repo, **opts):
1092 def series(ui, repo, **opts):
1085 """print the entire series file"""
1093 """print the entire series file"""
1086 repomap[repo].qseries(repo, missing=opts['missing'])
1094 repomap[repo].qseries(repo, missing=opts['missing'])
1087 return 0
1095 return 0
1088
1096
1089 def top(ui, repo, **opts):
1097 def top(ui, repo, **opts):
1090 """print the name of the current patch"""
1098 """print the name of the current patch"""
1091 repomap[repo].top(repo)
1099 repomap[repo].top(repo)
1092 return 0
1100 return 0
1093
1101
1094 def next(ui, repo, **opts):
1102 def next(ui, repo, **opts):
1095 """print the name of the next patch"""
1103 """print the name of the next patch"""
1096 repomap[repo].next(repo)
1104 repomap[repo].next(repo)
1097 return 0
1105 return 0
1098
1106
1099 def prev(ui, repo, **opts):
1107 def prev(ui, repo, **opts):
1100 """print the name of the previous patch"""
1108 """print the name of the previous patch"""
1101 repomap[repo].prev(repo)
1109 repomap[repo].prev(repo)
1102 return 0
1110 return 0
1103
1111
1104 def new(ui, repo, patch, **opts):
1112 def new(ui, repo, patch, **opts):
1105 """create a new patch"""
1113 """create a new patch"""
1106 q = repomap[repo]
1114 q = repomap[repo]
1107 q.new(repo, patch, msg=opts['message'], force=opts['force'])
1115 q.new(repo, patch, msg=opts['message'], force=opts['force'])
1108 q.save_dirty()
1116 q.save_dirty()
1109 return 0
1117 return 0
1110
1118
1111 def refresh(ui, repo, **opts):
1119 def refresh(ui, repo, **opts):
1112 """update the current patch"""
1120 """update the current patch"""
1113 q = repomap[repo]
1121 q = repomap[repo]
1114 q.refresh(repo, short=opts['short'])
1122 q.refresh(repo, short=opts['short'])
1115 q.save_dirty()
1123 q.save_dirty()
1116 return 0
1124 return 0
1117
1125
1118 def diff(ui, repo, *files, **opts):
1126 def diff(ui, repo, *files, **opts):
1119 """diff of the current patch"""
1127 """diff of the current patch"""
1120 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1128 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1121 repomap[repo].diff(repo, list(files))
1129 repomap[repo].diff(repo, list(files))
1122 return 0
1130 return 0
1123
1131
1124 def lastsavename(path):
1132 def lastsavename(path):
1125 (dir, base) = os.path.split(path)
1133 (dir, base) = os.path.split(path)
1126 names = os.listdir(dir)
1134 names = os.listdir(dir)
1127 namere = re.compile("%s.([0-9]+)" % base)
1135 namere = re.compile("%s.([0-9]+)" % base)
1128 max = None
1136 max = None
1129 maxname = None
1137 maxname = None
1130 for f in names:
1138 for f in names:
1131 m = namere.match(f)
1139 m = namere.match(f)
1132 if m:
1140 if m:
1133 index = int(m.group(1))
1141 index = int(m.group(1))
1134 if max == None or index > max:
1142 if max == None or index > max:
1135 max = index
1143 max = index
1136 maxname = f
1144 maxname = f
1137 if maxname:
1145 if maxname:
1138 return (os.path.join(dir, maxname), max)
1146 return (os.path.join(dir, maxname), max)
1139 return (None, None)
1147 return (None, None)
1140
1148
1141 def savename(path):
1149 def savename(path):
1142 (last, index) = lastsavename(path)
1150 (last, index) = lastsavename(path)
1143 if last is None:
1151 if last is None:
1144 index = 0
1152 index = 0
1145 newpath = path + ".%d" % (index + 1)
1153 newpath = path + ".%d" % (index + 1)
1146 return newpath
1154 return newpath
1147
1155
1148 def push(ui, repo, patch=None, **opts):
1156 def push(ui, repo, patch=None, **opts):
1149 """push the next patch onto the stack"""
1157 """push the next patch onto the stack"""
1150 q = repomap[repo]
1158 q = repomap[repo]
1151 mergeq = None
1159 mergeq = None
1152
1160
1153 if opts['all']:
1161 if opts['all']:
1154 patch = q.series[-1]
1162 patch = q.series[-1]
1155 if opts['merge']:
1163 if opts['merge']:
1156 if opts['name']:
1164 if opts['name']:
1157 newpath = opts['name']
1165 newpath = opts['name']
1158 else:
1166 else:
1159 newpath, i = lastsavename(q.path)
1167 newpath, i = lastsavename(q.path)
1160 if not newpath:
1168 if not newpath:
1161 ui.warn("no saved queues found, please use -n\n")
1169 ui.warn("no saved queues found, please use -n\n")
1162 return 1
1170 return 1
1163 mergeq = queue(ui, repo.join(""), newpath)
1171 mergeq = queue(ui, repo.join(""), newpath)
1164 ui.warn("merging with queue at: %s\n" % mergeq.path)
1172 ui.warn("merging with queue at: %s\n" % mergeq.path)
1165 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1173 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1166 mergeq=mergeq)
1174 mergeq=mergeq)
1167 q.save_dirty()
1175 q.save_dirty()
1168 return ret
1176 return ret
1169
1177
1170 def pop(ui, repo, patch=None, **opts):
1178 def pop(ui, repo, patch=None, **opts):
1171 """pop the current patch off the stack"""
1179 """pop the current patch off the stack"""
1172 localupdate = True
1180 localupdate = True
1173 if opts['name']:
1181 if opts['name']:
1174 q = queue(ui, repo.join(""), repo.join(opts['name']))
1182 q = queue(ui, repo.join(""), repo.join(opts['name']))
1175 ui.warn('using patch queue: %s\n' % q.path)
1183 ui.warn('using patch queue: %s\n' % q.path)
1176 localupdate = False
1184 localupdate = False
1177 else:
1185 else:
1178 q = repomap[repo]
1186 q = repomap[repo]
1179 if opts['all'] and len(q.applied) > 0:
1187 if opts['all'] and len(q.applied) > 0:
1180 patch = q.applied[0].split(':')[1]
1188 patch = q.applied[0].split(':')[1]
1181 q.pop(repo, patch, force=opts['force'], update=localupdate)
1189 q.pop(repo, patch, force=opts['force'], update=localupdate)
1182 q.save_dirty()
1190 q.save_dirty()
1183 return 0
1191 return 0
1184
1192
1185 def restore(ui, repo, rev, **opts):
1193 def restore(ui, repo, rev, **opts):
1186 """restore the queue state saved by a rev"""
1194 """restore the queue state saved by a rev"""
1187 rev = repo.lookup(rev)
1195 rev = repo.lookup(rev)
1188 q = repomap[repo]
1196 q = repomap[repo]
1189 q.restore(repo, rev, delete=opts['delete'],
1197 q.restore(repo, rev, delete=opts['delete'],
1190 qupdate=opts['update'])
1198 qupdate=opts['update'])
1191 q.save_dirty()
1199 q.save_dirty()
1192 return 0
1200 return 0
1193
1201
1194 def save(ui, repo, **opts):
1202 def save(ui, repo, **opts):
1195 """save current queue state"""
1203 """save current queue state"""
1196 q = repomap[repo]
1204 q = repomap[repo]
1197 ret = q.save(repo, msg=opts['message'])
1205 ret = q.save(repo, msg=opts['message'])
1198 if ret:
1206 if ret:
1199 return ret
1207 return ret
1200 q.save_dirty()
1208 q.save_dirty()
1201 if opts['copy']:
1209 if opts['copy']:
1202 path = q.path
1210 path = q.path
1203 if opts['name']:
1211 if opts['name']:
1204 newpath = os.path.join(q.basepath, opts['name'])
1212 newpath = os.path.join(q.basepath, opts['name'])
1205 if os.path.exists(newpath):
1213 if os.path.exists(newpath):
1206 if not os.path.isdir(newpath):
1214 if not os.path.isdir(newpath):
1207 ui.warn("destination %s exists and is not a directory\n" %
1215 ui.warn("destination %s exists and is not a directory\n" %
1208 newpath)
1216 newpath)
1209 sys.exit(1)
1217 sys.exit(1)
1210 if not opts['force']:
1218 if not opts['force']:
1211 ui.warn("destination %s exists, use -f to force\n" %
1219 ui.warn("destination %s exists, use -f to force\n" %
1212 newpath)
1220 newpath)
1213 sys.exit(1)
1221 sys.exit(1)
1214 else:
1222 else:
1215 newpath = savename(path)
1223 newpath = savename(path)
1216 ui.warn("copy %s to %s\n" % (path, newpath))
1224 ui.warn("copy %s to %s\n" % (path, newpath))
1217 util.copyfiles(path, newpath)
1225 util.copyfiles(path, newpath)
1218 if opts['empty']:
1226 if opts['empty']:
1219 try:
1227 try:
1220 os.unlink(os.path.join(q.path, q.status_path))
1228 os.unlink(os.path.join(q.path, q.status_path))
1221 except:
1229 except:
1222 pass
1230 pass
1223 return 0
1231 return 0
1224
1232
1225 def strip(ui, repo, rev, **opts):
1233 def strip(ui, repo, rev, **opts):
1226 """strip a revision and all later revs on the same branch"""
1234 """strip a revision and all later revs on the same branch"""
1227 rev = repo.lookup(rev)
1235 rev = repo.lookup(rev)
1228 backup = 'all'
1236 backup = 'all'
1229 if opts['backup']:
1237 if opts['backup']:
1230 backup = 'strip'
1238 backup = 'strip'
1231 elif opts['nobackup']:
1239 elif opts['nobackup']:
1232 backup = 'none'
1240 backup = 'none'
1233 repomap[repo].strip(repo, rev, backup=backup)
1241 repomap[repo].strip(repo, rev, backup=backup)
1234 return 0
1242 return 0
1235
1243
1236 def version(ui, q=None):
1244 def version(ui, q=None):
1237 """print the version number"""
1245 """print the version number"""
1238 ui.write("mq version %s\n" % versionstr)
1246 ui.write("mq version %s\n" % versionstr)
1239 return 0
1247 return 0
1240
1248
1241 def reposetup(ui, repo):
1249 def reposetup(ui, repo):
1242 repomap[repo] = queue(ui, repo.join(""))
1250 repomap[repo] = queue(ui, repo.join(""))
1243
1251
1244 cmdtable = {
1252 cmdtable = {
1245 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1253 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1246 "qcommit|qci":
1254 "qcommit|qci":
1247 (commit,
1255 (commit,
1248 commands.table["^commit|ci"][1],
1256 commands.table["^commit|ci"][1],
1249 'hg qcommit [OPTION]... [FILE]...'),
1257 'hg qcommit [OPTION]... [FILE]...'),
1250 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1258 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1251 "qdelete": (delete, [], 'hg qdelete PATCH'),
1259 "qdelete": (delete, [], 'hg qdelete PATCH'),
1252 "^qimport":
1260 "^qimport":
1253 (qimport,
1261 (qimport,
1254 [('e', 'existing', None, 'import file in patch dir'),
1262 [('e', 'existing', None, 'import file in patch dir'),
1255 ('n', 'name', '', 'patch file name'),
1263 ('n', 'name', '', 'patch file name'),
1256 ('f', 'force', None, 'overwrite existing files')],
1264 ('f', 'force', None, 'overwrite existing files')],
1257 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1265 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1258 "^qinit":
1266 "^qinit":
1259 (init,
1267 (init,
1260 [('c', 'create-repo', None, 'create patch repository')],
1268 [('c', 'create-repo', None, 'create patch repository')],
1261 'hg qinit [-c]'),
1269 'hg qinit [-c]'),
1262 "qnew":
1270 "qnew":
1263 (new,
1271 (new,
1264 [('m', 'message', '', 'commit message'),
1272 [('m', 'message', '', 'commit message'),
1265 ('f', 'force', None, 'force')],
1273 ('f', 'force', None, 'force')],
1266 'hg qnew [-m TEXT] [-f] PATCH'),
1274 'hg qnew [-m TEXT] [-f] PATCH'),
1267 "qnext": (next, [], 'hg qnext'),
1275 "qnext": (next, [], 'hg qnext'),
1268 "qprev": (prev, [], 'hg qprev'),
1276 "qprev": (prev, [], 'hg qprev'),
1269 "^qpop":
1277 "^qpop":
1270 (pop,
1278 (pop,
1271 [('a', 'all', None, 'pop all patches'),
1279 [('a', 'all', None, 'pop all patches'),
1272 ('n', 'name', '', 'queue name to pop'),
1280 ('n', 'name', '', 'queue name to pop'),
1273 ('f', 'force', None, 'forget any local changes')],
1281 ('f', 'force', None, 'forget any local changes')],
1274 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1282 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1275 "^qpush":
1283 "^qpush":
1276 (push,
1284 (push,
1277 [('f', 'force', None, 'apply if the patch has rejects'),
1285 [('f', 'force', None, 'apply if the patch has rejects'),
1278 ('l', 'list', None, 'list patch name in commit text'),
1286 ('l', 'list', None, 'list patch name in commit text'),
1279 ('a', 'all', None, 'apply all patches'),
1287 ('a', 'all', None, 'apply all patches'),
1280 ('m', 'merge', None, 'merge from another queue'),
1288 ('m', 'merge', None, 'merge from another queue'),
1281 ('n', 'name', '', 'merge queue name')],
1289 ('n', 'name', '', 'merge queue name')],
1282 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1290 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1283 "^qrefresh":
1291 "^qrefresh":
1284 (refresh,
1292 (refresh,
1285 [('s', 'short', None, 'short refresh')],
1293 [('s', 'short', None, 'short refresh')],
1286 'hg qrefresh [-s]'),
1294 'hg qrefresh [-s]'),
1287 "qrestore":
1295 "qrestore":
1288 (restore,
1296 (restore,
1289 [('d', 'delete', None, 'delete save entry'),
1297 [('d', 'delete', None, 'delete save entry'),
1290 ('u', 'update', None, 'update queue working dir')],
1298 ('u', 'update', None, 'update queue working dir')],
1291 'hg qrestore [-d] [-u] REV'),
1299 'hg qrestore [-d] [-u] REV'),
1292 "qsave":
1300 "qsave":
1293 (save,
1301 (save,
1294 [('m', 'message', '', 'commit message'),
1302 [('m', 'message', '', 'commit message'),
1295 ('c', 'copy', None, 'copy patch directory'),
1303 ('c', 'copy', None, 'copy patch directory'),
1296 ('n', 'name', '', 'copy directory name'),
1304 ('n', 'name', '', 'copy directory name'),
1297 ('e', 'empty', None, 'clear queue status file'),
1305 ('e', 'empty', None, 'clear queue status file'),
1298 ('f', 'force', None, 'force copy')],
1306 ('f', 'force', None, 'force copy')],
1299 'hg qsave [-m TEXT] [-c] [-n NAME] [-e] [-f]'),
1307 'hg qsave [-m TEXT] [-c] [-n NAME] [-e] [-f]'),
1300 "qseries":
1308 "qseries":
1301 (series,
1309 (series,
1302 [('m', 'missing', None, 'print patches not in series')],
1310 [('m', 'missing', None, 'print patches not in series')],
1303 'hg qseries [-m]'),
1311 'hg qseries [-m]'),
1304 "^strip":
1312 "^strip":
1305 (strip,
1313 (strip,
1306 [('f', 'force', None, 'force multi-head removal'),
1314 [('f', 'force', None, 'force multi-head removal'),
1307 ('b', 'backup', None, 'bundle unrelated changesets'),
1315 ('b', 'backup', None, 'bundle unrelated changesets'),
1308 ('n', 'nobackup', None, 'no backups')],
1316 ('n', 'nobackup', None, 'no backups')],
1309 'hg strip [-f] [-b] [-n] REV'),
1317 'hg strip [-f] [-b] [-n] REV'),
1310 "qtop": (top, [], 'hg qtop'),
1318 "qtop": (top, [], 'hg qtop'),
1311 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1319 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1312 "qversion": (version, [], 'hg qversion')
1320 "qversion": (version, [], 'hg qversion')
1313 }
1321 }
1314
1322
General Comments 0
You need to be logged in to leave comments. Login now