##// END OF EJS Templates
Merge with crew
Matt Mackall -
r2803:987c31e2 merge default
parent child Browse files
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 commands.norepo += " qclone qversion"
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 class queue:
52 class queue:
43 def __init__(self, ui, path, patchdir=None):
53 def __init__(self, ui, path, patchdir=None):
44 self.basepath = path
54 self.basepath = path
@@ -60,7 +70,8 b' class queue:'
60 self.parse_series()
70 self.parse_series()
61
71
62 if os.path.exists(os.path.join(self.path, self.status_path)):
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 def find_series(self, patch):
76 def find_series(self, patch):
66 pre = re.compile("(\s*)([^#]+)")
77 pre = re.compile("(\s*)([^#]+)")
@@ -88,7 +99,7 b' class queue:'
88 for i in items:
99 for i in items:
89 print >> fp, i
100 print >> fp, i
90 fp.close()
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 if self.series_dirty: write_list(self.full_series, self.series_path)
103 if self.series_dirty: write_list(self.full_series, self.series_path)
93
104
94 def readheaders(self, patch):
105 def readheaders(self, patch):
@@ -209,12 +220,10 b' class queue:'
209 return p1
220 return p1
210 if len(self.applied) == 0:
221 if len(self.applied) == 0:
211 return None
222 return None
212 (top, patch) = self.applied[-1].split(':')
223 return revlog.bin(self.applied[-1].rev)
213 top = revlog.bin(top)
214 return top
215 pp = repo.changelog.parents(rev)
224 pp = repo.changelog.parents(rev)
216 if pp[1] != revlog.nullid:
225 if pp[1] != revlog.nullid:
217 arevs = [ x.split(':')[0] for x in self.applied ]
226 arevs = [ x.rev for x in self.applied ]
218 p0 = revlog.hex(pp[0])
227 p0 = revlog.hex(pp[0])
219 p1 = revlog.hex(pp[1])
228 p1 = revlog.hex(pp[1])
220 if p0 in arevs:
229 if p0 in arevs:
@@ -234,7 +243,7 b' class queue:'
234 pname = ".hg.patches.merge.marker"
243 pname = ".hg.patches.merge.marker"
235 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
244 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
236 wlock=wlock)
245 wlock=wlock)
237 self.applied.append(revlog.hex(n) + ":" + pname)
246 self.applied.append(StatusEntry(revlog.hex(n), pname))
238 self.applied_dirty = 1
247 self.applied_dirty = 1
239
248
240 head = self.qparents(repo)
249 head = self.qparents(repo)
@@ -252,7 +261,7 b' class queue:'
252 rev = revlog.bin(info[1])
261 rev = revlog.bin(info[1])
253 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
262 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
254 if head:
263 if head:
255 self.applied.append(revlog.hex(head) + ":" + patch)
264 self.applied.append(StatusEntry(revlog.hex(head), patch))
256 self.applied_dirty = 1
265 self.applied_dirty = 1
257 if err:
266 if err:
258 return (err, head)
267 return (err, head)
@@ -263,8 +272,8 b' class queue:'
263 patchfile: file name of patch'''
272 patchfile: file name of patch'''
264 try:
273 try:
265 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
274 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
266 f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
275 f = os.popen("%s -d %s -p1 --no-backup-if-mismatch < %s" %
267 (pp, repo.root, patchfile))
276 (pp, util.shellquote(repo.root), util.shellquote(patchfile)))
268 except:
277 except:
269 self.ui.warn("patch failed, unable to continue (try -v)\n")
278 self.ui.warn("patch failed, unable to continue (try -v)\n")
270 return (None, [], False)
279 return (None, [], False)
@@ -275,11 +284,7 b' class queue:'
275 if self.ui.verbose:
284 if self.ui.verbose:
276 self.ui.warn(l + "\n")
285 self.ui.warn(l + "\n")
277 if l[:14] == 'patching file ':
286 if l[:14] == 'patching file ':
278 pf = os.path.normpath(l[14:])
287 pf = os.path.normpath(util.parse_patch_output(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]
283 if pf not in files:
288 if pf not in files:
284 files.append(pf)
289 files.append(pf)
285 printed_file = False
290 printed_file = False
@@ -351,7 +356,7 b' class queue:'
351 raise util.Abort(_("repo commit failed"))
356 raise util.Abort(_("repo commit failed"))
352
357
353 if update_status:
358 if update_status:
354 self.applied.append(revlog.hex(n) + ":" + patch)
359 self.applied.append(StatusEntry(revlog.hex(n), patch))
355
360
356 if patcherr:
361 if patcherr:
357 if not patchfound:
362 if not patchfound:
@@ -389,8 +394,7 b' class queue:'
389
394
390 def check_toppatch(self, repo):
395 def check_toppatch(self, repo):
391 if len(self.applied) > 0:
396 if len(self.applied) > 0:
392 (top, patch) = self.applied[-1].split(':')
397 top = revlog.bin(self.applied[-1].rev)
393 top = revlog.bin(top)
394 pp = repo.dirstate.parents()
398 pp = repo.dirstate.parents()
395 if top not in pp:
399 if top not in pp:
396 raise util.Abort(_("queue top not at same revision as working directory"))
400 raise util.Abort(_("queue top not at same revision as working directory"))
@@ -421,7 +425,7 b' class queue:'
421 if n == None:
425 if n == None:
422 raise util.Abort(_("repo commit failed"))
426 raise util.Abort(_("repo commit failed"))
423 self.full_series[insert:insert] = [patch]
427 self.full_series[insert:insert] = [patch]
424 self.applied.append(revlog.hex(n) + ":" + patch)
428 self.applied.append(StatusEntry(revlog.hex(n), patch))
425 self.parse_series()
429 self.parse_series()
426 self.series_dirty = 1
430 self.series_dirty = 1
427 self.applied_dirty = 1
431 self.applied_dirty = 1
@@ -501,9 +505,9 b' class queue:'
501 # we go in two steps here so the strip loop happens in a
505 # we go in two steps here so the strip loop happens in a
502 # sensible order. When stripping many files, this helps keep
506 # sensible order. When stripping many files, this helps keep
503 # our disk access patterns under control.
507 # our disk access patterns under control.
504 list = seen.keys()
508 seen_list = seen.keys()
505 list.sort()
509 seen_list.sort()
506 for f in list:
510 for f in seen_list:
507 ff = repo.file(f)
511 ff = repo.file(f)
508 filerev = seen[f]
512 filerev = seen[f]
509 if filerev != 0:
513 if filerev != 0:
@@ -535,7 +539,6 b' class queue:'
535 saveheads = []
539 saveheads = []
536 savebases = {}
540 savebases = {}
537
541
538 tip = chlog.tip()
539 heads = limitheads(chlog, rev)
542 heads = limitheads(chlog, rev)
540 seen = {}
543 seen = {}
541
544
@@ -566,7 +569,7 b' class queue:'
566 savebases[x] = 1
569 savebases[x] = 1
567
570
568 # create a changegroup for all the branches we need to keep
571 # create a changegroup for all the branches we need to keep
569 if backup is "all":
572 if backup == "all":
570 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
573 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
571 bundle(backupch)
574 bundle(backupch)
572 if saveheads:
575 if saveheads:
@@ -581,16 +584,15 b' class queue:'
581 if saveheads:
584 if saveheads:
582 self.ui.status("adding branch\n")
585 self.ui.status("adding branch\n")
583 commands.unbundle(self.ui, repo, chgrpfile, update=False)
586 commands.unbundle(self.ui, repo, chgrpfile, update=False)
584 if backup is not "strip":
587 if backup != "strip":
585 os.unlink(chgrpfile)
588 os.unlink(chgrpfile)
586
589
587 def isapplied(self, patch):
590 def isapplied(self, patch):
588 """returns (index, rev, patch)"""
591 """returns (index, rev, patch)"""
589 for i in xrange(len(self.applied)):
592 for i in xrange(len(self.applied)):
590 p = self.applied[i]
593 a = self.applied[i]
591 a = p.split(':')
594 if a.name == patch:
592 if a[1] == patch:
595 return (i, a.rev, a.name)
593 return (i, a[0], a[1])
594 return None
596 return None
595
597
596 # if the exact patch name does not exist, we try a few
598 # if the exact patch name does not exist, we try a few
@@ -693,7 +695,7 b' class queue:'
693 ret = self.mergepatch(repo, mergeq, s, wlock)
695 ret = self.mergepatch(repo, mergeq, s, wlock)
694 else:
696 else:
695 ret = self.apply(repo, s, list, wlock=wlock)
697 ret = self.apply(repo, s, list, wlock=wlock)
696 top = self.applied[-1].split(':')[1]
698 top = self.applied[-1].name
697 if ret[0]:
699 if ret[0]:
698 self.ui.write("Errors during apply, please fix and refresh %s\n" %
700 self.ui.write("Errors during apply, please fix and refresh %s\n" %
699 top)
701 top)
@@ -730,7 +732,7 b' class queue:'
730
732
731 if not update:
733 if not update:
732 parents = repo.dirstate.parents()
734 parents = repo.dirstate.parents()
733 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
735 rr = [ revlog.bin(x.rev) for x in self.applied ]
734 for p in parents:
736 for p in parents:
735 if p in rr:
737 if p in rr:
736 self.ui.warn("qpop: forcing dirstate update\n")
738 self.ui.warn("qpop: forcing dirstate update\n")
@@ -751,7 +753,7 b' class queue:'
751 if popi >= end:
753 if popi >= end:
752 self.ui.warn("qpop: %s is already at the top\n" % patch)
754 self.ui.warn("qpop: %s is already at the top\n" % patch)
753 return
755 return
754 info = [ popi ] + self.applied[popi].split(':')
756 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
755
757
756 start = info[0]
758 start = info[0]
757 rev = revlog.bin(info[1])
759 rev = revlog.bin(info[1])
@@ -784,7 +786,7 b' class queue:'
784 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
786 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
785 del self.applied[start:end]
787 del self.applied[start:end]
786 if len(self.applied):
788 if len(self.applied):
787 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
789 self.ui.write("Now at: %s\n" % self.applied[-1].name)
788 else:
790 else:
789 self.ui.write("Patch queue now empty\n")
791 self.ui.write("Patch queue now empty\n")
790
792
@@ -802,8 +804,7 b' class queue:'
802 return
804 return
803 wlock = repo.wlock()
805 wlock = repo.wlock()
804 self.check_toppatch(repo)
806 self.check_toppatch(repo)
805 qp = self.qparents(repo)
807 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
806 (top, patch) = self.applied[-1].split(':')
807 top = revlog.bin(top)
808 top = revlog.bin(top)
808 cparents = repo.changelog.parents(top)
809 cparents = repo.changelog.parents(top)
809 patchparent = self.qparents(repo, top)
810 patchparent = self.qparents(repo, top)
@@ -899,7 +900,7 b' class queue:'
899
900
900 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
901 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
901 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
902 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
902 self.applied[-1] = revlog.hex(n) + ':' + patch
903 self.applied[-1] = StatusEntry(revlog.hex(n), patch)
903 self.applied_dirty = 1
904 self.applied_dirty = 1
904 else:
905 else:
905 commands.dodiff(patchf, self.ui, repo, patchparent, None)
906 commands.dodiff(patchf, self.ui, repo, patchparent, None)
@@ -921,10 +922,7 b' class queue:'
921 start = self.series_end()
922 start = self.series_end()
922 else:
923 else:
923 start = self.series.index(patch) + 1
924 start = self.series.index(patch) + 1
924 for p in self.series[start:]:
925 return [(i, self.series[i]) for i in xrange(start, len(self.series))]
925 if self.ui.verbose:
926 self.ui.write("%d " % self.series.index(p))
927 self.ui.write("%s\n" % p)
928
926
929 def qseries(self, repo, missing=None, summary=False):
927 def qseries(self, repo, missing=None, summary=False):
930 start = self.series_end()
928 start = self.series_end()
@@ -944,7 +942,7 b' class queue:'
944 msg = ''
942 msg = ''
945 self.ui.write('%s%s\n' % (patch, msg))
943 self.ui.write('%s%s\n' % (patch, msg))
946 else:
944 else:
947 list = []
945 msng_list = []
948 for root, dirs, files in os.walk(self.path):
946 for root, dirs, files in os.walk(self.path):
949 d = root[len(self.path) + 1:]
947 d = root[len(self.path) + 1:]
950 for f in files:
948 for f in files:
@@ -952,13 +950,12 b' class queue:'
952 if (fl not in self.series and
950 if (fl not in self.series and
953 fl not in (self.status_path, self.series_path)
951 fl not in (self.status_path, self.series_path)
954 and not fl.startswith('.')):
952 and not fl.startswith('.')):
955 list.append(fl)
953 msng_list.append(fl)
956 list.sort()
954 msng_list.sort()
957 if list:
955 for x in msng_list:
958 for x in list:
956 if self.ui.verbose:
959 if self.ui.verbose:
957 self.ui.write("D ")
960 self.ui.write("D ")
958 self.ui.write("%s\n" % x)
961 self.ui.write("%s\n" % x)
962
959
963 def issaveline(self, l):
960 def issaveline(self, l):
964 name = l.split(':')[1]
961 name = l.split(':')[1]
@@ -987,12 +984,11 b' class queue:'
987 qpp = [ hg.bin(x) for x in l ]
984 qpp = [ hg.bin(x) for x in l ]
988 elif datastart != None:
985 elif datastart != None:
989 l = lines[i].rstrip()
986 l = lines[i].rstrip()
990 index = l.index(':')
987 se = StatusEntry(l)
991 id = l[:index]
988 file_ = se.name
992 file = l[index + 1:]
989 if se.rev:
993 if id:
990 applied.append(se)
994 applied.append(l)
991 series.append(file_)
995 series.append(file)
996 if datastart == None:
992 if datastart == None:
997 self.ui.warn("No saved patch data found\n")
993 self.ui.warn("No saved patch data found\n")
998 return 1
994 return 1
@@ -1043,18 +1039,18 b' class queue:'
1043 pp = r.dirstate.parents()
1039 pp = r.dirstate.parents()
1044 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1040 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1045 msg += "\n\nPatch Data:\n"
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 + '\n' or "")
1043 + '\n' or "")
1048 n = repo.commit(None, text, user=None, force=1)
1044 n = repo.commit(None, text, user=None, force=1)
1049 if not n:
1045 if not n:
1050 self.ui.warn("repo commit failed\n")
1046 self.ui.warn("repo commit failed\n")
1051 return 1
1047 return 1
1052 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1048 self.applied.append(StatusEntry(revlog.hex(n),'.hg.patches.save.line'))
1053 self.applied_dirty = 1
1049 self.applied_dirty = 1
1054
1050
1055 def full_series_end(self):
1051 def full_series_end(self):
1056 if len(self.applied) > 0:
1052 if len(self.applied) > 0:
1057 (top, p) = self.applied[-1].split(':')
1053 p = self.applied[-1].name
1058 end = self.find_series(p)
1054 end = self.find_series(p)
1059 if end == None:
1055 if end == None:
1060 return len(self.full_series)
1056 return len(self.full_series)
@@ -1064,7 +1060,7 b' class queue:'
1064 def series_end(self):
1060 def series_end(self):
1065 end = 0
1061 end = 0
1066 if len(self.applied) > 0:
1062 if len(self.applied) > 0:
1067 (top, p) = self.applied[-1].split(':')
1063 p = self.applied[-1].name
1068 try:
1064 try:
1069 end = self.series.index(p)
1065 end = self.series.index(p)
1070 except ValueError:
1066 except ValueError:
@@ -1084,8 +1080,7 b' class queue:'
1084 self.ui.write("%s\n" % p)
1080 self.ui.write("%s\n" % p)
1085
1081
1086 def appliedname(self, index):
1082 def appliedname(self, index):
1087 p = self.applied[index]
1083 pname = self.applied[index].name
1088 pname = p.split(':')[1]
1089 if not self.ui.verbose:
1084 if not self.ui.verbose:
1090 p = pname
1085 p = pname
1091 else:
1086 else:
@@ -1173,8 +1168,10 b' def applied(ui, repo, patch=None, **opts'
1173
1168
1174 def unapplied(ui, repo, patch=None, **opts):
1169 def unapplied(ui, repo, patch=None, **opts):
1175 """print the patches not yet applied"""
1170 """print the patches not yet applied"""
1176 repo.mq.unapplied(repo, patch)
1171 for i, p in repo.mq.unapplied(repo, patch):
1177 return 0
1172 if ui.verbose:
1173 ui.write("%d " % i)
1174 ui.write("%s\n" % p)
1178
1175
1179 def qimport(ui, repo, *filename, **opts):
1176 def qimport(ui, repo, *filename, **opts):
1180 """import a patch"""
1177 """import a patch"""
@@ -1223,7 +1220,7 b' def clone(ui, source, dest=None, **opts)'
1223 if sr.local():
1220 if sr.local():
1224 reposetup(ui, sr)
1221 reposetup(ui, sr)
1225 if sr.mq.applied:
1222 if sr.mq.applied:
1226 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1223 qbase = revlog.bin(sr.mq.applied[0].rev)
1227 if not hg.islocal(dest):
1224 if not hg.islocal(dest):
1228 destrev = sr.parents(qbase)[0]
1225 destrev = sr.parents(qbase)[0]
1229 ui.note(_('cloning main repo\n'))
1226 ui.note(_('cloning main repo\n'))
@@ -1286,7 +1283,7 b' def new(ui, repo, patch, **opts):'
1286 If neither is specified, the patch header is empty and the
1283 If neither is specified, the patch header is empty and the
1287 commit message is 'New patch: PATCH'"""
1284 commit message is 'New patch: PATCH'"""
1288 q = repo.mq
1285 q = repo.mq
1289 message=commands.logmessage(**opts)
1286 message = commands.logmessage(**opts)
1290 q.new(repo, patch, msg=message, force=opts['force'])
1287 q.new(repo, patch, msg=message, force=opts['force'])
1291 q.save_dirty()
1288 q.save_dirty()
1292 return 0
1289 return 0
@@ -1294,11 +1291,11 b' def new(ui, repo, patch, **opts):'
1294 def refresh(ui, repo, **opts):
1291 def refresh(ui, repo, **opts):
1295 """update the current patch"""
1292 """update the current patch"""
1296 q = repo.mq
1293 q = repo.mq
1297 message=commands.logmessage(**opts)
1294 message = commands.logmessage(**opts)
1298 if opts['edit']:
1295 if opts['edit']:
1299 if message:
1296 if message:
1300 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1297 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1301 patch = q.applied[-1].split(':')[1]
1298 patch = q.applied[-1].name
1302 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1299 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1303 message = ui.edit('\n'.join(message), user or ui.username())
1300 message = ui.edit('\n'.join(message), user or ui.username())
1304 q.refresh(repo, msg=message, short=opts['short'])
1301 q.refresh(repo, msg=message, short=opts['short'])
@@ -1331,7 +1328,7 b' def fold(ui, repo, *files, **opts):'
1331 if not q.check_toppatch(repo):
1328 if not q.check_toppatch(repo):
1332 raise util.Abort(_('No patches applied\n'))
1329 raise util.Abort(_('No patches applied\n'))
1333
1330
1334 message=commands.logmessage(**opts)
1331 message = commands.logmessage(**opts)
1335 if opts['edit']:
1332 if opts['edit']:
1336 if message:
1333 if message:
1337 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1334 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
@@ -1342,7 +1339,7 b' def fold(ui, repo, *files, **opts):'
1342 for f in files:
1339 for f in files:
1343 patch = q.lookup(f)
1340 patch = q.lookup(f)
1344 if patch in patches or patch == parent:
1341 if patch in patches or patch == parent:
1345 self.ui.warn(_('Skipping already folded patch %s') % patch)
1342 ui.warn(_('Skipping already folded patch %s') % patch)
1346 if q.isapplied(patch):
1343 if q.isapplied(patch):
1347 raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
1344 raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
1348 patches.append(patch)
1345 patches.append(patch)
@@ -1388,20 +1385,20 b' def header(ui, repo, patch=None):'
1388 ui.write('\n'.join(message) + '\n')
1385 ui.write('\n'.join(message) + '\n')
1389
1386
1390 def lastsavename(path):
1387 def lastsavename(path):
1391 (dir, base) = os.path.split(path)
1388 (directory, base) = os.path.split(path)
1392 names = os.listdir(dir)
1389 names = os.listdir(directory)
1393 namere = re.compile("%s.([0-9]+)" % base)
1390 namere = re.compile("%s.([0-9]+)" % base)
1394 max = None
1391 maxindex = None
1395 maxname = None
1392 maxname = None
1396 for f in names:
1393 for f in names:
1397 m = namere.match(f)
1394 m = namere.match(f)
1398 if m:
1395 if m:
1399 index = int(m.group(1))
1396 index = int(m.group(1))
1400 if max == None or index > max:
1397 if maxindex == None or index > maxindex:
1401 max = index
1398 maxindex = index
1402 maxname = f
1399 maxname = f
1403 if maxname:
1400 if maxname:
1404 return (os.path.join(dir, maxname), max)
1401 return (os.path.join(directory, maxname), maxindex)
1405 return (None, None)
1402 return (None, None)
1406
1403
1407 def savename(path):
1404 def savename(path):
@@ -1482,7 +1479,7 b' def rename(ui, repo, patch, name=None, *'
1482
1479
1483 info = q.isapplied(patch)
1480 info = q.isapplied(patch)
1484 if info:
1481 if info:
1485 q.applied[info[0]] = info[1] + ':' + name
1482 q.applied[info[0]] = StatusEntry(info[1], name)
1486 q.applied_dirty = 1
1483 q.applied_dirty = 1
1487
1484
1488 util.rename(os.path.join(q.path, patch), absdest)
1485 util.rename(os.path.join(q.path, patch), absdest)
@@ -1508,7 +1505,7 b' def restore(ui, repo, rev, **opts):'
1508 def save(ui, repo, **opts):
1505 def save(ui, repo, **opts):
1509 """save current queue state"""
1506 """save current queue state"""
1510 q = repo.mq
1507 q = repo.mq
1511 message=commands.logmessage(**opts)
1508 message = commands.logmessage(**opts)
1512 ret = q.save(repo, msg=message)
1509 ret = q.save(repo, msg=message)
1513 if ret:
1510 if ret:
1514 return ret
1511 return ret
@@ -1563,7 +1560,7 b' def reposetup(ui, repo):'
1563 if not q.applied:
1560 if not q.applied:
1564 return tagscache
1561 return tagscache
1565
1562
1566 mqtags = [patch.split(':') for patch in q.applied]
1563 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1567 mqtags.append((mqtags[-1][0], 'qtip'))
1564 mqtags.append((mqtags[-1][0], 'qtip'))
1568 mqtags.append((mqtags[0][0], 'qbase'))
1565 mqtags.append((mqtags[0][0], 'qbase'))
1569 for patch in mqtags:
1566 for patch in mqtags:
@@ -255,7 +255,7 b' def hook(ui, repo, hooktype, node=None, '
255 changegroup. else send one email per changeset.'''
255 changegroup. else send one email per changeset.'''
256 n = notifier(ui, repo, hooktype)
256 n = notifier(ui, repo, hooktype)
257 if not n.subs:
257 if not n.subs:
258 ui.debug(_('notify: no subscribers to this repo\n'))
258 ui.debug(_('notify: no subscribers to repo %s\n' % n.root))
259 return
259 return
260 if n.skipsource(source):
260 if n.skipsource(source):
261 ui.debug(_('notify: changes have source "%s" - skipping\n') %
261 ui.debug(_('notify: changes have source "%s" - skipping\n') %
@@ -288,7 +288,8 b' def patchbomb(ui, repo, *revs, **opts):'
288 fp.close()
288 fp.close()
289 else:
289 else:
290 ui.status('Sending ', m['Subject'], ' ...\n')
290 ui.status('Sending ', m['Subject'], ' ...\n')
291 m.__delitem__('bcc')
291 # Exim does not remove the Bcc field
292 del m['Bcc']
292 mail.sendmail(sender, to + bcc + cc, m.as_string(0))
293 mail.sendmail(sender, to + bcc + cc, m.as_string(0))
293
294
294 cmdtable = {
295 cmdtable = {
@@ -40,7 +40,7 b' def relpath(repo, args):'
40 return [util.normpath(os.path.join(cwd, x)) for x in args]
40 return [util.normpath(os.path.join(cwd, x)) for x in args]
41 return args
41 return args
42
42
43 def logmessage(**opts):
43 def logmessage(opts):
44 """ get the log message according to -m and -l option """
44 """ get the log message according to -m and -l option """
45 message = opts['message']
45 message = opts['message']
46 logfile = opts['logfile']
46 logfile = opts['logfile']
@@ -125,12 +125,22 b' def walkchangerevs(ui, repo, pats, opts)'
125
125
126
126
127 files, matchfn, anypats = matchpats(repo, pats, opts)
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 if repo.changelog.count() == 0:
130 if repo.changelog.count() == 0:
131 return [], False, matchfn
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 wanted = {}
144 wanted = {}
135 slowpath = anypats
145 slowpath = anypats
136 fncache = {}
146 fncache = {}
@@ -206,10 +216,55 b' def walkchangerevs(ui, repo, pats, opts)'
206 wanted[rev] = 1
216 wanted[rev] = 1
207
217
208 def iterate():
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 for i, window in increasing_windows(0, len(revs)):
265 for i, window in increasing_windows(0, len(revs)):
210 yield 'window', revs[0] < revs[-1], revs[-1]
266 yield 'window', revs[0] < revs[-1], revs[-1]
211 nrevs = [rev for rev in revs[i:i+window]
267 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
212 if rev in wanted]
213 srevs = list(nrevs)
268 srevs = list(nrevs)
214 srevs.sort()
269 srevs.sort()
215 for rev in srevs:
270 for rev in srevs:
@@ -1041,7 +1096,7 b' def commit(ui, repo, *pats, **opts):'
1041 If no commit message is specified, the editor configured in your hgrc
1096 If no commit message is specified, the editor configured in your hgrc
1042 or in the EDITOR environment variable is started to enter a message.
1097 or in the EDITOR environment variable is started to enter a message.
1043 """
1098 """
1044 message = logmessage(**opts)
1099 message = logmessage(opts)
1045
1100
1046 if opts['addremove']:
1101 if opts['addremove']:
1047 addremove_lock(ui, repo, pats, opts)
1102 addremove_lock(ui, repo, pats, opts)
@@ -1972,8 +2027,14 b' def log(ui, repo, *pats, **opts):'
1972 project.
2027 project.
1973
2028
1974 File history is shown without following rename or copy history of
2029 File history is shown without following rename or copy history of
1975 files. Use -f/--follow to follow history across renames and
2030 files. Use -f/--follow with a file name to follow history across
1976 copies.
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 By default this command outputs: changeset id and hash, tags,
2039 By default this command outputs: changeset id and hash, tags,
1979 non-trivial parents, user, date and time, and a summary for each
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 necessary. The file '.hg/localtags' is used for local tags (not
2789 necessary. The file '.hg/localtags' is used for local tags (not
2729 shared among repositories).
2790 shared among repositories).
2730 """
2791 """
2731 if name == "tip":
2792 if name in ['tip', '.']:
2732 raise util.Abort(_("the name 'tip' is reserved"))
2793 raise util.Abort(_("the name '%s' is reserved") % name)
2733 if rev_ is not None:
2794 if rev_ is not None:
2734 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2795 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2735 "please use 'hg tag [-r REV] NAME' instead\n"))
2796 "please use 'hg tag [-r REV] NAME' instead\n"))
@@ -3087,7 +3148,9 b' table = {'
3087 (log,
3148 (log,
3088 [('b', 'branches', None, _('show branches')),
3149 [('b', 'branches', None, _('show branches')),
3089 ('f', 'follow', None,
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 ('k', 'keyword', [], _('search for a keyword')),
3154 ('k', 'keyword', [], _('search for a keyword')),
3092 ('l', 'limit', '', _('limit number of changes displayed')),
3155 ('l', 'limit', '', _('limit number of changes displayed')),
3093 ('r', 'rev', [], _('show the specified revision or range')),
3156 ('r', 'rev', [], _('show the specified revision or range')),
@@ -292,6 +292,10 b' class localrepository(repo.repository):'
292 try:
292 try:
293 return self.tags()[key]
293 return self.tags()[key]
294 except KeyError:
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 try:
299 try:
296 return self.changelog.lookup(key)
300 return self.changelog.lookup(key)
297 except:
301 except:
@@ -1693,6 +1697,7 b' class localrepository(repo.repository):'
1693
1697
1694 return newheads - oldheads + 1
1698 return newheads - oldheads + 1
1695
1699
1700
1696 def stream_in(self, remote):
1701 def stream_in(self, remote):
1697 fp = remote.stream_out()
1702 fp = remote.stream_out()
1698 resp = int(fp.readline())
1703 resp = int(fp.readline())
@@ -48,7 +48,8 b' def merge3(repo, fn, my, other, p1, p2):'
48 return r
48 return r
49
49
50 def update(repo, node, allow=False, force=False, choose=None,
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 pl = repo.dirstate.parents()
53 pl = repo.dirstate.parents()
53 if not force and pl[1] != nullid:
54 if not force and pl[1] != nullid:
54 raise util.Abort(_("outstanding uncommitted merges"))
55 raise util.Abort(_("outstanding uncommitted merges"))
@@ -337,7 +338,7 b' def update(repo, node, allow=False, forc'
337 " hg merge %s\n"
338 " hg merge %s\n"
338 % (repo.changelog.rev(p1),
339 % (repo.changelog.rev(p1),
339 repo.changelog.rev(p2))))
340 repo.changelog.rev(p2))))
340 else:
341 elif remind:
341 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
342 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
342 elif failedmerge:
343 elif failedmerge:
343 repo.ui.status(_("There are unresolved merges with"
344 repo.ui.status(_("There are unresolved merges with"
@@ -99,9 +99,9 b' def patch(strip, patchname, ui, cwd=None'
99 patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
99 patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
100 args = []
100 args = []
101 if cwd:
101 if cwd:
102 args.append('-d "%s"' % cwd)
102 args.append('-d %s' % shellquote(cwd))
103 fp = os.popen('%s %s -p%d < "%s"' % (patcher, ' '.join(args), strip,
103 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
104 patchname))
104 shellquote(patchname)))
105 files = {}
105 files = {}
106 for line in fp:
106 for line in fp:
107 line = line.rstrip()
107 line = line.rstrip()
@@ -611,6 +611,9 b" if os.name == 'nt':"
611 def samestat(s1, s2):
611 def samestat(s1, s2):
612 return False
612 return False
613
613
614 def shellquote(s):
615 return '"%s"' % s.replace('"', '\\"')
616
614 def explain_exit(code):
617 def explain_exit(code):
615 return _("exited with status %d") % code, code
618 return _("exited with status %d") % code, code
616
619
@@ -700,6 +703,9 b' else:'
700 else:
703 else:
701 raise
704 raise
702
705
706 def shellquote(s):
707 return "'%s'" % s.replace("'", "'\\''")
708
703 def testpid(pid):
709 def testpid(pid):
704 '''return False if pid dead, True if running or not sure'''
710 '''return False if pid dead, True if running or not sure'''
705 try:
711 try:
@@ -28,3 +28,38 b' echo % one rename'
28 hg log -vf a
28 hg log -vf a
29 echo % many renames
29 echo % many renames
30 hg log -vf e
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 a
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