Show More
@@ -0,0 +1,93 b'' | |||
|
1 | # fetch.py - pull and merge remote changes | |
|
2 | # | |
|
3 | # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | |
|
4 | # | |
|
5 | # This software may be used and distributed according to the terms | |
|
6 | # of the GNU General Public License, incorporated herein by reference. | |
|
7 | ||
|
8 | from mercurial.demandload import * | |
|
9 | from mercurial.i18n import gettext as _ | |
|
10 | from mercurial.node import * | |
|
11 | demandload(globals(), 'mercurial:commands,hg,node,util') | |
|
12 | ||
|
13 | def fetch(ui, repo, source='default', **opts): | |
|
14 | '''Pull changes from a remote repository, merge new changes if needed. | |
|
15 | ||
|
16 | This finds all changes from the repository at the specified path | |
|
17 | or URL and adds them to the local repository. | |
|
18 | ||
|
19 | If the pulled changes add a new head, the head is automatically | |
|
20 | merged, and the result of the merge is committed. Otherwise, the | |
|
21 | working directory is updated.''' | |
|
22 | ||
|
23 | def postincoming(other, modheads): | |
|
24 | if modheads == 0: | |
|
25 | return 0 | |
|
26 | if modheads == 1: | |
|
27 | return commands.doupdate(ui, repo) | |
|
28 | newheads = repo.heads(parent) | |
|
29 | newchildren = [n for n in repo.heads(parent) if n != parent] | |
|
30 | newparent = parent | |
|
31 | if newchildren: | |
|
32 | commands.doupdate(ui, repo, node=hex(newchildren[0])) | |
|
33 | newparent = newchildren[0] | |
|
34 | newheads = [n for n in repo.heads() if n != newparent] | |
|
35 | err = False | |
|
36 | if newheads: | |
|
37 | ui.status(_('merging with new head %d:%s\n') % | |
|
38 | (repo.changelog.rev(newheads[0]), short(newheads[0]))) | |
|
39 | err = repo.update(newheads[0], allow=True, remind=False) | |
|
40 | if not err and len(newheads) > 1: | |
|
41 | ui.status(_('not merging with %d other new heads ' | |
|
42 | '(use "hg heads" and "hg merge" to merge them)') % | |
|
43 | (len(newheads) - 1)) | |
|
44 | if not err: | |
|
45 | mod, add, rem = repo.status()[:3] | |
|
46 | message = (commands.logmessage(opts) or | |
|
47 | (_('Automated merge with %s') % other.url())) | |
|
48 | n = repo.commit(mod + add + rem, message, | |
|
49 | opts['user'], opts['date'], | |
|
50 | force_editor=opts.get('force_editor')) | |
|
51 | ui.status(_('new changeset %d:%s merges remote changes ' | |
|
52 | 'with local\n') % (repo.changelog.rev(n), | |
|
53 | short(n))) | |
|
54 | def pull(): | |
|
55 | commands.setremoteconfig(ui, opts) | |
|
56 | ||
|
57 | other = hg.repository(ui, ui.expandpath(source)) | |
|
58 | ui.status(_('pulling from %s\n') % source) | |
|
59 | revs = None | |
|
60 | if opts['rev'] and not other.local(): | |
|
61 | raise util.Abort(_("fetch -r doesn't work for remote repositories yet")) | |
|
62 | elif opts['rev']: | |
|
63 | revs = [other.lookup(rev) for rev in opts['rev']] | |
|
64 | modheads = repo.pull(other, heads=revs) | |
|
65 | return postincoming(other, modheads) | |
|
66 | ||
|
67 | parent, p2 = repo.dirstate.parents() | |
|
68 | if parent != repo.changelog.tip(): | |
|
69 | raise util.Abort(_('working dir not at tip ' | |
|
70 | '(use "hg update" to check out tip)')) | |
|
71 | if p2 != nullid: | |
|
72 | raise util.Abort(_('outstanding uncommitted merge')) | |
|
73 | mod, add, rem = repo.status()[:3] | |
|
74 | if mod or add or rem: | |
|
75 | raise util.Abort(_('outstanding uncommitted changes')) | |
|
76 | if len(repo.heads()) > 1: | |
|
77 | raise util.Abort(_('multiple heads in this repository ' | |
|
78 | '(use "hg heads" and "hg merge" to merge them)')) | |
|
79 | return pull() | |
|
80 | ||
|
81 | cmdtable = { | |
|
82 | 'fetch': | |
|
83 | (fetch, | |
|
84 | [('e', 'ssh', '', _('specify ssh command to use')), | |
|
85 | ('m', 'message', '', _('use <text> as commit message')), | |
|
86 | ('l', 'logfile', '', _('read the commit message from <file>')), | |
|
87 | ('d', 'date', '', _('record datecode as commit date')), | |
|
88 | ('u', 'user', '', _('record user as commiter')), | |
|
89 | ('r', 'rev', [], _('a specific revision you would like to pull')), | |
|
90 | ('f', 'force-editor', None, _('edit commit message')), | |
|
91 | ('', 'remotecmd', '', _('hg command to run on the remote side'))], | |
|
92 | 'hg fetch [SOURCE]'), | |
|
93 | } |
@@ -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 |
@@ -60,7 +70,8 b' class queue:' | |||
|
60 | 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*)([^#]+)") |
@@ -88,7 +99,7 b' class queue:' | |||
|
88 | 99 | for i in items: |
|
89 | 100 | print >> fp, i |
|
90 | 101 | fp.close() |
|
91 | if self.applied_dirty: write_list(self.applied, self.status_path) | |
|
102 | if self.applied_dirty: write_list(map(str, self.applied), self.status_path) | |
|
92 | 103 | if self.series_dirty: write_list(self.full_series, self.series_path) |
|
93 | 104 | |
|
94 | 105 | def readheaders(self, patch): |
@@ -209,12 +220,10 b' class queue:' | |||
|
209 | 220 | return p1 |
|
210 | 221 | if len(self.applied) == 0: |
|
211 | 222 | return None |
|
212 | (top, patch) = self.applied[-1].split(':') | |
|
213 | top = revlog.bin(top) | |
|
214 | return top | |
|
223 | return revlog.bin(self.applied[-1].rev) | |
|
215 | 224 | pp = repo.changelog.parents(rev) |
|
216 | 225 | if pp[1] != revlog.nullid: |
|
217 |
arevs = [ x. |
|
|
226 | arevs = [ x.rev for x in self.applied ] | |
|
218 | 227 | p0 = revlog.hex(pp[0]) |
|
219 | 228 | p1 = revlog.hex(pp[1]) |
|
220 | 229 | if p0 in arevs: |
@@ -234,7 +243,7 b' class queue:' | |||
|
234 | 243 | pname = ".hg.patches.merge.marker" |
|
235 | 244 | n = repo.commit(None, '[mq]: merge marker', user=None, force=1, |
|
236 | 245 | wlock=wlock) |
|
237 |
self.applied.append(revlog.hex(n) |
|
|
246 | self.applied.append(StatusEntry(revlog.hex(n), pname)) | |
|
238 | 247 | self.applied_dirty = 1 |
|
239 | 248 | |
|
240 | 249 | head = self.qparents(repo) |
@@ -252,7 +261,7 b' class queue:' | |||
|
252 | 261 | rev = revlog.bin(info[1]) |
|
253 | 262 | (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock) |
|
254 | 263 | if head: |
|
255 |
self.applied.append(revlog.hex(head) |
|
|
264 | self.applied.append(StatusEntry(revlog.hex(head), patch)) | |
|
256 | 265 | self.applied_dirty = 1 |
|
257 | 266 | if err: |
|
258 | 267 | return (err, head) |
@@ -263,8 +272,8 b' class queue:' | |||
|
263 | 272 | patchfile: file name of patch''' |
|
264 | 273 | try: |
|
265 | 274 | pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') |
|
266 |
f = os.popen("%s -d |
|
|
267 | (pp, repo.root, patchfile)) | |
|
275 | f = os.popen("%s -d %s -p1 --no-backup-if-mismatch < %s" % | |
|
276 | (pp, util.shellquote(repo.root), util.shellquote(patchfile))) | |
|
268 | 277 | except: |
|
269 | 278 | self.ui.warn("patch failed, unable to continue (try -v)\n") |
|
270 | 279 | return (None, [], False) |
@@ -275,11 +284,7 b' class queue:' | |||
|
275 | 284 | if self.ui.verbose: |
|
276 | 285 | self.ui.warn(l + "\n") |
|
277 | 286 | if l[:14] == 'patching file ': |
|
278 |
pf = os.path.normpath(l |
|
|
279 | # when patch finds a space in the file name, it puts | |
|
280 | # single quotes around the filename. strip them off | |
|
281 | if pf[0] == "'" and pf[-1] == "'": | |
|
282 | pf = pf[1:-1] | |
|
287 | pf = os.path.normpath(util.parse_patch_output(l)) | |
|
283 | 288 | if pf not in files: |
|
284 | 289 | files.append(pf) |
|
285 | 290 | printed_file = False |
@@ -351,7 +356,7 b' class queue:' | |||
|
351 | 356 | raise util.Abort(_("repo commit failed")) |
|
352 | 357 | |
|
353 | 358 | if update_status: |
|
354 |
self.applied.append(revlog.hex(n) |
|
|
359 | self.applied.append(StatusEntry(revlog.hex(n), patch)) | |
|
355 | 360 | |
|
356 | 361 | if patcherr: |
|
357 | 362 | if not patchfound: |
@@ -389,8 +394,7 b' class queue:' | |||
|
389 | 394 | |
|
390 | 395 | def check_toppatch(self, repo): |
|
391 | 396 | if len(self.applied) > 0: |
|
392 |
|
|
|
393 | top = revlog.bin(top) | |
|
397 | top = revlog.bin(self.applied[-1].rev) | |
|
394 | 398 | pp = repo.dirstate.parents() |
|
395 | 399 | if top not in pp: |
|
396 | 400 | raise util.Abort(_("queue top not at same revision as working directory")) |
@@ -421,7 +425,7 b' class queue:' | |||
|
421 | 425 | if n == None: |
|
422 | 426 | raise util.Abort(_("repo commit failed")) |
|
423 | 427 | self.full_series[insert:insert] = [patch] |
|
424 |
self.applied.append(revlog.hex(n) |
|
|
428 | self.applied.append(StatusEntry(revlog.hex(n), patch)) | |
|
425 | 429 | self.parse_series() |
|
426 | 430 | self.series_dirty = 1 |
|
427 | 431 | self.applied_dirty = 1 |
@@ -501,9 +505,9 b' class queue:' | |||
|
501 | 505 | # we go in two steps here so the strip loop happens in a |
|
502 | 506 | # sensible order. When stripping many files, this helps keep |
|
503 | 507 | # our disk access patterns under control. |
|
504 | list = seen.keys() | |
|
505 | list.sort() | |
|
506 | for f in list: | |
|
508 | seen_list = seen.keys() | |
|
509 | seen_list.sort() | |
|
510 | for f in seen_list: | |
|
507 | 511 | ff = repo.file(f) |
|
508 | 512 | filerev = seen[f] |
|
509 | 513 | if filerev != 0: |
@@ -535,7 +539,6 b' class queue:' | |||
|
535 | 539 | saveheads = [] |
|
536 | 540 | savebases = {} |
|
537 | 541 | |
|
538 | tip = chlog.tip() | |
|
539 | 542 | heads = limitheads(chlog, rev) |
|
540 | 543 | seen = {} |
|
541 | 544 | |
@@ -566,7 +569,7 b' class queue:' | |||
|
566 | 569 | savebases[x] = 1 |
|
567 | 570 | |
|
568 | 571 | # create a changegroup for all the branches we need to keep |
|
569 |
if backup |
|
|
572 | if backup == "all": | |
|
570 | 573 | backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip') |
|
571 | 574 | bundle(backupch) |
|
572 | 575 | if saveheads: |
@@ -581,16 +584,15 b' class queue:' | |||
|
581 | 584 | if saveheads: |
|
582 | 585 | self.ui.status("adding branch\n") |
|
583 | 586 | commands.unbundle(self.ui, repo, chgrpfile, update=False) |
|
584 |
if backup |
|
|
587 | if backup != "strip": | |
|
585 | 588 | os.unlink(chgrpfile) |
|
586 | 589 | |
|
587 | 590 | def isapplied(self, patch): |
|
588 | 591 | """returns (index, rev, patch)""" |
|
589 | 592 | for i in xrange(len(self.applied)): |
|
590 |
|
|
|
591 |
a = p |
|
|
592 | if a[1] == patch: | |
|
593 | return (i, a[0], a[1]) | |
|
593 | a = self.applied[i] | |
|
594 | if a.name == patch: | |
|
595 | return (i, a.rev, a.name) | |
|
594 | 596 | return None |
|
595 | 597 | |
|
596 | 598 | # if the exact patch name does not exist, we try a few |
@@ -693,7 +695,7 b' class queue:' | |||
|
693 | 695 | ret = self.mergepatch(repo, mergeq, s, wlock) |
|
694 | 696 | else: |
|
695 | 697 | ret = self.apply(repo, s, list, wlock=wlock) |
|
696 |
top = self.applied[-1]. |
|
|
698 | top = self.applied[-1].name | |
|
697 | 699 | if ret[0]: |
|
698 | 700 | self.ui.write("Errors during apply, please fix and refresh %s\n" % |
|
699 | 701 | top) |
@@ -730,7 +732,7 b' class queue:' | |||
|
730 | 732 | |
|
731 | 733 | if not update: |
|
732 | 734 | parents = repo.dirstate.parents() |
|
733 |
rr = [ revlog.bin(x. |
|
|
735 | rr = [ revlog.bin(x.rev) for x in self.applied ] | |
|
734 | 736 | for p in parents: |
|
735 | 737 | if p in rr: |
|
736 | 738 | self.ui.warn("qpop: forcing dirstate update\n") |
@@ -751,7 +753,7 b' class queue:' | |||
|
751 | 753 | if popi >= end: |
|
752 | 754 | self.ui.warn("qpop: %s is already at the top\n" % patch) |
|
753 | 755 | return |
|
754 |
info = [ popi ] + self.applied[popi]. |
|
|
756 | info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name] | |
|
755 | 757 | |
|
756 | 758 | start = info[0] |
|
757 | 759 | rev = revlog.bin(info[1]) |
@@ -784,7 +786,7 b' class queue:' | |||
|
784 | 786 | self.strip(repo, rev, update=False, backup='strip', wlock=wlock) |
|
785 | 787 | del self.applied[start:end] |
|
786 | 788 | if len(self.applied): |
|
787 |
self.ui.write("Now at: %s\n" % self.applied[-1]. |
|
|
789 | self.ui.write("Now at: %s\n" % self.applied[-1].name) | |
|
788 | 790 | else: |
|
789 | 791 | self.ui.write("Patch queue now empty\n") |
|
790 | 792 | |
@@ -802,8 +804,7 b' class queue:' | |||
|
802 | 804 | return |
|
803 | 805 | wlock = repo.wlock() |
|
804 | 806 | self.check_toppatch(repo) |
|
805 | qp = self.qparents(repo) | |
|
806 | (top, patch) = self.applied[-1].split(':') | |
|
807 | (top, patch) = (self.applied[-1].rev, self.applied[-1].name) | |
|
807 | 808 | top = revlog.bin(top) |
|
808 | 809 | cparents = repo.changelog.parents(top) |
|
809 | 810 | patchparent = self.qparents(repo, top) |
@@ -899,7 +900,7 b' class queue:' | |||
|
899 | 900 | |
|
900 | 901 | self.strip(repo, top, update=False, backup='strip', wlock=wlock) |
|
901 | 902 | n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock) |
|
902 |
self.applied[-1] = revlog.hex(n) |
|
|
903 | self.applied[-1] = StatusEntry(revlog.hex(n), patch) | |
|
903 | 904 | self.applied_dirty = 1 |
|
904 | 905 | else: |
|
905 | 906 | commands.dodiff(patchf, self.ui, repo, patchparent, None) |
@@ -921,10 +922,7 b' class queue:' | |||
|
921 | 922 | start = self.series_end() |
|
922 | 923 | else: |
|
923 | 924 | start = self.series.index(patch) + 1 |
|
924 | for p in self.series[start:]: | |
|
925 | if self.ui.verbose: | |
|
926 | self.ui.write("%d " % self.series.index(p)) | |
|
927 | self.ui.write("%s\n" % p) | |
|
925 | return [(i, self.series[i]) for i in xrange(start, len(self.series))] | |
|
928 | 926 | |
|
929 | 927 | def qseries(self, repo, missing=None, summary=False): |
|
930 | 928 | start = self.series_end() |
@@ -944,7 +942,7 b' class queue:' | |||
|
944 | 942 | msg = '' |
|
945 | 943 | self.ui.write('%s%s\n' % (patch, msg)) |
|
946 | 944 | else: |
|
947 | list = [] | |
|
945 | msng_list = [] | |
|
948 | 946 | for root, dirs, files in os.walk(self.path): |
|
949 | 947 | d = root[len(self.path) + 1:] |
|
950 | 948 | for f in files: |
@@ -952,13 +950,12 b' class queue:' | |||
|
952 | 950 | if (fl not in self.series and |
|
953 | 951 | fl not in (self.status_path, self.series_path) |
|
954 | 952 | and not fl.startswith('.')): |
|
955 | list.append(fl) | |
|
956 | list.sort() | |
|
957 |
|
|
|
958 |
f |
|
|
959 |
|
|
|
960 |
|
|
|
961 | self.ui.write("%s\n" % x) | |
|
953 | msng_list.append(fl) | |
|
954 | msng_list.sort() | |
|
955 | for x in msng_list: | |
|
956 | if self.ui.verbose: | |
|
957 | self.ui.write("D ") | |
|
958 | self.ui.write("%s\n" % x) | |
|
962 | 959 | |
|
963 | 960 | def issaveline(self, l): |
|
964 | 961 | name = l.split(':')[1] |
@@ -987,12 +984,11 b' class queue:' | |||
|
987 | 984 | qpp = [ hg.bin(x) for x in l ] |
|
988 | 985 | elif datastart != None: |
|
989 | 986 | l = lines[i].rstrip() |
|
990 |
|
|
|
991 |
|
|
|
992 |
|
|
|
993 |
|
|
|
994 |
|
|
|
995 | series.append(file) | |
|
987 | se = StatusEntry(l) | |
|
988 | file_ = se.name | |
|
989 | if se.rev: | |
|
990 | applied.append(se) | |
|
991 | series.append(file_) | |
|
996 | 992 | if datastart == None: |
|
997 | 993 | self.ui.warn("No saved patch data found\n") |
|
998 | 994 | return 1 |
@@ -1043,18 +1039,18 b' class queue:' | |||
|
1043 | 1039 | pp = r.dirstate.parents() |
|
1044 | 1040 | msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1])) |
|
1045 | 1041 | msg += "\n\nPatch Data:\n" |
|
1046 | text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar) | |
|
1042 | text = msg + "\n".join(str(self.applied)) + '\n' + (ar and "\n".join(ar) | |
|
1047 | 1043 | + '\n' or "") |
|
1048 | 1044 | n = repo.commit(None, text, user=None, force=1) |
|
1049 | 1045 | if not n: |
|
1050 | 1046 | self.ui.warn("repo commit failed\n") |
|
1051 | 1047 | return 1 |
|
1052 |
self.applied.append(revlog.hex(n) |
|
|
1048 | self.applied.append(StatusEntry(revlog.hex(n),'.hg.patches.save.line')) | |
|
1053 | 1049 | self.applied_dirty = 1 |
|
1054 | 1050 | |
|
1055 | 1051 | def full_series_end(self): |
|
1056 | 1052 | if len(self.applied) > 0: |
|
1057 |
|
|
|
1053 | p = self.applied[-1].name | |
|
1058 | 1054 | end = self.find_series(p) |
|
1059 | 1055 | if end == None: |
|
1060 | 1056 | return len(self.full_series) |
@@ -1064,7 +1060,7 b' class queue:' | |||
|
1064 | 1060 | def series_end(self): |
|
1065 | 1061 | end = 0 |
|
1066 | 1062 | if len(self.applied) > 0: |
|
1067 |
|
|
|
1063 | p = self.applied[-1].name | |
|
1068 | 1064 | try: |
|
1069 | 1065 | end = self.series.index(p) |
|
1070 | 1066 | except ValueError: |
@@ -1084,8 +1080,7 b' class queue:' | |||
|
1084 | 1080 | self.ui.write("%s\n" % p) |
|
1085 | 1081 | |
|
1086 | 1082 | def appliedname(self, index): |
|
1087 | p = self.applied[index] | |
|
1088 | pname = p.split(':')[1] | |
|
1083 | pname = self.applied[index].name | |
|
1089 | 1084 | if not self.ui.verbose: |
|
1090 | 1085 | p = pname |
|
1091 | 1086 | else: |
@@ -1173,8 +1168,10 b' def applied(ui, repo, patch=None, **opts' | |||
|
1173 | 1168 | |
|
1174 | 1169 | def unapplied(ui, repo, patch=None, **opts): |
|
1175 | 1170 | """print the patches not yet applied""" |
|
1176 | repo.mq.unapplied(repo, patch) | |
|
1177 | return 0 | |
|
1171 | for i, p in repo.mq.unapplied(repo, patch): | |
|
1172 | if ui.verbose: | |
|
1173 | ui.write("%d " % i) | |
|
1174 | ui.write("%s\n" % p) | |
|
1178 | 1175 | |
|
1179 | 1176 | def qimport(ui, repo, *filename, **opts): |
|
1180 | 1177 | """import a patch""" |
@@ -1223,7 +1220,7 b' def clone(ui, source, dest=None, **opts)' | |||
|
1223 | 1220 | if sr.local(): |
|
1224 | 1221 | reposetup(ui, sr) |
|
1225 | 1222 | if sr.mq.applied: |
|
1226 |
qbase = revlog.bin(sr.mq.applied[0]. |
|
|
1223 | qbase = revlog.bin(sr.mq.applied[0].rev) | |
|
1227 | 1224 | if not hg.islocal(dest): |
|
1228 | 1225 | destrev = sr.parents(qbase)[0] |
|
1229 | 1226 | ui.note(_('cloning main repo\n')) |
@@ -1286,7 +1283,7 b' def new(ui, repo, patch, **opts):' | |||
|
1286 | 1283 | If neither is specified, the patch header is empty and the |
|
1287 | 1284 | commit message is 'New patch: PATCH'""" |
|
1288 | 1285 | q = repo.mq |
|
1289 | message=commands.logmessage(**opts) | |
|
1286 | message = commands.logmessage(**opts) | |
|
1290 | 1287 | q.new(repo, patch, msg=message, force=opts['force']) |
|
1291 | 1288 | q.save_dirty() |
|
1292 | 1289 | return 0 |
@@ -1294,11 +1291,11 b' def new(ui, repo, patch, **opts):' | |||
|
1294 | 1291 | def refresh(ui, repo, **opts): |
|
1295 | 1292 | """update the current patch""" |
|
1296 | 1293 | q = repo.mq |
|
1297 | message=commands.logmessage(**opts) | |
|
1294 | message = commands.logmessage(**opts) | |
|
1298 | 1295 | if opts['edit']: |
|
1299 | 1296 | if message: |
|
1300 | 1297 | raise util.Abort(_('option "-e" incompatible with "-m" or "-l"')) |
|
1301 |
patch = q.applied[-1]. |
|
|
1298 | patch = q.applied[-1].name | |
|
1302 | 1299 | (message, comment, user, date, hasdiff) = q.readheaders(patch) |
|
1303 | 1300 | message = ui.edit('\n'.join(message), user or ui.username()) |
|
1304 | 1301 | q.refresh(repo, msg=message, short=opts['short']) |
@@ -1331,7 +1328,7 b' def fold(ui, repo, *files, **opts):' | |||
|
1331 | 1328 | if not q.check_toppatch(repo): |
|
1332 | 1329 | raise util.Abort(_('No patches applied\n')) |
|
1333 | 1330 | |
|
1334 | message=commands.logmessage(**opts) | |
|
1331 | message = commands.logmessage(**opts) | |
|
1335 | 1332 | if opts['edit']: |
|
1336 | 1333 | if message: |
|
1337 | 1334 | raise util.Abort(_('option "-e" incompatible with "-m" or "-l"')) |
@@ -1342,7 +1339,7 b' def fold(ui, repo, *files, **opts):' | |||
|
1342 | 1339 | for f in files: |
|
1343 | 1340 | patch = q.lookup(f) |
|
1344 | 1341 | if patch in patches or patch == parent: |
|
1345 |
|
|
|
1342 | ui.warn(_('Skipping already folded patch %s') % patch) | |
|
1346 | 1343 | if q.isapplied(patch): |
|
1347 | 1344 | raise util.Abort(_('qfold cannot fold already applied patch %s') % patch) |
|
1348 | 1345 | patches.append(patch) |
@@ -1388,20 +1385,20 b' def header(ui, repo, patch=None):' | |||
|
1388 | 1385 | ui.write('\n'.join(message) + '\n') |
|
1389 | 1386 | |
|
1390 | 1387 | def lastsavename(path): |
|
1391 | (dir, base) = os.path.split(path) | |
|
1392 | names = os.listdir(dir) | |
|
1388 | (directory, base) = os.path.split(path) | |
|
1389 | names = os.listdir(directory) | |
|
1393 | 1390 | namere = re.compile("%s.([0-9]+)" % base) |
|
1394 | max = None | |
|
1391 | maxindex = None | |
|
1395 | 1392 | maxname = None |
|
1396 | 1393 | for f in names: |
|
1397 | 1394 | m = namere.match(f) |
|
1398 | 1395 | if m: |
|
1399 | 1396 | index = int(m.group(1)) |
|
1400 | if max == None or index > max: | |
|
1401 | max = index | |
|
1397 | if maxindex == None or index > maxindex: | |
|
1398 | maxindex = index | |
|
1402 | 1399 | maxname = f |
|
1403 | 1400 | if maxname: |
|
1404 | return (os.path.join(dir, maxname), max) | |
|
1401 | return (os.path.join(directory, maxname), maxindex) | |
|
1405 | 1402 | return (None, None) |
|
1406 | 1403 | |
|
1407 | 1404 | def savename(path): |
@@ -1482,7 +1479,7 b' def rename(ui, repo, patch, name=None, *' | |||
|
1482 | 1479 | |
|
1483 | 1480 | info = q.isapplied(patch) |
|
1484 | 1481 | if info: |
|
1485 |
q.applied[info[0]] = info[1] |
|
|
1482 | q.applied[info[0]] = StatusEntry(info[1], name) | |
|
1486 | 1483 | q.applied_dirty = 1 |
|
1487 | 1484 | |
|
1488 | 1485 | util.rename(os.path.join(q.path, patch), absdest) |
@@ -1508,7 +1505,7 b' def restore(ui, repo, rev, **opts):' | |||
|
1508 | 1505 | def save(ui, repo, **opts): |
|
1509 | 1506 | """save current queue state""" |
|
1510 | 1507 | q = repo.mq |
|
1511 | message=commands.logmessage(**opts) | |
|
1508 | message = commands.logmessage(**opts) | |
|
1512 | 1509 | ret = q.save(repo, msg=message) |
|
1513 | 1510 | if ret: |
|
1514 | 1511 | return ret |
@@ -1563,7 +1560,7 b' def reposetup(ui, repo):' | |||
|
1563 | 1560 | if not q.applied: |
|
1564 | 1561 | return tagscache |
|
1565 | 1562 | |
|
1566 |
mqtags = [patch. |
|
|
1563 | mqtags = [(patch.rev, patch.name) for patch in q.applied] | |
|
1567 | 1564 | mqtags.append((mqtags[-1][0], 'qtip')) |
|
1568 | 1565 | mqtags.append((mqtags[0][0], 'qbase')) |
|
1569 | 1566 | for patch in mqtags: |
@@ -255,7 +255,7 b' def hook(ui, repo, hooktype, node=None, ' | |||
|
255 | 255 | changegroup. else send one email per changeset.''' |
|
256 | 256 | n = notifier(ui, repo, hooktype) |
|
257 | 257 | if not n.subs: |
|
258 |
ui.debug(_('notify: no subscribers to |
|
|
258 | ui.debug(_('notify: no subscribers to repo %s\n' % n.root)) | |
|
259 | 259 | return |
|
260 | 260 | if n.skipsource(source): |
|
261 | 261 | ui.debug(_('notify: changes have source "%s" - skipping\n') % |
@@ -288,7 +288,8 b' def patchbomb(ui, repo, *revs, **opts):' | |||
|
288 | 288 | fp.close() |
|
289 | 289 | else: |
|
290 | 290 | ui.status('Sending ', m['Subject'], ' ...\n') |
|
291 | m.__delitem__('bcc') | |
|
291 | # Exim does not remove the Bcc field | |
|
292 | del m['Bcc'] | |
|
292 | 293 | mail.sendmail(sender, to + bcc + cc, m.as_string(0)) |
|
293 | 294 | |
|
294 | 295 | cmdtable = { |
@@ -40,7 +40,7 b' def relpath(repo, args):' | |||
|
40 | 40 | return [util.normpath(os.path.join(cwd, x)) for x in args] |
|
41 | 41 | return args |
|
42 | 42 | |
|
43 |
def logmessage( |
|
|
43 | def logmessage(opts): | |
|
44 | 44 | """ get the log message according to -m and -l option """ |
|
45 | 45 | message = opts['message'] |
|
46 | 46 | logfile = opts['logfile'] |
@@ -125,12 +125,22 b' def walkchangerevs(ui, repo, pats, opts)' | |||
|
125 | 125 | |
|
126 | 126 | |
|
127 | 127 | files, matchfn, anypats = matchpats(repo, pats, opts) |
|
128 | follow = opts.get('follow') | |
|
128 | follow = opts.get('follow') or opts.get('follow_first') | |
|
129 | 129 | |
|
130 | 130 | if repo.changelog.count() == 0: |
|
131 | 131 | return [], False, matchfn |
|
132 | 132 | |
|
133 | revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0'])) | |
|
133 | if follow: | |
|
134 | p = repo.dirstate.parents()[0] | |
|
135 | if p == nullid: | |
|
136 | ui.warn(_('No working directory revision; defaulting to tip\n')) | |
|
137 | start = 'tip' | |
|
138 | else: | |
|
139 | start = repo.changelog.rev(p) | |
|
140 | defrange = '%s:0' % start | |
|
141 | else: | |
|
142 | defrange = 'tip:0' | |
|
143 | revs = map(int, revrange(ui, repo, opts['rev'] or [defrange])) | |
|
134 | 144 | wanted = {} |
|
135 | 145 | slowpath = anypats |
|
136 | 146 | fncache = {} |
@@ -206,10 +216,55 b' def walkchangerevs(ui, repo, pats, opts)' | |||
|
206 | 216 | wanted[rev] = 1 |
|
207 | 217 | |
|
208 | 218 | def iterate(): |
|
219 | class followfilter: | |
|
220 | def __init__(self, onlyfirst=False): | |
|
221 | self.startrev = -1 | |
|
222 | self.roots = [] | |
|
223 | self.onlyfirst = onlyfirst | |
|
224 | ||
|
225 | def match(self, rev): | |
|
226 | def realparents(rev): | |
|
227 | if self.onlyfirst: | |
|
228 | return repo.changelog.parentrevs(rev)[0:1] | |
|
229 | else: | |
|
230 | return filter(lambda x: x != -1, repo.changelog.parentrevs(rev)) | |
|
231 | ||
|
232 | if self.startrev == -1: | |
|
233 | self.startrev = rev | |
|
234 | return True | |
|
235 | ||
|
236 | if rev > self.startrev: | |
|
237 | # forward: all descendants | |
|
238 | if not self.roots: | |
|
239 | self.roots.append(self.startrev) | |
|
240 | for parent in realparents(rev): | |
|
241 | if parent in self.roots: | |
|
242 | self.roots.append(rev) | |
|
243 | return True | |
|
244 | else: | |
|
245 | # backwards: all parents | |
|
246 | if not self.roots: | |
|
247 | self.roots.extend(realparents(self.startrev)) | |
|
248 | if rev in self.roots: | |
|
249 | self.roots.remove(rev) | |
|
250 | self.roots.extend(realparents(rev)) | |
|
251 | return True | |
|
252 | ||
|
253 | return False | |
|
254 | ||
|
255 | if follow and not files: | |
|
256 | ff = followfilter(onlyfirst=opts.get('follow_first')) | |
|
257 | def want(rev): | |
|
258 | if rev not in wanted: | |
|
259 | return False | |
|
260 | return ff.match(rev) | |
|
261 | else: | |
|
262 | def want(rev): | |
|
263 | return rev in wanted | |
|
264 | ||
|
209 | 265 | for i, window in increasing_windows(0, len(revs)): |
|
210 | 266 | yield 'window', revs[0] < revs[-1], revs[-1] |
|
211 | nrevs = [rev for rev in revs[i:i+window] | |
|
212 | if rev in wanted] | |
|
267 | nrevs = [rev for rev in revs[i:i+window] if want(rev)] | |
|
213 | 268 | srevs = list(nrevs) |
|
214 | 269 | srevs.sort() |
|
215 | 270 | for rev in srevs: |
@@ -1041,7 +1096,7 b' def commit(ui, repo, *pats, **opts):' | |||
|
1041 | 1096 | If no commit message is specified, the editor configured in your hgrc |
|
1042 | 1097 | or in the EDITOR environment variable is started to enter a message. |
|
1043 | 1098 | """ |
|
1044 |
message = logmessage( |
|
|
1099 | message = logmessage(opts) | |
|
1045 | 1100 | |
|
1046 | 1101 | if opts['addremove']: |
|
1047 | 1102 | addremove_lock(ui, repo, pats, opts) |
@@ -1972,8 +2027,14 b' def log(ui, repo, *pats, **opts):' | |||
|
1972 | 2027 | project. |
|
1973 | 2028 | |
|
1974 | 2029 | File history is shown without following rename or copy history of |
|
1975 |
files. Use -f/--follow to follow history across |
|
|
1976 | copies. | |
|
2030 | files. Use -f/--follow with a file name to follow history across | |
|
2031 | renames and copies. --follow without a file name will only show | |
|
2032 | ancestors or descendants of the starting revision. --follow-first | |
|
2033 | only follows the first parent of merge revisions. | |
|
2034 | ||
|
2035 | If no revision range is specified, the default is tip:0 unless | |
|
2036 | --follow is set, in which case the working directory parent is | |
|
2037 | used as the starting revision. | |
|
1977 | 2038 | |
|
1978 | 2039 | By default this command outputs: changeset id and hash, tags, |
|
1979 | 2040 | non-trivial parents, user, date and time, and a summary for each |
@@ -2728,8 +2789,8 b' def tag(ui, repo, name, rev_=None, **opt' | |||
|
2728 | 2789 | necessary. The file '.hg/localtags' is used for local tags (not |
|
2729 | 2790 | shared among repositories). |
|
2730 | 2791 | """ |
|
2731 |
if name |
|
|
2732 |
raise util.Abort(_("the name ' |
|
|
2792 | if name in ['tip', '.']: | |
|
2793 | raise util.Abort(_("the name '%s' is reserved") % name) | |
|
2733 | 2794 | if rev_ is not None: |
|
2734 | 2795 | ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, " |
|
2735 | 2796 | "please use 'hg tag [-r REV] NAME' instead\n")) |
@@ -3087,7 +3148,9 b' table = {' | |||
|
3087 | 3148 | (log, |
|
3088 | 3149 | [('b', 'branches', None, _('show branches')), |
|
3089 | 3150 | ('f', 'follow', None, |
|
3090 | _('follow file history across copies and renames')), | |
|
3151 | _('follow changeset history, or file history across copies and renames')), | |
|
3152 | ('', 'follow-first', None, | |
|
3153 | _('only follow the first parent of merge changesets')), | |
|
3091 | 3154 | ('k', 'keyword', [], _('search for a keyword')), |
|
3092 | 3155 | ('l', 'limit', '', _('limit number of changes displayed')), |
|
3093 | 3156 | ('r', 'rev', [], _('show the specified revision or range')), |
@@ -292,6 +292,10 b' class localrepository(repo.repository):' | |||
|
292 | 292 | try: |
|
293 | 293 | return self.tags()[key] |
|
294 | 294 | except KeyError: |
|
295 | if key == '.': | |
|
296 | key = self.dirstate.parents()[0] | |
|
297 | if key == nullid: | |
|
298 | raise repo.RepoError(_("no revision checked out")) | |
|
295 | 299 | try: |
|
296 | 300 | return self.changelog.lookup(key) |
|
297 | 301 | except: |
@@ -1693,6 +1697,7 b' class localrepository(repo.repository):' | |||
|
1693 | 1697 | |
|
1694 | 1698 | return newheads - oldheads + 1 |
|
1695 | 1699 | |
|
1700 | ||
|
1696 | 1701 | def stream_in(self, remote): |
|
1697 | 1702 | fp = remote.stream_out() |
|
1698 | 1703 | resp = int(fp.readline()) |
@@ -48,7 +48,8 b' def merge3(repo, fn, my, other, p1, p2):' | |||
|
48 | 48 | return r |
|
49 | 49 | |
|
50 | 50 | def update(repo, node, allow=False, force=False, choose=None, |
|
51 |
moddirstate=True, forcemerge=False, wlock=None, show_stats=True |
|
|
51 | moddirstate=True, forcemerge=False, wlock=None, show_stats=True, | |
|
52 | remind=True): | |
|
52 | 53 | pl = repo.dirstate.parents() |
|
53 | 54 | if not force and pl[1] != nullid: |
|
54 | 55 | raise util.Abort(_("outstanding uncommitted merges")) |
@@ -337,7 +338,7 b' def update(repo, node, allow=False, forc' | |||
|
337 | 338 | " hg merge %s\n" |
|
338 | 339 | % (repo.changelog.rev(p1), |
|
339 | 340 | repo.changelog.rev(p2)))) |
|
340 |
el |
|
|
341 | elif remind: | |
|
341 | 342 | repo.ui.status(_("(branch merge, don't forget to commit)\n")) |
|
342 | 343 | elif failedmerge: |
|
343 | 344 | repo.ui.status(_("There are unresolved merges with" |
@@ -99,9 +99,9 b' def patch(strip, patchname, ui, cwd=None' | |||
|
99 | 99 | patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') |
|
100 | 100 | args = [] |
|
101 | 101 | if cwd: |
|
102 |
args.append('-d |
|
|
103 |
fp = os.popen('%s %s -p%d < |
|
|
104 |
|
|
|
102 | args.append('-d %s' % shellquote(cwd)) | |
|
103 | fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, | |
|
104 | shellquote(patchname))) | |
|
105 | 105 | files = {} |
|
106 | 106 | for line in fp: |
|
107 | 107 | line = line.rstrip() |
@@ -611,6 +611,9 b" if os.name == 'nt':" | |||
|
611 | 611 | def samestat(s1, s2): |
|
612 | 612 | return False |
|
613 | 613 | |
|
614 | def shellquote(s): | |
|
615 | return '"%s"' % s.replace('"', '\\"') | |
|
616 | ||
|
614 | 617 | def explain_exit(code): |
|
615 | 618 | return _("exited with status %d") % code, code |
|
616 | 619 | |
@@ -700,6 +703,9 b' else:' | |||
|
700 | 703 | else: |
|
701 | 704 | raise |
|
702 | 705 | |
|
706 | def shellquote(s): | |
|
707 | return "'%s'" % s.replace("'", "'\\''") | |
|
708 | ||
|
703 | 709 | def testpid(pid): |
|
704 | 710 | '''return False if pid dead, True if running or not sure''' |
|
705 | 711 | try: |
@@ -28,3 +28,38 b' echo % one rename' | |||
|
28 | 28 | hg log -vf a |
|
29 | 29 | echo % many renames |
|
30 | 30 | hg log -vf e |
|
31 | ||
|
32 | # log --follow tests | |
|
33 | hg init ../follow | |
|
34 | cd ../follow | |
|
35 | echo base > base | |
|
36 | hg ci -Ambase -d '1 0' | |
|
37 | ||
|
38 | echo r1 >> base | |
|
39 | hg ci -Amr1 -d '1 0' | |
|
40 | echo r2 >> base | |
|
41 | hg ci -Amr2 -d '1 0' | |
|
42 | ||
|
43 | hg up -C 1 | |
|
44 | echo b1 > b1 | |
|
45 | hg ci -Amb1 -d '1 0' | |
|
46 | ||
|
47 | echo % log -f | |
|
48 | hg log -f | |
|
49 | ||
|
50 | hg up -C 0 | |
|
51 | echo b2 > b2 | |
|
52 | hg ci -Amb2 -d '1 0' | |
|
53 | ||
|
54 | echo % log -f -r 1:tip | |
|
55 | hg log -f -r 1:tip | |
|
56 | ||
|
57 | hg up -C 3 | |
|
58 | hg merge tip | |
|
59 | hg ci -mm12 -d '1 0' | |
|
60 | ||
|
61 | echo postm >> b1 | |
|
62 | hg ci -Amb1.1 -d'1 0' | |
|
63 | ||
|
64 | echo % log --follow-first | |
|
65 | hg log --follow-first |
@@ -76,3 +76,76 b' description:' | |||
|
76 | 76 | a |
|
77 | 77 | |
|
78 | 78 | |
|
79 | adding base | |
|
80 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
|
81 | adding b1 | |
|
82 | % log -f | |
|
83 | changeset: 3:e62f78d544b4 | |
|
84 | tag: tip | |
|
85 | parent: 1:3d5bf5654eda | |
|
86 | user: test | |
|
87 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
88 | summary: b1 | |
|
89 | ||
|
90 | changeset: 1:3d5bf5654eda | |
|
91 | user: test | |
|
92 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
93 | summary: r1 | |
|
94 | ||
|
95 | changeset: 0:67e992f2c4f3 | |
|
96 | user: test | |
|
97 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
98 | summary: base | |
|
99 | ||
|
100 | 1 files updated, 0 files merged, 1 files removed, 0 files unresolved | |
|
101 | adding b2 | |
|
102 | % log -f -r 1:tip | |
|
103 | changeset: 1:3d5bf5654eda | |
|
104 | user: test | |
|
105 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
106 | summary: r1 | |
|
107 | ||
|
108 | changeset: 2:60c670bf5b30 | |
|
109 | user: test | |
|
110 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
111 | summary: r2 | |
|
112 | ||
|
113 | changeset: 3:e62f78d544b4 | |
|
114 | parent: 1:3d5bf5654eda | |
|
115 | user: test | |
|
116 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
117 | summary: b1 | |
|
118 | ||
|
119 | 2 files updated, 0 files merged, 1 files removed, 0 files unresolved | |
|
120 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
|
121 | (branch merge, don't forget to commit) | |
|
122 | % log --follow-first | |
|
123 | changeset: 6:2404bbcab562 | |
|
124 | tag: tip | |
|
125 | user: test | |
|
126 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
127 | summary: b1.1 | |
|
128 | ||
|
129 | changeset: 5:302e9dd6890d | |
|
130 | parent: 3:e62f78d544b4 | |
|
131 | parent: 4:ddb82e70d1a1 | |
|
132 | user: test | |
|
133 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
134 | summary: m12 | |
|
135 | ||
|
136 | changeset: 3:e62f78d544b4 | |
|
137 | parent: 1:3d5bf5654eda | |
|
138 | user: test | |
|
139 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
140 | summary: b1 | |
|
141 | ||
|
142 | changeset: 1:3d5bf5654eda | |
|
143 | user: test | |
|
144 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
145 | summary: r1 | |
|
146 | ||
|
147 | changeset: 0:67e992f2c4f3 | |
|
148 | user: test | |
|
149 | date: Thu Jan 01 00:00:01 1970 +0000 | |
|
150 | summary: base | |
|
151 |
General Comments 0
You need to be logged in to leave comments.
Login now