--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/fetch.py Mon Aug 07 16:47:06 2006 -0500
@@ -0,0 +1,93 @@
+# fetch.py - pull and merge remote changes
+#
+# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+from mercurial.demandload import *
+from mercurial.i18n import gettext as _
+from mercurial.node import *
+demandload(globals(), 'mercurial:commands,hg,node,util')
+
+def fetch(ui, repo, source='default', **opts):
+ '''Pull changes from a remote repository, merge new changes if needed.
+
+ This finds all changes from the repository at the specified path
+ or URL and adds them to the local repository.
+
+ If the pulled changes add a new head, the head is automatically
+ merged, and the result of the merge is committed. Otherwise, the
+ working directory is updated.'''
+
+ def postincoming(other, modheads):
+ if modheads == 0:
+ return 0
+ if modheads == 1:
+ return commands.doupdate(ui, repo)
+ newheads = repo.heads(parent)
+ newchildren = [n for n in repo.heads(parent) if n != parent]
+ newparent = parent
+ if newchildren:
+ commands.doupdate(ui, repo, node=hex(newchildren[0]))
+ newparent = newchildren[0]
+ newheads = [n for n in repo.heads() if n != newparent]
+ err = False
+ if newheads:
+ ui.status(_('merging with new head %d:%s\n') %
+ (repo.changelog.rev(newheads[0]), short(newheads[0])))
+ err = repo.update(newheads[0], allow=True, remind=False)
+ if not err and len(newheads) > 1:
+ ui.status(_('not merging with %d other new heads '
+ '(use "hg heads" and "hg merge" to merge them)') %
+ (len(newheads) - 1))
+ if not err:
+ mod, add, rem = repo.status()[:3]
+ message = (commands.logmessage(opts) or
+ (_('Automated merge with %s') % other.url()))
+ n = repo.commit(mod + add + rem, message,
+ opts['user'], opts['date'],
+ force_editor=opts.get('force_editor'))
+ ui.status(_('new changeset %d:%s merges remote changes '
+ 'with local\n') % (repo.changelog.rev(n),
+ short(n)))
+ def pull():
+ commands.setremoteconfig(ui, opts)
+
+ other = hg.repository(ui, ui.expandpath(source))
+ ui.status(_('pulling from %s\n') % source)
+ revs = None
+ if opts['rev'] and not other.local():
+ raise util.Abort(_("fetch -r doesn't work for remote repositories yet"))
+ elif opts['rev']:
+ revs = [other.lookup(rev) for rev in opts['rev']]
+ modheads = repo.pull(other, heads=revs)
+ return postincoming(other, modheads)
+
+ parent, p2 = repo.dirstate.parents()
+ if parent != repo.changelog.tip():
+ raise util.Abort(_('working dir not at tip '
+ '(use "hg update" to check out tip)'))
+ if p2 != nullid:
+ raise util.Abort(_('outstanding uncommitted merge'))
+ mod, add, rem = repo.status()[:3]
+ if mod or add or rem:
+ raise util.Abort(_('outstanding uncommitted changes'))
+ if len(repo.heads()) > 1:
+ raise util.Abort(_('multiple heads in this repository '
+ '(use "hg heads" and "hg merge" to merge them)'))
+ return pull()
+
+cmdtable = {
+ 'fetch':
+ (fetch,
+ [('e', 'ssh', '', _('specify ssh command to use')),
+ ('m', 'message', '', _('use <text> as commit message')),
+ ('l', 'logfile', '', _('read the commit message from <file>')),
+ ('d', 'date', '', _('record datecode as commit date')),
+ ('u', 'user', '', _('record user as commiter')),
+ ('r', 'rev', [], _('a specific revision you would like to pull')),
+ ('f', 'force-editor', None, _('edit commit message')),
+ ('', 'remotecmd', '', _('hg command to run on the remote side'))],
+ 'hg fetch [SOURCE]'),
+ }
--- a/hgext/mq.py Mon Aug 07 16:27:09 2006 -0500
+++ b/hgext/mq.py Mon Aug 07 16:47:06 2006 -0500
@@ -39,6 +39,16 @@
commands.norepo += " qclone qversion"
+class StatusEntry:
+ def __init__(self, rev, name=None):
+ if not name:
+ self.rev, self.name = rev.split(':')
+ else:
+ self.rev, self.name = rev, name
+
+ def __str__(self):
+ return self.rev + ':' + self.name
+
class queue:
def __init__(self, ui, path, patchdir=None):
self.basepath = path
@@ -60,7 +70,8 @@
self.parse_series()
if os.path.exists(os.path.join(self.path, self.status_path)):
- self.applied = self.opener(self.status_path).read().splitlines()
+ self.applied = [StatusEntry(l)
+ for l in self.opener(self.status_path).read().splitlines()]
def find_series(self, patch):
pre = re.compile("(\s*)([^#]+)")
@@ -88,7 +99,7 @@
for i in items:
print >> fp, i
fp.close()
- if self.applied_dirty: write_list(self.applied, self.status_path)
+ if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
if self.series_dirty: write_list(self.full_series, self.series_path)
def readheaders(self, patch):
@@ -209,12 +220,10 @@
return p1
if len(self.applied) == 0:
return None
- (top, patch) = self.applied[-1].split(':')
- top = revlog.bin(top)
- return top
+ return revlog.bin(self.applied[-1].rev)
pp = repo.changelog.parents(rev)
if pp[1] != revlog.nullid:
- arevs = [ x.split(':')[0] for x in self.applied ]
+ arevs = [ x.rev for x in self.applied ]
p0 = revlog.hex(pp[0])
p1 = revlog.hex(pp[1])
if p0 in arevs:
@@ -234,7 +243,7 @@
pname = ".hg.patches.merge.marker"
n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
wlock=wlock)
- self.applied.append(revlog.hex(n) + ":" + pname)
+ self.applied.append(StatusEntry(revlog.hex(n), pname))
self.applied_dirty = 1
head = self.qparents(repo)
@@ -252,7 +261,7 @@
rev = revlog.bin(info[1])
(err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
if head:
- self.applied.append(revlog.hex(head) + ":" + patch)
+ self.applied.append(StatusEntry(revlog.hex(head), patch))
self.applied_dirty = 1
if err:
return (err, head)
@@ -263,8 +272,8 @@
patchfile: file name of patch'''
try:
pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
- f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
- (pp, repo.root, patchfile))
+ f = os.popen("%s -d %s -p1 --no-backup-if-mismatch < %s" %
+ (pp, util.shellquote(repo.root), util.shellquote(patchfile)))
except:
self.ui.warn("patch failed, unable to continue (try -v)\n")
return (None, [], False)
@@ -275,11 +284,7 @@
if self.ui.verbose:
self.ui.warn(l + "\n")
if l[:14] == 'patching file ':
- pf = os.path.normpath(l[14:])
- # when patch finds a space in the file name, it puts
- # single quotes around the filename. strip them off
- if pf[0] == "'" and pf[-1] == "'":
- pf = pf[1:-1]
+ pf = os.path.normpath(util.parse_patch_output(l))
if pf not in files:
files.append(pf)
printed_file = False
@@ -351,7 +356,7 @@
raise util.Abort(_("repo commit failed"))
if update_status:
- self.applied.append(revlog.hex(n) + ":" + patch)
+ self.applied.append(StatusEntry(revlog.hex(n), patch))
if patcherr:
if not patchfound:
@@ -389,8 +394,7 @@
def check_toppatch(self, repo):
if len(self.applied) > 0:
- (top, patch) = self.applied[-1].split(':')
- top = revlog.bin(top)
+ top = revlog.bin(self.applied[-1].rev)
pp = repo.dirstate.parents()
if top not in pp:
raise util.Abort(_("queue top not at same revision as working directory"))
@@ -421,7 +425,7 @@
if n == None:
raise util.Abort(_("repo commit failed"))
self.full_series[insert:insert] = [patch]
- self.applied.append(revlog.hex(n) + ":" + patch)
+ self.applied.append(StatusEntry(revlog.hex(n), patch))
self.parse_series()
self.series_dirty = 1
self.applied_dirty = 1
@@ -501,9 +505,9 @@
# we go in two steps here so the strip loop happens in a
# sensible order. When stripping many files, this helps keep
# our disk access patterns under control.
- list = seen.keys()
- list.sort()
- for f in list:
+ seen_list = seen.keys()
+ seen_list.sort()
+ for f in seen_list:
ff = repo.file(f)
filerev = seen[f]
if filerev != 0:
@@ -535,7 +539,6 @@
saveheads = []
savebases = {}
- tip = chlog.tip()
heads = limitheads(chlog, rev)
seen = {}
@@ -566,7 +569,7 @@
savebases[x] = 1
# create a changegroup for all the branches we need to keep
- if backup is "all":
+ if backup == "all":
backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
bundle(backupch)
if saveheads:
@@ -581,16 +584,15 @@
if saveheads:
self.ui.status("adding branch\n")
commands.unbundle(self.ui, repo, chgrpfile, update=False)
- if backup is not "strip":
+ if backup != "strip":
os.unlink(chgrpfile)
def isapplied(self, patch):
"""returns (index, rev, patch)"""
for i in xrange(len(self.applied)):
- p = self.applied[i]
- a = p.split(':')
- if a[1] == patch:
- return (i, a[0], a[1])
+ a = self.applied[i]
+ if a.name == patch:
+ return (i, a.rev, a.name)
return None
# if the exact patch name does not exist, we try a few
@@ -693,7 +695,7 @@
ret = self.mergepatch(repo, mergeq, s, wlock)
else:
ret = self.apply(repo, s, list, wlock=wlock)
- top = self.applied[-1].split(':')[1]
+ top = self.applied[-1].name
if ret[0]:
self.ui.write("Errors during apply, please fix and refresh %s\n" %
top)
@@ -730,7 +732,7 @@
if not update:
parents = repo.dirstate.parents()
- rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
+ rr = [ revlog.bin(x.rev) for x in self.applied ]
for p in parents:
if p in rr:
self.ui.warn("qpop: forcing dirstate update\n")
@@ -751,7 +753,7 @@
if popi >= end:
self.ui.warn("qpop: %s is already at the top\n" % patch)
return
- info = [ popi ] + self.applied[popi].split(':')
+ info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
start = info[0]
rev = revlog.bin(info[1])
@@ -784,7 +786,7 @@
self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
del self.applied[start:end]
if len(self.applied):
- self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
+ self.ui.write("Now at: %s\n" % self.applied[-1].name)
else:
self.ui.write("Patch queue now empty\n")
@@ -802,8 +804,7 @@
return
wlock = repo.wlock()
self.check_toppatch(repo)
- qp = self.qparents(repo)
- (top, patch) = self.applied[-1].split(':')
+ (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
top = revlog.bin(top)
cparents = repo.changelog.parents(top)
patchparent = self.qparents(repo, top)
@@ -899,7 +900,7 @@
self.strip(repo, top, update=False, backup='strip', wlock=wlock)
n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
- self.applied[-1] = revlog.hex(n) + ':' + patch
+ self.applied[-1] = StatusEntry(revlog.hex(n), patch)
self.applied_dirty = 1
else:
commands.dodiff(patchf, self.ui, repo, patchparent, None)
@@ -921,10 +922,7 @@
start = self.series_end()
else:
start = self.series.index(patch) + 1
- for p in self.series[start:]:
- if self.ui.verbose:
- self.ui.write("%d " % self.series.index(p))
- self.ui.write("%s\n" % p)
+ return [(i, self.series[i]) for i in xrange(start, len(self.series))]
def qseries(self, repo, missing=None, summary=False):
start = self.series_end()
@@ -944,7 +942,7 @@
msg = ''
self.ui.write('%s%s\n' % (patch, msg))
else:
- list = []
+ msng_list = []
for root, dirs, files in os.walk(self.path):
d = root[len(self.path) + 1:]
for f in files:
@@ -952,13 +950,12 @@
if (fl not in self.series and
fl not in (self.status_path, self.series_path)
and not fl.startswith('.')):
- list.append(fl)
- list.sort()
- if list:
- for x in list:
- if self.ui.verbose:
- self.ui.write("D ")
- self.ui.write("%s\n" % x)
+ msng_list.append(fl)
+ msng_list.sort()
+ for x in msng_list:
+ if self.ui.verbose:
+ self.ui.write("D ")
+ self.ui.write("%s\n" % x)
def issaveline(self, l):
name = l.split(':')[1]
@@ -987,12 +984,11 @@
qpp = [ hg.bin(x) for x in l ]
elif datastart != None:
l = lines[i].rstrip()
- index = l.index(':')
- id = l[:index]
- file = l[index + 1:]
- if id:
- applied.append(l)
- series.append(file)
+ se = StatusEntry(l)
+ file_ = se.name
+ if se.rev:
+ applied.append(se)
+ series.append(file_)
if datastart == None:
self.ui.warn("No saved patch data found\n")
return 1
@@ -1043,18 +1039,18 @@
pp = r.dirstate.parents()
msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
msg += "\n\nPatch Data:\n"
- text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
+ text = msg + "\n".join(str(self.applied)) + '\n' + (ar and "\n".join(ar)
+ '\n' or "")
n = repo.commit(None, text, user=None, force=1)
if not n:
self.ui.warn("repo commit failed\n")
return 1
- self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
+ self.applied.append(StatusEntry(revlog.hex(n),'.hg.patches.save.line'))
self.applied_dirty = 1
def full_series_end(self):
if len(self.applied) > 0:
- (top, p) = self.applied[-1].split(':')
+ p = self.applied[-1].name
end = self.find_series(p)
if end == None:
return len(self.full_series)
@@ -1064,7 +1060,7 @@
def series_end(self):
end = 0
if len(self.applied) > 0:
- (top, p) = self.applied[-1].split(':')
+ p = self.applied[-1].name
try:
end = self.series.index(p)
except ValueError:
@@ -1084,8 +1080,7 @@
self.ui.write("%s\n" % p)
def appliedname(self, index):
- p = self.applied[index]
- pname = p.split(':')[1]
+ pname = self.applied[index].name
if not self.ui.verbose:
p = pname
else:
@@ -1173,8 +1168,10 @@
def unapplied(ui, repo, patch=None, **opts):
"""print the patches not yet applied"""
- repo.mq.unapplied(repo, patch)
- return 0
+ for i, p in repo.mq.unapplied(repo, patch):
+ if ui.verbose:
+ ui.write("%d " % i)
+ ui.write("%s\n" % p)
def qimport(ui, repo, *filename, **opts):
"""import a patch"""
@@ -1223,7 +1220,7 @@
if sr.local():
reposetup(ui, sr)
if sr.mq.applied:
- qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
+ qbase = revlog.bin(sr.mq.applied[0].rev)
if not hg.islocal(dest):
destrev = sr.parents(qbase)[0]
ui.note(_('cloning main repo\n'))
@@ -1286,7 +1283,7 @@
If neither is specified, the patch header is empty and the
commit message is 'New patch: PATCH'"""
q = repo.mq
- message=commands.logmessage(**opts)
+ message = commands.logmessage(**opts)
q.new(repo, patch, msg=message, force=opts['force'])
q.save_dirty()
return 0
@@ -1294,11 +1291,11 @@
def refresh(ui, repo, **opts):
"""update the current patch"""
q = repo.mq
- message=commands.logmessage(**opts)
+ message = commands.logmessage(**opts)
if opts['edit']:
if message:
raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
- patch = q.applied[-1].split(':')[1]
+ patch = q.applied[-1].name
(message, comment, user, date, hasdiff) = q.readheaders(patch)
message = ui.edit('\n'.join(message), user or ui.username())
q.refresh(repo, msg=message, short=opts['short'])
@@ -1331,7 +1328,7 @@
if not q.check_toppatch(repo):
raise util.Abort(_('No patches applied\n'))
- message=commands.logmessage(**opts)
+ message = commands.logmessage(**opts)
if opts['edit']:
if message:
raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
@@ -1342,7 +1339,7 @@
for f in files:
patch = q.lookup(f)
if patch in patches or patch == parent:
- self.ui.warn(_('Skipping already folded patch %s') % patch)
+ ui.warn(_('Skipping already folded patch %s') % patch)
if q.isapplied(patch):
raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
patches.append(patch)
@@ -1388,20 +1385,20 @@
ui.write('\n'.join(message) + '\n')
def lastsavename(path):
- (dir, base) = os.path.split(path)
- names = os.listdir(dir)
+ (directory, base) = os.path.split(path)
+ names = os.listdir(directory)
namere = re.compile("%s.([0-9]+)" % base)
- max = None
+ maxindex = None
maxname = None
for f in names:
m = namere.match(f)
if m:
index = int(m.group(1))
- if max == None or index > max:
- max = index
+ if maxindex == None or index > maxindex:
+ maxindex = index
maxname = f
if maxname:
- return (os.path.join(dir, maxname), max)
+ return (os.path.join(directory, maxname), maxindex)
return (None, None)
def savename(path):
@@ -1482,7 +1479,7 @@
info = q.isapplied(patch)
if info:
- q.applied[info[0]] = info[1] + ':' + name
+ q.applied[info[0]] = StatusEntry(info[1], name)
q.applied_dirty = 1
util.rename(os.path.join(q.path, patch), absdest)
@@ -1508,7 +1505,7 @@
def save(ui, repo, **opts):
"""save current queue state"""
q = repo.mq
- message=commands.logmessage(**opts)
+ message = commands.logmessage(**opts)
ret = q.save(repo, msg=message)
if ret:
return ret
@@ -1563,7 +1560,7 @@
if not q.applied:
return tagscache
- mqtags = [patch.split(':') for patch in q.applied]
+ mqtags = [(patch.rev, patch.name) for patch in q.applied]
mqtags.append((mqtags[-1][0], 'qtip'))
mqtags.append((mqtags[0][0], 'qbase'))
for patch in mqtags:
--- a/hgext/notify.py Mon Aug 07 16:27:09 2006 -0500
+++ b/hgext/notify.py Mon Aug 07 16:47:06 2006 -0500
@@ -255,7 +255,7 @@
changegroup. else send one email per changeset.'''
n = notifier(ui, repo, hooktype)
if not n.subs:
- ui.debug(_('notify: no subscribers to this repo\n'))
+ ui.debug(_('notify: no subscribers to repo %s\n' % n.root))
return
if n.skipsource(source):
ui.debug(_('notify: changes have source "%s" - skipping\n') %
--- a/hgext/patchbomb.py Mon Aug 07 16:27:09 2006 -0500
+++ b/hgext/patchbomb.py Mon Aug 07 16:47:06 2006 -0500
@@ -288,7 +288,8 @@
fp.close()
else:
ui.status('Sending ', m['Subject'], ' ...\n')
- m.__delitem__('bcc')
+ # Exim does not remove the Bcc field
+ del m['Bcc']
mail.sendmail(sender, to + bcc + cc, m.as_string(0))
cmdtable = {
--- a/mercurial/commands.py Mon Aug 07 16:27:09 2006 -0500
+++ b/mercurial/commands.py Mon Aug 07 16:47:06 2006 -0500
@@ -40,7 +40,7 @@
return [util.normpath(os.path.join(cwd, x)) for x in args]
return args
-def logmessage(**opts):
+def logmessage(opts):
""" get the log message according to -m and -l option """
message = opts['message']
logfile = opts['logfile']
@@ -125,12 +125,22 @@
files, matchfn, anypats = matchpats(repo, pats, opts)
- follow = opts.get('follow')
+ follow = opts.get('follow') or opts.get('follow_first')
if repo.changelog.count() == 0:
return [], False, matchfn
- revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
+ if follow:
+ p = repo.dirstate.parents()[0]
+ if p == nullid:
+ ui.warn(_('No working directory revision; defaulting to tip\n'))
+ start = 'tip'
+ else:
+ start = repo.changelog.rev(p)
+ defrange = '%s:0' % start
+ else:
+ defrange = 'tip:0'
+ revs = map(int, revrange(ui, repo, opts['rev'] or [defrange]))
wanted = {}
slowpath = anypats
fncache = {}
@@ -206,10 +216,55 @@
wanted[rev] = 1
def iterate():
+ class followfilter:
+ def __init__(self, onlyfirst=False):
+ self.startrev = -1
+ self.roots = []
+ self.onlyfirst = onlyfirst
+
+ def match(self, rev):
+ def realparents(rev):
+ if self.onlyfirst:
+ return repo.changelog.parentrevs(rev)[0:1]
+ else:
+ return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
+
+ if self.startrev == -1:
+ self.startrev = rev
+ return True
+
+ if rev > self.startrev:
+ # forward: all descendants
+ if not self.roots:
+ self.roots.append(self.startrev)
+ for parent in realparents(rev):
+ if parent in self.roots:
+ self.roots.append(rev)
+ return True
+ else:
+ # backwards: all parents
+ if not self.roots:
+ self.roots.extend(realparents(self.startrev))
+ if rev in self.roots:
+ self.roots.remove(rev)
+ self.roots.extend(realparents(rev))
+ return True
+
+ return False
+
+ if follow and not files:
+ ff = followfilter(onlyfirst=opts.get('follow_first'))
+ def want(rev):
+ if rev not in wanted:
+ return False
+ return ff.match(rev)
+ else:
+ def want(rev):
+ return rev in wanted
+
for i, window in increasing_windows(0, len(revs)):
yield 'window', revs[0] < revs[-1], revs[-1]
- nrevs = [rev for rev in revs[i:i+window]
- if rev in wanted]
+ nrevs = [rev for rev in revs[i:i+window] if want(rev)]
srevs = list(nrevs)
srevs.sort()
for rev in srevs:
@@ -1041,7 +1096,7 @@
If no commit message is specified, the editor configured in your hgrc
or in the EDITOR environment variable is started to enter a message.
"""
- message = logmessage(**opts)
+ message = logmessage(opts)
if opts['addremove']:
addremove_lock(ui, repo, pats, opts)
@@ -1972,8 +2027,14 @@
project.
File history is shown without following rename or copy history of
- files. Use -f/--follow to follow history across renames and
- copies.
+ files. Use -f/--follow with a file name to follow history across
+ renames and copies. --follow without a file name will only show
+ ancestors or descendants of the starting revision. --follow-first
+ only follows the first parent of merge revisions.
+
+ If no revision range is specified, the default is tip:0 unless
+ --follow is set, in which case the working directory parent is
+ used as the starting revision.
By default this command outputs: changeset id and hash, tags,
non-trivial parents, user, date and time, and a summary for each
@@ -2728,8 +2789,8 @@
necessary. The file '.hg/localtags' is used for local tags (not
shared among repositories).
"""
- if name == "tip":
- raise util.Abort(_("the name 'tip' is reserved"))
+ if name in ['tip', '.']:
+ raise util.Abort(_("the name '%s' is reserved") % name)
if rev_ is not None:
ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
"please use 'hg tag [-r REV] NAME' instead\n"))
@@ -3087,7 +3148,9 @@
(log,
[('b', 'branches', None, _('show branches')),
('f', 'follow', None,
- _('follow file history across copies and renames')),
+ _('follow changeset history, or file history across copies and renames')),
+ ('', 'follow-first', None,
+ _('only follow the first parent of merge changesets')),
('k', 'keyword', [], _('search for a keyword')),
('l', 'limit', '', _('limit number of changes displayed')),
('r', 'rev', [], _('show the specified revision or range')),
--- a/mercurial/localrepo.py Mon Aug 07 16:27:09 2006 -0500
+++ b/mercurial/localrepo.py Mon Aug 07 16:47:06 2006 -0500
@@ -292,6 +292,10 @@
try:
return self.tags()[key]
except KeyError:
+ if key == '.':
+ key = self.dirstate.parents()[0]
+ if key == nullid:
+ raise repo.RepoError(_("no revision checked out"))
try:
return self.changelog.lookup(key)
except:
@@ -1693,6 +1697,7 @@
return newheads - oldheads + 1
+
def stream_in(self, remote):
fp = remote.stream_out()
resp = int(fp.readline())
--- a/mercurial/merge.py Mon Aug 07 16:27:09 2006 -0500
+++ b/mercurial/merge.py Mon Aug 07 16:47:06 2006 -0500
@@ -48,7 +48,8 @@
return r
def update(repo, node, allow=False, force=False, choose=None,
- moddirstate=True, forcemerge=False, wlock=None, show_stats=True):
+ moddirstate=True, forcemerge=False, wlock=None, show_stats=True,
+ remind=True):
pl = repo.dirstate.parents()
if not force and pl[1] != nullid:
raise util.Abort(_("outstanding uncommitted merges"))
@@ -337,7 +338,7 @@
" hg merge %s\n"
% (repo.changelog.rev(p1),
repo.changelog.rev(p2))))
- else:
+ elif remind:
repo.ui.status(_("(branch merge, don't forget to commit)\n"))
elif failedmerge:
repo.ui.status(_("There are unresolved merges with"
--- a/mercurial/util.py Mon Aug 07 16:27:09 2006 -0500
+++ b/mercurial/util.py Mon Aug 07 16:47:06 2006 -0500
@@ -99,9 +99,9 @@
patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
args = []
if cwd:
- args.append('-d "%s"' % cwd)
- fp = os.popen('%s %s -p%d < "%s"' % (patcher, ' '.join(args), strip,
- patchname))
+ args.append('-d %s' % shellquote(cwd))
+ fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
+ shellquote(patchname)))
files = {}
for line in fp:
line = line.rstrip()
@@ -611,6 +611,9 @@
def samestat(s1, s2):
return False
+ def shellquote(s):
+ return '"%s"' % s.replace('"', '\\"')
+
def explain_exit(code):
return _("exited with status %d") % code, code
@@ -700,6 +703,9 @@
else:
raise
+ def shellquote(s):
+ return "'%s'" % s.replace("'", "'\\''")
+
def testpid(pid):
'''return False if pid dead, True if running or not sure'''
try:
--- a/tests/test-log Mon Aug 07 16:27:09 2006 -0500
+++ b/tests/test-log Mon Aug 07 16:47:06 2006 -0500
@@ -28,3 +28,38 @@
hg log -vf a
echo % many renames
hg log -vf e
+
+# log --follow tests
+hg init ../follow
+cd ../follow
+echo base > base
+hg ci -Ambase -d '1 0'
+
+echo r1 >> base
+hg ci -Amr1 -d '1 0'
+echo r2 >> base
+hg ci -Amr2 -d '1 0'
+
+hg up -C 1
+echo b1 > b1
+hg ci -Amb1 -d '1 0'
+
+echo % log -f
+hg log -f
+
+hg up -C 0
+echo b2 > b2
+hg ci -Amb2 -d '1 0'
+
+echo % log -f -r 1:tip
+hg log -f -r 1:tip
+
+hg up -C 3
+hg merge tip
+hg ci -mm12 -d '1 0'
+
+echo postm >> b1
+hg ci -Amb1.1 -d'1 0'
+
+echo % log --follow-first
+hg log --follow-first
--- a/tests/test-log.out Mon Aug 07 16:27:09 2006 -0500
+++ b/tests/test-log.out Mon Aug 07 16:47:06 2006 -0500
@@ -76,3 +76,76 @@
a
+adding base
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adding b1
+% log -f
+changeset: 3:e62f78d544b4
+tag: tip
+parent: 1:3d5bf5654eda
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: b1
+
+changeset: 1:3d5bf5654eda
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: r1
+
+changeset: 0:67e992f2c4f3
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: base
+
+1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+adding b2
+% log -f -r 1:tip
+changeset: 1:3d5bf5654eda
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: r1
+
+changeset: 2:60c670bf5b30
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: r2
+
+changeset: 3:e62f78d544b4
+parent: 1:3d5bf5654eda
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: b1
+
+2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+% log --follow-first
+changeset: 6:2404bbcab562
+tag: tip
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: b1.1
+
+changeset: 5:302e9dd6890d
+parent: 3:e62f78d544b4
+parent: 4:ddb82e70d1a1
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: m12
+
+changeset: 3:e62f78d544b4
+parent: 1:3d5bf5654eda
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: b1
+
+changeset: 1:3d5bf5654eda
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: r1
+
+changeset: 0:67e992f2c4f3
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: base
+