comparison hgext/mq.py @ 2830:49988d9f0758

Merge with crew, fix most tests
author Matt Mackall <mpm@selenic.com>
date Wed, 09 Aug 2006 13:55:18 -0500
parents 30f59f4a327e 05316bb57d01
children 582cbc4392cb 046a8b03ea59
comparison
equal deleted inserted replaced
2829:4870f795f681 2830:49988d9f0758
33 from mercurial.demandload import * 33 from mercurial.demandload import *
34 demandload(globals(), "os sys re struct traceback errno bz2") 34 demandload(globals(), "os sys re struct traceback errno bz2")
35 from mercurial.i18n import gettext as _ 35 from mercurial.i18n import gettext as _
36 from mercurial import ui, hg, revlog, commands, util 36 from mercurial import ui, hg, revlog, commands, util
37 37
38 versionstr = "0.45"
39
40 commands.norepo += " qclone qversion" 38 commands.norepo += " qclone qversion"
41 39
42 class StatusEntry: 40 class statusentry:
43 def __init__(self, rev, name=None): 41 def __init__(self, rev, name=None):
44 if not name: 42 if not name:
45 self.rev, self.name = rev.split(':') 43 fields = rev.split(':')
44 if len(fields) == 2:
45 self.rev, self.name = fields
46 else:
47 self.rev, self.name = None, None
46 else: 48 else:
47 self.rev, self.name = rev, name 49 self.rev, self.name = rev, name
48 50
49 def __str__(self): 51 def __str__(self):
50 return self.rev + ':' + self.name 52 return self.rev + ':' + self.name
51 53
52 class queue: 54 class queue:
53 def __init__(self, ui, path, patchdir=None): 55 def __init__(self, ui, path, patchdir=None):
54 self.basepath = path 56 self.basepath = path
55 if patchdir: 57 self.path = patchdir or os.path.join(path, "patches")
56 self.path = patchdir
57 else:
58 self.path = os.path.join(path, "patches")
59 self.opener = util.opener(self.path) 58 self.opener = util.opener(self.path)
60 self.ui = ui 59 self.ui = ui
61 self.applied = [] 60 self.applied = []
62 self.full_series = [] 61 self.full_series = []
63 self.applied_dirty = 0 62 self.applied_dirty = 0
64 self.series_dirty = 0 63 self.series_dirty = 0
65 self.series_path = "series" 64 self.series_path = "series"
66 self.status_path = "status" 65 self.status_path = "status"
67 66 self.guards_path = "guards"
68 if os.path.exists(os.path.join(self.path, self.series_path)): 67 self.active_guards = None
68 self.guards_dirty = False
69
70 if os.path.exists(self.join(self.series_path)):
69 self.full_series = self.opener(self.series_path).read().splitlines() 71 self.full_series = self.opener(self.series_path).read().splitlines()
70 self.parse_series() 72 self.parse_series()
71 73
72 if os.path.exists(os.path.join(self.path, self.status_path)): 74 if os.path.exists(self.join(self.status_path)):
73 self.applied = [StatusEntry(l) 75 lines = self.opener(self.status_path).read().splitlines()
74 for l in self.opener(self.status_path).read().splitlines()] 76 self.applied = [statusentry(l) for l in lines]
77
78 def join(self, *p):
79 return os.path.join(self.path, *p)
75 80
76 def find_series(self, patch): 81 def find_series(self, patch):
77 pre = re.compile("(\s*)([^#]+)") 82 pre = re.compile("(\s*)([^#]+)")
78 index = 0 83 index = 0
79 for l in self.full_series: 84 for l in self.full_series:
84 if s == patch: 89 if s == patch:
85 return index 90 return index
86 index += 1 91 index += 1
87 return None 92 return None
88 93
94 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
95
89 def parse_series(self): 96 def parse_series(self):
90 self.series = [] 97 self.series = []
98 self.series_guards = []
91 for l in self.full_series: 99 for l in self.full_series:
92 s = l.split('#', 1)[0].strip() 100 h = l.find('#')
93 if s: 101 if h == -1:
94 self.series.append(s) 102 patch = l
103 comment = ''
104 elif h == 0:
105 continue
106 else:
107 patch = l[:h]
108 comment = l[h:]
109 patch = patch.strip()
110 if patch:
111 self.series.append(patch)
112 self.series_guards.append(self.guard_re.findall(comment))
113
114 def check_guard(self, guard):
115 bad_chars = '# \t\r\n\f'
116 first = guard[0]
117 for c in '-+':
118 if first == c:
119 return (_('guard %r starts with invalid character: %r') %
120 (guard, c))
121 for c in bad_chars:
122 if c in guard:
123 return _('invalid character in guard %r: %r') % (guard, c)
124
125 def set_active(self, guards):
126 for guard in guards:
127 bad = self.check_guard(guard)
128 if bad:
129 raise util.Abort(bad)
130 guards = dict.fromkeys(guards).keys()
131 guards.sort()
132 self.ui.debug('active guards: %s\n' % ' '.join(guards))
133 self.active_guards = guards
134 self.guards_dirty = True
135
136 def active(self):
137 if self.active_guards is None:
138 self.active_guards = []
139 try:
140 guards = self.opener(self.guards_path).read().split()
141 except IOError, err:
142 if err.errno != errno.ENOENT: raise
143 guards = []
144 for i, guard in enumerate(guards):
145 bad = self.check_guard(guard)
146 if bad:
147 self.ui.warn('%s:%d: %s\n' %
148 (self.join(self.guards_path), i + 1, bad))
149 else:
150 self.active_guards.append(guard)
151 return self.active_guards
152
153 def set_guards(self, idx, guards):
154 for g in guards:
155 if len(g) < 2:
156 raise util.Abort(_('guard %r too short') % g)
157 if g[0] not in '-+':
158 raise util.Abort(_('guard %r starts with invalid char') % g)
159 bad = self.check_guard(g[1:])
160 if bad:
161 raise util.Abort(bad)
162 drop = self.guard_re.sub('', self.full_series[idx])
163 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
164 self.parse_series()
165 self.series_dirty = True
166
167 def pushable(self, idx):
168 if isinstance(idx, str):
169 idx = self.series.index(idx)
170 patchguards = self.series_guards[idx]
171 if not patchguards:
172 return True, None
173 default = False
174 guards = self.active()
175 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
176 if exactneg:
177 return False, exactneg[0]
178 pos = [g for g in patchguards if g[0] == '+']
179 nonpos = [g for g in pos if g[1:] not in guards]
180 if pos:
181 if not nonpos:
182 return True, ''
183 return False, nonpos
184 return True, ''
185
186 def explain_pushable(self, idx, all_patches=False):
187 write = all_patches and self.ui.write or self.ui.warn
188 if all_patches or self.ui.verbose:
189 if isinstance(idx, str):
190 idx = self.series.index(idx)
191 pushable, why = self.pushable(idx)
192 if all_patches and pushable:
193 if why is None:
194 write(_('allowing %s - no guards in effect\n') %
195 self.series[idx])
196 else:
197 if not why:
198 write(_('allowing %s - no matching negative guards\n') %
199 self.series[idx])
200 else:
201 write(_('allowing %s - guarded by %r\n') %
202 (self.series[idx], why))
203 if not pushable:
204 if why:
205 write(_('skipping %s - guarded by %r\n') %
206 (self.series[idx], ' '.join(why)))
207 else:
208 write(_('skipping %s - no matching guards\n') %
209 self.series[idx])
95 210
96 def save_dirty(self): 211 def save_dirty(self):
97 def write_list(items, path): 212 def write_list(items, path):
98 fp = self.opener(path, 'w') 213 fp = self.opener(path, 'w')
99 for i in items: 214 for i in items:
100 print >> fp, i 215 print >> fp, i
101 fp.close() 216 fp.close()
102 if self.applied_dirty: write_list(map(str, self.applied), self.status_path) 217 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
103 if self.series_dirty: write_list(self.full_series, self.series_path) 218 if self.series_dirty: write_list(self.full_series, self.series_path)
219 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
104 220
105 def readheaders(self, patch): 221 def readheaders(self, patch):
106 def eatdiff(lines): 222 def eatdiff(lines):
107 while lines: 223 while lines:
108 l = lines[-1] 224 l = lines[-1]
118 if re.match('\s*$', l): 234 if re.match('\s*$', l):
119 del lines[-1] 235 del lines[-1]
120 else: 236 else:
121 break 237 break
122 238
123 pf = os.path.join(self.path, patch) 239 pf = self.join(patch)
124 message = [] 240 message = []
125 comments = [] 241 comments = []
126 user = None 242 user = None
127 date = None 243 date = None
128 format = None 244 format = None
241 # the first patch in the queue is never a merge patch 357 # the first patch in the queue is never a merge patch
242 # 358 #
243 pname = ".hg.patches.merge.marker" 359 pname = ".hg.patches.merge.marker"
244 n = repo.commit(None, '[mq]: merge marker', user=None, force=1, 360 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
245 wlock=wlock) 361 wlock=wlock)
246 self.applied.append(StatusEntry(revlog.hex(n), pname)) 362 self.applied.append(statusentry(revlog.hex(n), pname))
247 self.applied_dirty = 1 363 self.applied_dirty = 1
248 364
249 head = self.qparents(repo) 365 head = self.qparents(repo)
250 366
251 for patch in series: 367 for patch in series:
252 patch = mergeq.lookup(patch, strict=True) 368 patch = mergeq.lookup(patch, strict=True)
253 if not patch: 369 if not patch:
254 self.ui.warn("patch %s does not exist\n" % patch) 370 self.ui.warn("patch %s does not exist\n" % patch)
255 return (1, None) 371 return (1, None)
256 372 pushable, reason = self.pushable(patch)
373 if not pushable:
374 self.explain_pushable(patch, all_patches=True)
375 continue
257 info = mergeq.isapplied(patch) 376 info = mergeq.isapplied(patch)
258 if not info: 377 if not info:
259 self.ui.warn("patch %s is not applied\n" % patch) 378 self.ui.warn("patch %s is not applied\n" % patch)
260 return (1, None) 379 return (1, None)
261 rev = revlog.bin(info[1]) 380 rev = revlog.bin(info[1])
262 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock) 381 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
263 if head: 382 if head:
264 self.applied.append(StatusEntry(revlog.hex(head), patch)) 383 self.applied.append(statusentry(revlog.hex(head), patch))
265 self.applied_dirty = 1 384 self.applied_dirty = 1
266 if err: 385 if err:
267 return (err, head) 386 return (err, head)
268 return (0, head) 387 return (0, head)
269 388
315 wlock = repo.wlock() 434 wlock = repo.wlock()
316 lock = repo.lock() 435 lock = repo.lock()
317 tr = repo.transaction() 436 tr = repo.transaction()
318 n = None 437 n = None
319 for patch in series: 438 for patch in series:
439 pushable, reason = self.pushable(patch)
440 if not pushable:
441 self.explain_pushable(patch, all_patches=True)
442 continue
320 self.ui.warn("applying %s\n" % patch) 443 self.ui.warn("applying %s\n" % patch)
321 pf = os.path.join(patchdir, patch) 444 pf = os.path.join(patchdir, patch)
322 445
323 try: 446 try:
324 message, comments, user, date, patchfound = self.readheaders(patch) 447 message, comments, user, date, patchfound = self.readheaders(patch)
354 477
355 if n == None: 478 if n == None:
356 raise util.Abort(_("repo commit failed")) 479 raise util.Abort(_("repo commit failed"))
357 480
358 if update_status: 481 if update_status:
359 self.applied.append(StatusEntry(revlog.hex(n), patch)) 482 self.applied.append(statusentry(revlog.hex(n), patch))
360 483
361 if patcherr: 484 if patcherr:
362 if not patchfound: 485 if not patchfound:
363 self.ui.warn("patch %s is empty\n" % patch) 486 self.ui.warn("patch %s is empty\n" % patch)
364 err = 0 487 err = 0
384 if force: 507 if force:
385 r = self.qrepo() 508 r = self.qrepo()
386 if r: 509 if r:
387 r.remove([patch], True) 510 r.remove([patch], True)
388 else: 511 else:
389 os.unlink(os.path.join(self.path, patch)) 512 os.unlink(self.join(patch))
390 i = self.find_series(patch) 513 i = self.find_series(patch)
391 del self.full_series[i] 514 del self.full_series[i]
392 self.parse_series() 515 self.parse_series()
393 self.series_dirty = 1 516 self.series_dirty = 1
394 517
403 def check_localchanges(self, repo): 526 def check_localchanges(self, repo):
404 (c, a, r, d, u) = repo.changes(None, None) 527 (c, a, r, d, u) = repo.changes(None, None)
405 if c or a or d or r: 528 if c or a or d or r:
406 raise util.Abort(_("local changes found, refresh first")) 529 raise util.Abort(_("local changes found, refresh first"))
407 def new(self, repo, patch, msg=None, force=None): 530 def new(self, repo, patch, msg=None, force=None):
408 if os.path.exists(os.path.join(self.path, patch)): 531 if os.path.exists(self.join(patch)):
409 raise util.Abort(_('patch "%s" already exists') % patch) 532 raise util.Abort(_('patch "%s" already exists') % patch)
410 commitfiles = [] 533 commitfiles = []
411 (c, a, r, d, u) = repo.changes(None, None) 534 (c, a, r, d, u) = repo.changes(None, None)
412 if c or a or d or r: 535 if c or a or d or r:
413 if not force: 536 if not force:
423 n = repo.commit(commitfiles, 546 n = repo.commit(commitfiles,
424 "New patch: %s" % patch, force=True, wlock=wlock) 547 "New patch: %s" % patch, force=True, wlock=wlock)
425 if n == None: 548 if n == None:
426 raise util.Abort(_("repo commit failed")) 549 raise util.Abort(_("repo commit failed"))
427 self.full_series[insert:insert] = [patch] 550 self.full_series[insert:insert] = [patch]
428 self.applied.append(StatusEntry(revlog.hex(n), patch)) 551 self.applied.append(statusentry(revlog.hex(n), patch))
429 self.parse_series() 552 self.parse_series()
430 self.series_dirty = 1 553 self.series_dirty = 1
431 self.applied_dirty = 1 554 self.applied_dirty = 1
432 p = self.opener(patch, "w") 555 p = self.opener(patch, "w")
433 if msg: 556 if msg:
626 # sure the file name passed in does not exist (checked below) 749 # sure the file name passed in does not exist (checked below)
627 res = partial_name(patch) 750 res = partial_name(patch)
628 if res and res == patch: 751 if res and res == patch:
629 return res 752 return res
630 753
631 if not os.path.isfile(os.path.join(self.path, patch)): 754 if not os.path.isfile(self.join(patch)):
632 try: 755 try:
633 sno = int(patch) 756 sno = int(patch)
634 except(ValueError, OverflowError): 757 except(ValueError, OverflowError):
635 pass 758 pass
636 else: 759 else:
637 if sno < len(self.series): 760 if sno < len(self.series):
638 patch = self.series[sno] 761 return self.series[sno]
639 return patch
640 if not strict: 762 if not strict:
641 # return any partial match made above 763 # return any partial match made above
642 if res: 764 if res:
643 return res 765 return res
644 minus = patch.rsplit('-', 1) 766 minus = patch.rsplit('-', 1)
898 else: 1020 else:
899 message = msg 1021 message = msg
900 1022
901 self.strip(repo, top, update=False, backup='strip', wlock=wlock) 1023 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
902 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock) 1024 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
903 self.applied[-1] = StatusEntry(revlog.hex(n), patch) 1025 self.applied[-1] = statusentry(revlog.hex(n), patch)
904 self.applied_dirty = 1 1026 self.applied_dirty = 1
905 else: 1027 else:
906 commands.dodiff(patchf, self.ui, repo, patchparent, None) 1028 commands.dodiff(patchf, self.ui, repo, patchparent, None)
907 patchf.close() 1029 patchf.close()
908 self.pop(repo, force=True, wlock=wlock) 1030 self.pop(repo, force=True, wlock=wlock)
920 raise util.Abort(_("patch %s is not in series file") % patch) 1042 raise util.Abort(_("patch %s is not in series file") % patch)
921 if not patch: 1043 if not patch:
922 start = self.series_end() 1044 start = self.series_end()
923 else: 1045 else:
924 start = self.series.index(patch) + 1 1046 start = self.series.index(patch) + 1
925 return [(i, self.series[i]) for i in xrange(start, len(self.series))] 1047 unapplied = []
1048 for i in xrange(start, len(self.series)):
1049 pushable, reason = self.pushable(i)
1050 if pushable:
1051 unapplied.append((i, self.series[i]))
1052 self.explain_pushable(i)
1053 return unapplied
926 1054
927 def qseries(self, repo, missing=None, summary=False): 1055 def qseries(self, repo, missing=None, summary=False):
928 start = self.series_end() 1056 start = self.series_end(all_patches=True)
929 if not missing: 1057 if not missing:
930 for i in range(len(self.series)): 1058 for i in range(len(self.series)):
931 patch = self.series[i] 1059 patch = self.series[i]
932 if self.ui.verbose: 1060 if self.ui.verbose:
933 if i < start: 1061 if i < start:
934 status = 'A' 1062 status = 'A'
1063 elif self.pushable(i)[0]:
1064 status = 'U'
935 else: 1065 else:
936 status = 'U' 1066 status = 'G'
937 self.ui.write('%d %s ' % (i, status)) 1067 self.ui.write('%d %s ' % (i, status))
938 if summary: 1068 if summary:
939 msg = self.readheaders(patch)[0] 1069 msg = self.readheaders(patch)[0]
940 msg = msg and ': ' + msg[0] or ': ' 1070 msg = msg and ': ' + msg[0] or ': '
941 else: 1071 else:
956 if self.ui.verbose: 1086 if self.ui.verbose:
957 self.ui.write("D ") 1087 self.ui.write("D ")
958 self.ui.write("%s\n" % x) 1088 self.ui.write("%s\n" % x)
959 1089
960 def issaveline(self, l): 1090 def issaveline(self, l):
961 name = l.split(':')[1] 1091 if l.name == '.hg.patches.save.line':
962 if name == '.hg.patches.save.line':
963 return True 1092 return True
964 1093
965 def qrepo(self, create=False): 1094 def qrepo(self, create=False):
966 if create or os.path.isdir(os.path.join(self.path, ".hg")): 1095 if create or os.path.isdir(self.join(".hg")):
967 return hg.repository(self.ui, path=self.path, create=create) 1096 return hg.repository(self.ui, path=self.path, create=create)
968 1097
969 def restore(self, repo, rev, delete=None, qupdate=None): 1098 def restore(self, repo, rev, delete=None, qupdate=None):
970 c = repo.changelog.read(rev) 1099 c = repo.changelog.read(rev)
971 desc = c[4].strip() 1100 desc = c[4].strip()
982 l = lines[i].rstrip() 1111 l = lines[i].rstrip()
983 l = l[10:].split(' ') 1112 l = l[10:].split(' ')
984 qpp = [ hg.bin(x) for x in l ] 1113 qpp = [ hg.bin(x) for x in l ]
985 elif datastart != None: 1114 elif datastart != None:
986 l = lines[i].rstrip() 1115 l = lines[i].rstrip()
987 se = StatusEntry(l) 1116 se = statusentry(l)
988 file_ = se.name 1117 file_ = se.name
989 if se.rev: 1118 if se.rev:
990 applied.append(se) 1119 applied.append(se)
991 series.append(file_) 1120 series.append(file_)
992 if datastart == None: 1121 if datastart == None:
1037 r = self.qrepo() 1166 r = self.qrepo()
1038 if r: 1167 if r:
1039 pp = r.dirstate.parents() 1168 pp = r.dirstate.parents()
1040 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1])) 1169 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1041 msg += "\n\nPatch Data:\n" 1170 msg += "\n\nPatch Data:\n"
1042 text = msg + "\n".join(str(self.applied)) + '\n' + (ar and "\n".join(ar) 1171 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1043 + '\n' or "") 1172 "\n".join(ar) + '\n' or "")
1044 n = repo.commit(None, text, user=None, force=1) 1173 n = repo.commit(None, text, user=None, force=1)
1045 if not n: 1174 if not n:
1046 self.ui.warn("repo commit failed\n") 1175 self.ui.warn("repo commit failed\n")
1047 return 1 1176 return 1
1048 self.applied.append(StatusEntry(revlog.hex(n),'.hg.patches.save.line')) 1177 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1049 self.applied_dirty = 1 1178 self.applied_dirty = 1
1050 1179
1051 def full_series_end(self): 1180 def full_series_end(self):
1052 if len(self.applied) > 0: 1181 if len(self.applied) > 0:
1053 p = self.applied[-1].name 1182 p = self.applied[-1].name
1055 if end == None: 1184 if end == None:
1056 return len(self.full_series) 1185 return len(self.full_series)
1057 return end + 1 1186 return end + 1
1058 return 0 1187 return 0
1059 1188
1060 def series_end(self): 1189 def series_end(self, all_patches=False):
1061 end = 0 1190 end = 0
1191 def next(start):
1192 if all_patches:
1193 return start
1194 i = start
1195 while i < len(self.series):
1196 p, reason = self.pushable(i)
1197 if p:
1198 break
1199 self.explain_pushable(i)
1200 i += 1
1201 return i
1062 if len(self.applied) > 0: 1202 if len(self.applied) > 0:
1063 p = self.applied[-1].name 1203 p = self.applied[-1].name
1064 try: 1204 try:
1065 end = self.series.index(p) 1205 end = self.series.index(p)
1066 except ValueError: 1206 except ValueError:
1067 return 0 1207 return 0
1068 return end + 1 1208 return next(end + 1)
1069 return end 1209 return next(end)
1070 1210
1071 def qapplied(self, repo, patch=None): 1211 def qapplied(self, repo, patch=None):
1072 if patch and patch not in self.series: 1212 if patch and patch not in self.series:
1073 raise util.Abort(_("patch %s is not in series file") % patch) 1213 raise util.Abort(_("patch %s is not in series file") % patch)
1074 if not patch: 1214 if not patch:
1121 added = [] 1261 added = []
1122 for filename in files: 1262 for filename in files:
1123 if existing: 1263 if existing:
1124 if not patch: 1264 if not patch:
1125 patch = filename 1265 patch = filename
1126 if not os.path.isfile(os.path.join(self.path, patch)): 1266 if not os.path.isfile(self.join(patch)):
1127 raise util.Abort(_("patch %s does not exist") % patch) 1267 raise util.Abort(_("patch %s does not exist") % patch)
1128 else: 1268 else:
1129 try: 1269 try:
1130 text = file(filename).read() 1270 text = file(filename).read()
1131 except IOError: 1271 except IOError:
1132 raise util.Abort(_("unable to read %s") % patch) 1272 raise util.Abort(_("unable to read %s") % patch)
1133 if not patch: 1273 if not patch:
1134 patch = os.path.split(filename)[1] 1274 patch = os.path.split(filename)[1]
1135 if not force and os.path.exists(os.path.join(self.path, patch)): 1275 if not force and os.path.exists(self.join(patch)):
1136 raise util.Abort(_('patch "%s" already exists') % patch) 1276 raise util.Abort(_('patch "%s" already exists') % patch)
1137 patchf = self.opener(patch, "w") 1277 patchf = self.opener(patch, "w")
1138 patchf.write(text) 1278 patchf.write(text)
1139 if patch in self.series: 1279 if patch in self.series:
1140 raise util.Abort(_('patch %s is already in the series file') 1280 raise util.Abort(_('patch %s is already in the series file')
1345 patches.append(patch) 1485 patches.append(patch)
1346 1486
1347 for patch in patches: 1487 for patch in patches:
1348 if not message: 1488 if not message:
1349 messages.append(q.readheaders(patch)[0]) 1489 messages.append(q.readheaders(patch)[0])
1350 pf = os.path.join(q.path, patch) 1490 pf = q.join(patch)
1351 (patchsuccess, files, fuzz) = q.patch(repo, pf) 1491 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1352 if not patchsuccess: 1492 if not patchsuccess:
1353 raise util.Abort(_('Error folding patch %s') % patch) 1493 raise util.Abort(_('Error folding patch %s') % patch)
1354 1494
1355 if not message: 1495 if not message:
1366 1506
1367 for patch in patches: 1507 for patch in patches:
1368 q.delete(repo, patch, force=opts['force']) 1508 q.delete(repo, patch, force=opts['force'])
1369 1509
1370 q.save_dirty() 1510 q.save_dirty()
1511
1512 def guard(ui, repo, *args, **opts):
1513 '''set or print guards for a patch
1514
1515 guards control whether a patch can be pushed. a patch with no
1516 guards is aways pushed. a patch with posative guard ("+foo") is
1517 pushed only if qselect command enables guard "foo". a patch with
1518 nagative guard ("-foo") is never pushed if qselect command enables
1519 guard "foo".
1520
1521 with no arguments, default is to print current active guards.
1522 with arguments, set active guards for patch.
1523
1524 to set nagative guard "-foo" on topmost patch ("--" is needed so
1525 hg will not interpret "-foo" as argument):
1526 hg qguard -- -foo
1527
1528 to set guards on other patch:
1529 hg qguard other.patch +2.6.17 -stable
1530 '''
1531 def status(idx):
1532 guards = q.series_guards[idx] or ['unguarded']
1533 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1534 q = repo.mq
1535 patch = None
1536 args = list(args)
1537 if opts['list']:
1538 if args or opts['none']:
1539 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1540 for i in xrange(len(q.series)):
1541 status(i)
1542 return
1543 if not args or args[0][0:1] in '-+':
1544 if not q.applied:
1545 raise util.Abort(_('no patches applied'))
1546 patch = q.applied[-1].name
1547 if patch is None and args[0][0:1] not in '-+':
1548 patch = args.pop(0)
1549 if patch is None:
1550 raise util.Abort(_('no patch to work with'))
1551 if args or opts['none']:
1552 q.set_guards(q.find_series(patch), args)
1553 q.save_dirty()
1554 else:
1555 status(q.series.index(q.lookup(patch)))
1371 1556
1372 def header(ui, repo, patch=None): 1557 def header(ui, repo, patch=None):
1373 """Print the header of the topmost or specified patch""" 1558 """Print the header of the topmost or specified patch"""
1374 q = repo.mq 1559 q = repo.mq
1375 1560
1456 patch = None 1641 patch = None
1457 1642
1458 if name in q.series: 1643 if name in q.series:
1459 raise util.Abort(_('A patch named %s already exists in the series file') % name) 1644 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1460 1645
1461 absdest = os.path.join(q.path, name) 1646 absdest = q.join(name)
1462 if os.path.exists(absdest): 1647 if os.path.exists(absdest):
1463 raise util.Abort(_('%s already exists') % absdest) 1648 raise util.Abort(_('%s already exists') % absdest)
1464 1649
1465 if patch: 1650 if patch:
1466 patch = q.lookup(patch) 1651 patch = q.lookup(patch)
1477 q.parse_series() 1662 q.parse_series()
1478 q.series_dirty = 1 1663 q.series_dirty = 1
1479 1664
1480 info = q.isapplied(patch) 1665 info = q.isapplied(patch)
1481 if info: 1666 if info:
1482 q.applied[info[0]] = StatusEntry(info[1], name) 1667 q.applied[info[0]] = statusentry(info[1], name)
1483 q.applied_dirty = 1 1668 q.applied_dirty = 1
1484 1669
1485 util.rename(os.path.join(q.path, patch), absdest) 1670 util.rename(q.join(patch), absdest)
1486 r = q.qrepo() 1671 r = q.qrepo()
1487 if r: 1672 if r:
1488 wlock = r.wlock() 1673 wlock = r.wlock()
1489 if r.dirstate.state(name) == 'r': 1674 if r.dirstate.state(name) == 'r':
1490 r.undelete([name], wlock) 1675 r.undelete([name], wlock)
1525 newpath = savename(path) 1710 newpath = savename(path)
1526 ui.warn("copy %s to %s\n" % (path, newpath)) 1711 ui.warn("copy %s to %s\n" % (path, newpath))
1527 util.copyfiles(path, newpath) 1712 util.copyfiles(path, newpath)
1528 if opts['empty']: 1713 if opts['empty']:
1529 try: 1714 try:
1530 os.unlink(os.path.join(q.path, q.status_path)) 1715 os.unlink(q.join(q.status_path))
1531 except: 1716 except:
1532 pass 1717 pass
1533 return 0 1718 return 0
1534 1719
1535 def strip(ui, repo, rev, **opts): 1720 def strip(ui, repo, rev, **opts):
1541 elif opts['nobackup']: 1726 elif opts['nobackup']:
1542 backup = 'none' 1727 backup = 'none'
1543 repo.mq.strip(repo, rev, backup=backup) 1728 repo.mq.strip(repo, rev, backup=backup)
1544 return 0 1729 return 0
1545 1730
1546 def version(ui, q=None): 1731 def select(ui, repo, *args, **opts):
1547 """print the version number of the mq extension""" 1732 '''set or print guarded patches to push
1548 ui.write("mq version %s\n" % versionstr) 1733
1549 return 0 1734 use qguard command to set or print guards on patch. then use
1735 qselect to tell mq which guards to use. example:
1736
1737 qguard foo.patch -stable (nagative guard)
1738 qguard bar.patch +stable (posative guard)
1739 qselect stable
1740
1741 this sets "stable" guard. mq will skip foo.patch (because it has
1742 nagative match) but push bar.patch (because it has posative
1743 match). patch is pushed only if all posative guards match and no
1744 nagative guards match.
1745
1746 with no arguments, default is to print current active guards.
1747 with arguments, set active guards as given.
1748
1749 use -n/--none to deactivate guards (no other arguments needed).
1750 when no guards active, patches with posative guards are skipped,
1751 patches with nagative guards are pushed.
1752
1753 use -s/--series to print list of all guards in series file (no
1754 other arguments needed). use -v for more information.'''
1755
1756 q = repo.mq
1757 guards = q.active()
1758 if args or opts['none']:
1759 q.set_active(args)
1760 q.save_dirty()
1761 if not args:
1762 ui.status(_('guards deactivated\n'))
1763 if q.series:
1764 ui.status(_('%d of %d unapplied patches active\n') %
1765 (len(q.unapplied(repo)), len(q.series)))
1766 elif opts['series']:
1767 guards = {}
1768 noguards = 0
1769 for gs in q.series_guards:
1770 if not gs:
1771 noguards += 1
1772 for g in gs:
1773 guards.setdefault(g, 0)
1774 guards[g] += 1
1775 if ui.verbose:
1776 guards['NONE'] = noguards
1777 guards = guards.items()
1778 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1779 if guards:
1780 ui.note(_('guards in series file:\n'))
1781 for guard, count in guards:
1782 ui.note('%2d ' % count)
1783 ui.write(guard, '\n')
1784 else:
1785 ui.note(_('no guards in series file\n'))
1786 else:
1787 if guards:
1788 ui.note(_('active guards:\n'))
1789 for g in guards:
1790 ui.write(g, '\n')
1791 else:
1792 ui.write(_('no active guards\n'))
1550 1793
1551 def reposetup(ui, repo): 1794 def reposetup(ui, repo):
1552 class MqRepo(repo.__class__): 1795 class mqrepo(repo.__class__):
1553 def tags(self): 1796 def tags(self):
1554 if self.tagscache: 1797 if self.tagscache:
1555 return self.tagscache 1798 return self.tagscache
1556 1799
1557 tagscache = super(MqRepo, self).tags() 1800 tagscache = super(mqrepo, self).tags()
1558 1801
1559 q = self.mq 1802 q = self.mq
1560 if not q.applied: 1803 if not q.applied:
1561 return tagscache 1804 return tagscache
1562 1805
1569 else: 1812 else:
1570 tagscache[patch[1]] = revlog.bin(patch[0]) 1813 tagscache[patch[1]] = revlog.bin(patch[0])
1571 1814
1572 return tagscache 1815 return tagscache
1573 1816
1574 repo.__class__ = MqRepo 1817 repo.__class__ = mqrepo
1575 repo.mq = queue(ui, repo.join("")) 1818 repo.mq = queue(ui, repo.join(""))
1576 1819
1577 cmdtable = { 1820 cmdtable = {
1578 "qapplied": (applied, [], 'hg qapplied [PATCH]'), 1821 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1579 "qclone": (clone, 1822 "qclone": (clone,
1600 [('e', 'edit', None, _('edit patch header')), 1843 [('e', 'edit', None, _('edit patch header')),
1601 ('f', 'force', None, _('delete folded patch files')), 1844 ('f', 'force', None, _('delete folded patch files')),
1602 ('m', 'message', '', _('set patch header to <text>')), 1845 ('m', 'message', '', _('set patch header to <text>')),
1603 ('l', 'logfile', '', _('set patch header to contents of <file>'))], 1846 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1604 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'), 1847 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
1848 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
1849 ('n', 'none', None, _('drop all guards'))],
1850 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
1605 'qheader': (header, [], 1851 'qheader': (header, [],
1606 _('hg qheader [PATCH]')), 1852 _('hg qheader [PATCH]')),
1607 "^qimport": 1853 "^qimport":
1608 (qimport, 1854 (qimport,
1609 [('e', 'existing', None, 'import file in patch dir'), 1855 [('e', 'existing', None, 'import file in patch dir'),
1657 ('c', 'copy', None, 'copy patch directory'), 1903 ('c', 'copy', None, 'copy patch directory'),
1658 ('n', 'name', '', 'copy directory name'), 1904 ('n', 'name', '', 'copy directory name'),
1659 ('e', 'empty', None, 'clear queue status file'), 1905 ('e', 'empty', None, 'clear queue status file'),
1660 ('f', 'force', None, 'force copy')], 1906 ('f', 'force', None, 'force copy')],
1661 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'), 1907 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1908 "qselect": (select,
1909 [('n', 'none', None, _('disable all guards')),
1910 ('s', 'series', None, _('list all guards in series file'))],
1911 'hg qselect [GUARDS]'),
1662 "qseries": 1912 "qseries":
1663 (series, 1913 (series,
1664 [('m', 'missing', None, 'print patches not in series'), 1914 [('m', 'missing', None, 'print patches not in series'),
1665 ('s', 'summary', None, _('print first line of patch header'))], 1915 ('s', 'summary', None, _('print first line of patch header'))],
1666 'hg qseries [-m]'), 1916 'hg qseries [-m]'),
1670 ('b', 'backup', None, 'bundle unrelated changesets'), 1920 ('b', 'backup', None, 'bundle unrelated changesets'),
1671 ('n', 'nobackup', None, 'no backups')], 1921 ('n', 'nobackup', None, 'no backups')],
1672 'hg strip [-f] [-b] [-n] REV'), 1922 'hg strip [-f] [-b] [-n] REV'),
1673 "qtop": (top, [], 'hg qtop'), 1923 "qtop": (top, [], 'hg qtop'),
1674 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'), 1924 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1675 "qversion": (version, [], 'hg qversion')
1676 } 1925 }
1677 1926