##// END OF EJS Templates
merge with self.
Vadim Gelfer -
r2787:01488693 merge default
parent child Browse files
Show More
@@ -39,6 +39,16 b' versionstr = "0.45"'
39 39
40 40 commands.norepo += " qclone qversion"
41 41
42 class StatusEntry:
43 def __init__(self, rev, name=None):
44 if not name:
45 self.rev, self.name = rev.split(':')
46 else:
47 self.rev, self.name = rev, name
48
49 def __str__(self):
50 return self.rev + ':' + self.name
51
42 52 class queue:
43 53 def __init__(self, ui, path, patchdir=None):
44 54 self.basepath = path
@@ -57,10 +67,11 b' class queue:'
57 67
58 68 if os.path.exists(os.path.join(self.path, self.series_path)):
59 69 self.full_series = self.opener(self.series_path).read().splitlines()
60 self.read_series(self.full_series)
70 self.parse_series()
61 71
62 72 if os.path.exists(os.path.join(self.path, self.status_path)):
63 self.applied = self.opener(self.status_path).read().splitlines()
73 self.applied = [StatusEntry(l)
74 for l in self.opener(self.status_path).read().splitlines()]
64 75
65 76 def find_series(self, patch):
66 77 pre = re.compile("(\s*)([^#]+)")
@@ -75,34 +86,21 b' class queue:'
75 86 index += 1
76 87 return None
77 88
78 def read_series(self, list):
79 def matcher(list):
80 pre = re.compile("(\s*)([^#]+)")
81 for l in list:
82 m = pre.match(l)
83 if m:
84 s = m.group(2)
85 s = s.rstrip()
86 if len(s) > 0:
87 yield s
89 def parse_series(self):
88 90 self.series = []
89 self.series = [ x for x in matcher(list) ]
91 for l in self.full_series:
92 s = l.split('#', 1)[0].strip()
93 if s:
94 self.series.append(s)
90 95
91 96 def save_dirty(self):
92 if self.applied_dirty:
93 if len(self.applied) > 0:
94 nl = "\n"
95 else:
96 nl = ""
97 f = self.opener(self.status_path, "w")
98 f.write("\n".join(self.applied) + nl)
99 if self.series_dirty:
100 if len(self.full_series) > 0:
101 nl = "\n"
102 else:
103 nl = ""
104 f = self.opener(self.series_path, "w")
105 f.write("\n".join(self.full_series) + nl)
97 def write_list(items, path):
98 fp = self.opener(path, 'w')
99 for i in items:
100 print >> fp, i
101 fp.close()
102 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)
106 104
107 105 def readheaders(self, patch):
108 106 def eatdiff(lines):
@@ -222,12 +220,10 b' class queue:'
222 220 return p1
223 221 if len(self.applied) == 0:
224 222 return None
225 (top, patch) = self.applied[-1].split(':')
226 top = revlog.bin(top)
227 return top
223 return revlog.bin(self.applied[-1].rev)
228 224 pp = repo.changelog.parents(rev)
229 225 if pp[1] != revlog.nullid:
230 arevs = [ x.split(':')[0] for x in self.applied ]
226 arevs = [ x.rev for x in self.applied ]
231 227 p0 = revlog.hex(pp[0])
232 228 p1 = revlog.hex(pp[1])
233 229 if p0 in arevs:
@@ -247,7 +243,7 b' class queue:'
247 243 pname = ".hg.patches.merge.marker"
248 244 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
249 245 wlock=wlock)
250 self.applied.append(revlog.hex(n) + ":" + pname)
246 self.applied.append(StatusEntry(revlog.hex(n), pname))
251 247 self.applied_dirty = 1
252 248
253 249 head = self.qparents(repo)
@@ -265,7 +261,7 b' class queue:'
265 261 rev = revlog.bin(info[1])
266 262 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
267 263 if head:
268 self.applied.append(revlog.hex(head) + ":" + patch)
264 self.applied.append(StatusEntry(revlog.hex(head), patch))
269 265 self.applied_dirty = 1
270 266 if err:
271 267 return (err, head)
@@ -364,7 +360,7 b' class queue:'
364 360 raise util.Abort(_("repo commit failed"))
365 361
366 362 if update_status:
367 self.applied.append(revlog.hex(n) + ":" + patch)
363 self.applied.append(StatusEntry(revlog.hex(n), patch))
368 364
369 365 if patcherr:
370 366 if not patchfound:
@@ -397,13 +393,12 b' class queue:'
397 393 os.unlink(os.path.join(self.path, patch))
398 394 i = self.find_series(patch)
399 395 del self.full_series[i]
400 self.read_series(self.full_series)
396 self.parse_series()
401 397 self.series_dirty = 1
402 398
403 399 def check_toppatch(self, repo):
404 400 if len(self.applied) > 0:
405 (top, patch) = self.applied[-1].split(':')
406 top = revlog.bin(top)
401 top = revlog.bin(self.applied[-1].rev)
407 402 pp = repo.dirstate.parents()
408 403 if top not in pp:
409 404 raise util.Abort(_("queue top not at same revision as working directory"))
@@ -434,8 +429,8 b' class queue:'
434 429 if n == None:
435 430 raise util.Abort(_("repo commit failed"))
436 431 self.full_series[insert:insert] = [patch]
437 self.applied.append(revlog.hex(n) + ":" + patch)
438 self.read_series(self.full_series)
432 self.applied.append(StatusEntry(revlog.hex(n), patch))
433 self.parse_series()
439 434 self.series_dirty = 1
440 435 self.applied_dirty = 1
441 436 p = self.opener(patch, "w")
@@ -600,10 +595,9 b' class queue:'
600 595 def isapplied(self, patch):
601 596 """returns (index, rev, patch)"""
602 597 for i in xrange(len(self.applied)):
603 p = self.applied[i]
604 a = p.split(':')
605 if a[1] == patch:
606 return (i, a[0], a[1])
598 a = self.applied[i]
599 if a.name == patch:
600 return (i, a.rev, a.name)
607 601 return None
608 602
609 603 # if the exact patch name does not exist, we try a few
@@ -706,7 +700,7 b' class queue:'
706 700 ret = self.mergepatch(repo, mergeq, s, wlock)
707 701 else:
708 702 ret = self.apply(repo, s, list, wlock=wlock)
709 top = self.applied[-1].split(':')[1]
703 top = self.applied[-1].name
710 704 if ret[0]:
711 705 self.ui.write("Errors during apply, please fix and refresh %s\n" %
712 706 top)
@@ -743,7 +737,7 b' class queue:'
743 737
744 738 if not update:
745 739 parents = repo.dirstate.parents()
746 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
740 rr = [ revlog.bin(x.rev) for x in self.applied ]
747 741 for p in parents:
748 742 if p in rr:
749 743 self.ui.warn("qpop: forcing dirstate update\n")
@@ -764,7 +758,7 b' class queue:'
764 758 if popi >= end:
765 759 self.ui.warn("qpop: %s is already at the top\n" % patch)
766 760 return
767 info = [ popi ] + self.applied[popi].split(':')
761 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
768 762
769 763 start = info[0]
770 764 rev = revlog.bin(info[1])
@@ -797,7 +791,7 b' class queue:'
797 791 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
798 792 del self.applied[start:end]
799 793 if len(self.applied):
800 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
794 self.ui.write("Now at: %s\n" % self.applied[-1].name)
801 795 else:
802 796 self.ui.write("Patch queue now empty\n")
803 797
@@ -816,7 +810,7 b' class queue:'
816 810 wlock = repo.wlock()
817 811 self.check_toppatch(repo)
818 812 qp = self.qparents(repo)
819 (top, patch) = self.applied[-1].split(':')
813 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
820 814 top = revlog.bin(top)
821 815 cparents = repo.changelog.parents(top)
822 816 patchparent = self.qparents(repo, top)
@@ -912,7 +906,7 b' class queue:'
912 906
913 907 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
914 908 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
915 self.applied[-1] = revlog.hex(n) + ':' + patch
909 self.applied[-1] = StatusEntry(revlog.hex(n), patch)
916 910 self.applied_dirty = 1
917 911 else:
918 912 commands.dodiff(patchf, self.ui, repo, patchparent, None)
@@ -934,10 +928,7 b' class queue:'
934 928 start = self.series_end()
935 929 else:
936 930 start = self.series.index(patch) + 1
937 for p in self.series[start:]:
938 if self.ui.verbose:
939 self.ui.write("%d " % self.series.index(p))
940 self.ui.write("%s\n" % p)
931 return [(i, self.series[i]) for i in xrange(start, len(self.series))]
941 932
942 933 def qseries(self, repo, missing=None, summary=False):
943 934 start = self.series_end()
@@ -1000,11 +991,11 b' class queue:'
1000 991 qpp = [ hg.bin(x) for x in l ]
1001 992 elif datastart != None:
1002 993 l = lines[i].rstrip()
1003 index = l.index(':')
1004 id = l[:index]
1005 file = l[index + 1:]
994 se = StatusEntry(l)
995 id = se.rev
996 file = se.name
1006 997 if id:
1007 applied.append(l)
998 applied.append(se)
1008 999 series.append(file)
1009 1000 if datastart == None:
1010 1001 self.ui.warn("No saved patch data found\n")
@@ -1012,7 +1003,7 b' class queue:'
1012 1003 self.ui.warn("restoring status: %s\n" % lines[0])
1013 1004 self.full_series = series
1014 1005 self.applied = applied
1015 self.read_series(self.full_series)
1006 self.parse_series()
1016 1007 self.series_dirty = 1
1017 1008 self.applied_dirty = 1
1018 1009 heads = repo.changelog.heads()
@@ -1056,18 +1047,18 b' class queue:'
1056 1047 pp = r.dirstate.parents()
1057 1048 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1058 1049 msg += "\n\nPatch Data:\n"
1059 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1050 text = msg + "\n".join(str(self.applied)) + '\n' + (ar and "\n".join(ar)
1060 1051 + '\n' or "")
1061 1052 n = repo.commit(None, text, user=None, force=1)
1062 1053 if not n:
1063 1054 self.ui.warn("repo commit failed\n")
1064 1055 return 1
1065 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1056 self.applied.append(StatusEntry(revlog.hex(n),'.hg.patches.save.line'))
1066 1057 self.applied_dirty = 1
1067 1058
1068 1059 def full_series_end(self):
1069 1060 if len(self.applied) > 0:
1070 (top, p) = self.applied[-1].split(':')
1061 p = self.applied[-1].name
1071 1062 end = self.find_series(p)
1072 1063 if end == None:
1073 1064 return len(self.full_series)
@@ -1077,7 +1068,7 b' class queue:'
1077 1068 def series_end(self):
1078 1069 end = 0
1079 1070 if len(self.applied) > 0:
1080 (top, p) = self.applied[-1].split(':')
1071 p = self.applied[-1].name
1081 1072 try:
1082 1073 end = self.series.index(p)
1083 1074 except ValueError:
@@ -1097,8 +1088,7 b' class queue:'
1097 1088 self.ui.write("%s\n" % p)
1098 1089
1099 1090 def appliedname(self, index):
1100 p = self.applied[index]
1101 pname = p.split(':')[1]
1091 pname = self.applied[index].name
1102 1092 if not self.ui.verbose:
1103 1093 p = pname
1104 1094 else:
@@ -1159,7 +1149,7 b' class queue:'
1159 1149 % patch)
1160 1150 index = self.full_series_end() + i
1161 1151 self.full_series[index:index] = [patch]
1162 self.read_series(self.full_series)
1152 self.parse_series()
1163 1153 self.ui.warn("adding %s to series file\n" % patch)
1164 1154 i += 1
1165 1155 added.append(patch)
@@ -1186,8 +1176,10 b' def applied(ui, repo, patch=None, **opts'
1186 1176
1187 1177 def unapplied(ui, repo, patch=None, **opts):
1188 1178 """print the patches not yet applied"""
1189 repo.mq.unapplied(repo, patch)
1190 return 0
1179 for i, p in repo.mq.unapplied(repo, patch):
1180 if ui.verbose:
1181 ui.write("%d " % i)
1182 ui.write("%s\n" % p)
1191 1183
1192 1184 def qimport(ui, repo, *filename, **opts):
1193 1185 """import a patch"""
@@ -1228,7 +1220,7 b' def clone(ui, source, dest=None, **opts)'
1228 1220 Source patch repository is looked for in <src>/.hg/patches by
1229 1221 default. Use -p <url> to change.
1230 1222 '''
1231 commands.setremoteconfig(**opts)
1223 commands.setremoteconfig(ui, opts)
1232 1224 if dest is None:
1233 1225 dest = hg.defaultdest(source)
1234 1226 sr = hg.repository(ui, ui.expandpath(source))
@@ -1236,7 +1228,7 b' def clone(ui, source, dest=None, **opts)'
1236 1228 if sr.local():
1237 1229 reposetup(ui, sr)
1238 1230 if sr.mq.applied:
1239 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1231 qbase = revlog.bin(sr.mq.applied[0].rev)
1240 1232 if not hg.islocal(dest):
1241 1233 destrev = sr.parents(qbase)[0]
1242 1234 ui.note(_('cloning main repo\n'))
@@ -1297,10 +1289,7 b' def new(ui, repo, patch, **opts):'
1297 1289
1298 1290 -m or -l set the patch header as well as the commit message.
1299 1291 If neither is specified, the patch header is empty and the
1300 commit message is 'New patch: PATCH'
1301
1302 If -f is specified, the patch will be initialized with any
1303 uncommitted changes. Otherwise, if there outsta"""
1292 commit message is 'New patch: PATCH'"""
1304 1293 q = repo.mq
1305 1294 message=commands.logmessage(**opts)
1306 1295 q.new(repo, patch, msg=message, force=opts['force'])
@@ -1314,7 +1303,7 b' def refresh(ui, repo, **opts):'
1314 1303 if opts['edit']:
1315 1304 if message:
1316 1305 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1317 patch = q.applied[-1].split(':')[1]
1306 patch = q.applied[-1].name
1318 1307 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1319 1308 message = ui.edit('\n'.join(message), user or ui.username())
1320 1309 q.refresh(repo, msg=message, short=opts['short'])
@@ -1330,7 +1319,13 b' def diff(ui, repo, *files, **opts):'
1330 1319 def fold(ui, repo, *files, **opts):
1331 1320 """fold the named patches into the current patch
1332 1321
1333 Patches must not yet be applied.
1322 Patches must not yet be applied. Each patch will be successively
1323 applied to the current patch in the order given. If all the
1324 patches apply successfully, the current patch will be refreshed
1325 with the new cumulative patch, and the folded patches will
1326 be deleted. With -f/--force, the folded patch files will
1327 be removed afterwards.
1328
1334 1329 The header for each folded patch will be concatenated with
1335 1330 the current patch header, separated by a line of '* * *'."""
1336 1331
@@ -1378,7 +1373,7 b' def fold(ui, repo, *files, **opts):'
1378 1373 q.refresh(repo, msg=message)
1379 1374
1380 1375 for patch in patches:
1381 q.delete(repo, patch)
1376 q.delete(repo, patch, force=opts['force'])
1382 1377
1383 1378 q.save_dirty()
1384 1379
@@ -1487,12 +1482,12 b' def rename(ui, repo, patch, name=None, *'
1487 1482 ui.write('Renaming %s to %s\n' % (patch, name))
1488 1483 i = q.find_series(patch)
1489 1484 q.full_series[i] = name
1490 q.read_series(q.full_series)
1485 q.parse_series()
1491 1486 q.series_dirty = 1
1492 1487
1493 1488 info = q.isapplied(patch)
1494 1489 if info:
1495 q.applied[info[0]] = info[1] + ':' + name
1490 q.applied[info[0]] = StatusEntry(info[1], name)
1496 1491 q.applied_dirty = 1
1497 1492
1498 1493 util.rename(os.path.join(q.path, patch), absdest)
@@ -1573,7 +1568,7 b' def reposetup(ui, repo):'
1573 1568 if not q.applied:
1574 1569 return tagscache
1575 1570
1576 mqtags = [patch.split(':') for patch in q.applied]
1571 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1577 1572 mqtags.append((mqtags[-1][0], 'qtip'))
1578 1573 mqtags.append((mqtags[0][0], 'qbase'))
1579 1574 for patch in mqtags:
@@ -1611,6 +1606,7 b' cmdtable = {'
1611 1606 'qfold':
1612 1607 (fold,
1613 1608 [('e', 'edit', None, _('edit patch header')),
1609 ('f', 'force', None, _('delete folded patch files')),
1614 1610 ('m', 'message', '', _('set patch header to <text>')),
1615 1611 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1616 1612 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
@@ -13,7 +13,8 b' demandload(globals(), "localrepo bundler'
13 13 demandload(globals(), "errno lock os shutil util")
14 14
15 15 def _local(path):
16 return os.path.isfile(util.drop_scheme('file', path)) and bundlerepo or localrepo
16 return (os.path.isfile(path and util.drop_scheme('file', path)) and
17 bundlerepo or localrepo)
17 18
18 19 schemes = {
19 20 'bundle': bundlerepo,
@@ -1,7 +1,10 b''
1 1 #!/bin/sh
2 2
3 3 hg init a
4 mkdir a/d1
5 mkdir a/d1/d2
4 6 echo line 1 > a/a
7 echo line 1 > a/d1/d2/a
5 8 hg --cwd a ci -d '0 0' -Ama
6 9
7 10 echo line 2 >> a/a
@@ -79,3 +82,19 b' python mkmsg.py | hg --cwd b import -'
79 82 hg --cwd b tip | grep second
80 83 rm -rf b
81 84
85 # bug non regression test
86 # importing a patch in a subdirectory failed at the commit stage
87 echo line 2 >> a/d1/d2/a
88 hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
89 echo % hg import in a subdirectory
90 hg clone -r0 a b
91 hg --cwd a export tip | sed -e 's/d1\/d2\///' > tip.patch
92 pushd b/d1/d2 2>&1 > /dev/null
93 hg import ../../../tip.patch
94 popd 2>&1 > /dev/null
95 echo "% message should be 'subdir change'"
96 hg --cwd b tip | grep 'subdir change'
97 echo "% committer should be 'someoneelse'"
98 hg --cwd b tip | grep someoneelse
99 echo "% should be empty"
100 hg --cwd b status
@@ -1,11 +1,12 b''
1 1 adding a
2 adding d1/d2/a
2 3 % import exported patch
3 4 requesting all changes
4 5 adding changesets
5 6 adding manifests
6 7 adding file changes
7 added 1 changesets with 1 changes to 1 files
8 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
8 added 1 changesets with 2 changes to 2 files
9 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
9 10 applying ../tip.patch
10 11 patching file a
11 12 % message should be same
@@ -17,8 +18,8 b' requesting all changes'
17 18 adding changesets
18 19 adding manifests
19 20 adding file changes
20 added 1 changesets with 1 changes to 1 files
21 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 added 1 changesets with 2 changes to 2 files
22 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 23 applying ../tip.patch
23 24 patching file a
24 25 transaction abort!
@@ -28,8 +29,8 b' requesting all changes'
28 29 adding changesets
29 30 adding manifests
30 31 adding file changes
31 added 1 changesets with 1 changes to 1 files
32 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 added 1 changesets with 2 changes to 2 files
33 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 34 applying ../tip.patch
34 35 patching file a
35 36 % import from stdin
@@ -37,8 +38,8 b' requesting all changes'
37 38 adding changesets
38 39 adding manifests
39 40 adding file changes
40 added 1 changesets with 1 changes to 1 files
41 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 added 1 changesets with 2 changes to 2 files
42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 43 applying patch from stdin
43 44 patching file a
44 45 % override commit message
@@ -46,8 +47,8 b' requesting all changes'
46 47 adding changesets
47 48 adding manifests
48 49 adding file changes
49 added 1 changesets with 1 changes to 1 files
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 added 1 changesets with 2 changes to 2 files
51 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 52 applying patch from stdin
52 53 patching file a
53 54 summary: override
@@ -56,8 +57,8 b' requesting all changes'
56 57 adding changesets
57 58 adding manifests
58 59 adding file changes
59 added 1 changesets with 1 changes to 1 files
60 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 added 1 changesets with 2 changes to 2 files
61 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 62 applying ../msg.patch
62 63 patching file a
63 64 user: email patcher
@@ -67,8 +68,8 b' requesting all changes'
67 68 adding changesets
68 69 adding manifests
69 70 adding file changes
70 added 1 changesets with 1 changes to 1 files
71 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 added 1 changesets with 2 changes to 2 files
72 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 73 applying patch from stdin
73 74 patching file a
74 75 % plain diff in email, subject, no message body
@@ -76,8 +77,8 b' requesting all changes'
76 77 adding changesets
77 78 adding manifests
78 79 adding file changes
79 added 1 changesets with 1 changes to 1 files
80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 added 1 changesets with 2 changes to 2 files
81 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 82 applying patch from stdin
82 83 patching file a
83 84 % plain diff in email, no subject, no message body, should fail
@@ -85,8 +86,8 b' requesting all changes'
85 86 adding changesets
86 87 adding manifests
87 88 adding file changes
88 added 1 changesets with 1 changes to 1 files
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 added 1 changesets with 2 changes to 2 files
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 91 applying patch from stdin
91 92 patching file a
92 93 transaction abort!
@@ -96,8 +97,22 b' requesting all changes'
96 97 adding changesets
97 98 adding manifests
98 99 adding file changes
99 added 1 changesets with 1 changes to 1 files
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 added 1 changesets with 2 changes to 2 files
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 102 applying patch from stdin
102 103 patching file a
103 104 summary: second change
105 % hg import in a subdirectory
106 requesting all changes
107 adding changesets
108 adding manifests
109 adding file changes
110 added 1 changesets with 2 changes to 2 files
111 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
112 applying ../../../tip.patch
113 patching file a
114 % message should be 'subdir change'
115 summary: subdir change
116 % committer should be 'someoneelse'
117 user: someoneelse
118 % should be empty
General Comments 0
You need to be logged in to leave comments. Login now