Changeset 50302f954e2f…
Parent eb3f0219a412…
by Kevin Gessner <kevin@fogcreek.com>
Changes to 6 files · Browse files at 50302f954e2f Showing diff from parent eb3f0219a412 Diff from another changeset...
|
|
- # Copyright (C) 2009-2011 Fog Creek Software. All rights reserved.
+# Copyright (C) 2009-2013 Fog Creek Software. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
'''automatically push large repositories in chunks'''
from mercurial import cmdutil, commands, hg, extensions
from mercurial.i18n import _
max_push_size = 1000
+
def findoutgoing(repo, other):
try:
# Mercurial 1.6 through 1.8
from mercurial import discovery
return discovery.findoutgoing(repo, other, force=False)
except AttributeError:
# Mercurial 1.9 and higher
common, _anyinc, _heads = discovery.findcommonincoming(repo, other, force=False)
return repo.changelog.findmissing(common)
except ImportError:
# Mercurial 1.5 and lower
return repo.findoutgoing(other, force=False)
+
def prepush(repo, other, force, revs):
try:
from mercurial import discovery
try:
# Mercurial 1.6 through 2.0
return discovery.prepush(repo, other, force, revs, False)
except AttributeError:
# Mercurial 2.1 and higher
fci = discovery.findcommonincoming(repo, other, force=force)
outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs, commoninc=fci, force=force)
if not force:
discovery.checkheads(repo, other, outgoing, fci[2], False)
return [True]
except ImportError:
# Mercurial 1.5 and lower
return repo.prepush(other, False, revs)
+
def remoteui(repo, opts):
if hasattr(cmdutil, 'remoteui'):
# Mercurial 1.5 and lower
return cmdutil.remoteui(repo, opts)
else:
# Mercurial 1.6 and higher
return hg.remoteui(repo, opts)
+
def bigpush(push_fn, ui, repo, dest=None, *files, **opts):
'''Pushes this repository to a target repository.
If this repository is small, behaves as the native push command.
For large, remote repositories, the repository is pushed in chunks
of size optimized for performance on the network.'''
if not opts.get('chunked'):
return push_fn(ui, repo, dest, **opts)
source, revs = parseurl(ui.expandpath(dest or 'default-push', dest or 'default'))
try:
other = hg.repository(remoteui(repo, opts), source)
except:
# hg 2.3+
other = hg.peer(repo, opts, source)
if hasattr(hg, 'addbranchrevs'):
revs = hg.addbranchrevs(repo, other, revs, opts.get('rev'))[0]
if revs:
revs = [repo.lookup(rev) for rev in revs]
if other.local():
return push_fn(ui, repo, dest, **opts)
ui.status(_('pushing to %s\n') % other.path)
outgoing = findoutgoing(repo, other)
if outgoing:
outgoing = repo.changelog.nodesbetween(outgoing, revs)[0]
# if the push will create multiple heads and isn't forced, fail now
# (prepush prints an error message, so we can just exit)
if not opts.get('force') and not opts.get('new_branch') and None == prepush(repo, other, False, revs)[0]:
return
try:
push_size = 1
while len(outgoing) > 0:
ui.debug('start: %d to push\n' % len(outgoing))
current_push_size = min(push_size, len(outgoing))
ui.debug('pushing: %d\n' % current_push_size)
# force the push, because we checked above that by the time the whole push is done, we'll have merged back to one head
remote_heads = repo.push(other, force=True, revs=outgoing[:current_push_size])
- if remote_heads: # push succeeded
+ if remote_heads: # push succeeded
outgoing = outgoing[current_push_size:]
ui.debug('pushed %d ok\n' % current_push_size)
if push_size < max_push_size:
push_size *= 2
- else: # push failed; try again with a smaller size
+ else: # push failed; try again with a smaller size
push_size /= 2
ui.debug('failed, trying %d\n' % current_push_size)
if push_size == 0:
raise UnpushableChangesetError
except UnpushableChangesetError:
ui.status(_('unable to push changeset %s\n') % outgoing[0])
ui.debug('done\n')
+
def parseurl(source):
'''wrap hg.parseurl to work on 1.3 -> 1.5'''
return hg.parseurl(source, None)[:2]
+
def uisetup(ui):
push_cmd = extensions.wrapcommand(commands.table, 'push', bigpush)
push_cmd[1].extend([('', 'chunked', None, 'push large repository in chunks')])
+
class UnpushableChangesetError(Exception):
pass
|
|
|
@@ -1,4 +1,4 @@ - # Copyright (C) 2009-2012 Fog Creek Software. All rights reserved.
+# Copyright (C) 2009-2013 Fog Creek Software. All rights reserved.
#
# To enable the "gestalt" extension put these lines in your ~/.hgrc:
# [extensions]
@@ -66,6 +66,7 @@ return repo.changelog.findmissing(common)
demandimport.enable()
+
@apply
def _HG_VERSION():
'''return the mercurial version as a tuple rather than a string
@@ -79,6 +80,7 @@ version[i] = part
return tuple(version)
+
def parseurl(source):
'''wrap hg.parseurl to work on 1.5 and 1.6
@@ -96,6 +98,7 @@ hashbranch = branches and branches[0] or None
return uri, hashbranch
+
def addbranchrevs(lrepo, repo, hashbranch):
'''wrap hg.addbranchrevs to work on 1.5 and 1.6 and returns the
first value (revs) only
@@ -111,6 +114,7 @@ revs, checkout = hg.addbranchrevs(lrepo, repo, branches, None)
return revs
+
##
# Private utility functions for determining advice output
def _isrepo(ui, repo, files, opts):
@@ -122,12 +126,14 @@ return True
return False
+
def _ismerging(ui, repo, files, opts):
if repo.dirstate.parents()[1] != node.nullid:
ui.status(_('It appears there is a merge in progress.\n'))
return True
return False
+
def _haschanges(ui, repo, files, opts):
changed = any(repo.status())
if changed:
@@ -139,6 +145,7 @@ return True
return False
+
def _shouldmerge(ui, repo, files, opts):
heads = repo.branchheads(closed=False)
if len(heads) > 1:
@@ -150,6 +157,7 @@ return True
return False
+
def _shouldsync(ui, repo, files, opts):
source, hashbranch = parseurl(ui.expandpath('default'))
@@ -194,6 +202,7 @@ return True
return False
+
def _istip(ui, repo, files, opts):
tip = repo['tip']
cwd = repo['.']
@@ -206,10 +215,12 @@ return True
return False
+
def _shouldwritemorecode(ui, *ignored):
ui.status(_('Everything is up-to-date. Write more code!\n'))
return True
+
##
# General utility methods
def outgoing(repo, origin):
@@ -219,6 +230,7 @@ out = repo.changelog.nodesbetween(out, None)[0]
return out
+
def incoming(repo, origin, revs):
'''return a list of incoming changesets'''
if revs:
@@ -235,13 +247,14 @@ cg = origin.changegroup(incoming, 'incoming')
else:
cg = origin.changegroupsubset(incoming, revs, 'incoming')
- fname = changegroup.writebundle(cg, None, "HG10UN")
+ fname = changegroup.writebundle(cg, None, "HG10UN")
origin = bundlerepo.bundlerepository(repo.ui, repo.root, fname)
incoming = origin.changelog.findmissing(common, revs)
if hasattr(origin, 'close'):
origin.close()
return incoming
+
##
# commands
def overview(ui, repo, source=None, **opts):
@@ -316,6 +329,7 @@ for l in status.splitlines():
print ' %s' % l
+
def advice(ui, repo, *files, **opts):
'''provides a suggestion of your next step
@@ -336,6 +350,7 @@ if fun(ui, repo, files, opts):
return
+
def next(ui, repo, *files, **opts):
'''provides an overview and explanation of what to do next
@@ -357,6 +372,6 @@ (next,
[('d', 'detail', None, _('provide verbose output'))],
_('hg next [OPTION] [REMOTE REPOSITORY]')),
- }
+}
commands.optionalrepo += 'wtf advice overview next'
|
|
|
@@ -1,4 +1,4 @@ - # Copyright (C) 2011, 2012 Fog Creek Software. All rights reserved.
+# Copyright (C) 2011-2013 Fog Creek Software. All rights reserved.
#
# To enable the "kiln" extension put these lines in your ~/.hgrc:
# [extensions]
@@ -40,6 +40,7 @@pushing to Kiln. See :hg:`help push` and
http://kiln.stackexchange.com/questions/4679/ for more information.
'''
+
import itertools
import os
import re
@@ -52,7 +53,7 @@from cookielib import MozillaCookieJar
from hashlib import md5
from mercurial import (commands, cmdutil, demandimport, extensions, hg,
- localrepo, match, util)
+ localrepo, match, util)
from mercurial import ui as hgui
from mercurial import url as hgurl
from mercurial.error import RepoError
@@ -73,17 +74,20 @@
try:
import webbrowser
+
def browse(url):
webbrowser.open(escape_reserved(url))
except ImportError:
if os.name == 'nt':
import win32api
+
def browse(url):
win32api.ShellExecute(0, 'open', escape_reserved(url), None, None, 0)
demandimport.enable()
_did_version_check = False
+
class APIError(Exception):
def __init__(self, obj):
'''takes a json object for debugging
@@ -95,6 +99,7 @@ def __str__(self):
return '\n'.join('%s: %s' % (k, v) for k, v in self.errors.items())
+
class Review(dict):
def __init__(self, json, ui=None, token=None, baseurl=None):
self.ui = ui
@@ -119,29 +124,32 @@ 'token': self.token,
'ixBug': self.key,
'revs': revs,
- }
+ }
call_api(self.ui, self.baseurl, 'Api/1.0/Repo/%d/CaseAssociation/Create' % ixRepo, params, post=True)
else:
params = {
'token': self.token,
'revs': revs,
'ixRepo': ixRepo,
- }
+ }
call_api(self.ui, self.baseurl, 'Api/2.0/Review/%s/Association/Create' % self.key, params, post=True)
return urljoin(self.baseurl, 'Review', self.key)
@classmethod
def get_reviews(klass, ui, token, baseurl, ixRepo):
- review_lists = call_api(ui, baseurl, 'Api/2.0/Reviews', dict(token=token))
+ review_lists = call_api(ui, baseurl, 'Api/2.0/Reviews', dict(token=token, fReviewed="false", fAwaitingReview="false", nDaysActive=14))
reviews = {}
for key, review_list in review_lists.iteritems():
- if not key.startswith('reviews'): continue
+ if not key.startswith('reviews'):
+ continue
for review in review_list:
review = Review(review, ui, token, baseurl)
- if not review.belongs_to(ixRepo): continue
+ if not review.belongs_to(ixRepo):
+ continue
reviews[review.key.lower()] = review
return reviews
+
def urljoin(*components):
url = components[0]
for next in components[1:]:
@@ -152,6 +160,7 @@ url += next
return url
+
def _baseurl(ui, path):
try:
url = str(util.url(util.removeauth(path)))
@@ -168,12 +177,13 @@ else:
return None
+
def escape_reserved(path):
reserved = re.compile(
- r'^(((com[1-9]|lpt[1-9]|con|prn|aux)(\..*)?)|web\.config' +
- r'|clock\$|app_data|app_code|app_browsers' +
- r'|app_globalresources|app_localresources|app_themes' +
- r'|app_webreferences|bin|.*\.(cs|vb)html?)$', re.IGNORECASE)
+ r'^(((com[1-9]|lpt[1-9]|con|prn|aux)(\..*)?)|web\.config' +
+ r'|clock\$|app_data|app_code|app_browsers' +
+ r'|app_globalresources|app_localresources|app_themes' +
+ r'|app_webreferences|bin|.*\.(cs|vb)html?|.*\.(svc|xamlx|xoml|rules))$', re.IGNORECASE)
p = path.split('?')
path = p[0]
query = '?' + p[1] if len(p) > 1 else ''
@@ -182,9 +192,11 @@ else part
for part in path.split('/')) + query
+
def normalize_name(s):
return s.lower().replace(' ', '-')
+
def call_api(ui, baseurl, urlsuffix, params, post=False):
'''returns the json object for the url and the data dictionary
@@ -206,14 +218,18 @@ raise util.Abort(_('kiln: an error occurred while trying to reach %s') % url)
if isinstance(obj, dict) and 'errors' in obj:
- if 'token' in params and obj['errors'][0]['codeError'] == 'InvalidToken':
+ error_code = obj['errors'][0]['codeError']
+ if 'token' in params and error_code == 'InvalidToken':
token = login(ui, baseurl)
add_kilnapi_token(ui, baseurl, token)
params['token'] = token
return call_api(ui, baseurl, urlsuffix, params, post)
+ elif error_code == 'BadAuthentication':
+ raise util.Abort(_('authorization failed'))
raise APIError(obj)
return obj
+
def login(ui, url):
ui.write(_('realm: %s\n') % url)
user = ui.prompt('username:')
@@ -225,6 +241,7 @@ return token
raise util.Abort(_('authorization failed'))
+
def get_domain(url):
temp = url[url.find('://') + len('://'):]
domain = temp[:temp.find('/')]
@@ -236,6 +253,7 @@
return domain
+
def _get_path(path):
if os.name == 'nt':
ret = os.path.expanduser('~\\_' + path)
@@ -246,6 +264,7 @@ ret = re.sub(r'([A-Za-z]):', r'\1:\\', ret)
return ret
+
def _upgradecheck(ui, repo):
global _did_version_check
if _did_version_check or not ui.configbool('kiln', 'autoupdate', True):
@@ -253,6 +272,7 @@ _did_version_check = True
_upgrade(ui, repo)
+
def _upgrade(ui, repo):
ext_dir = os.path.dirname(os.path.abspath(__file__))
ui.debug(_('kiln: checking for extensions upgrade for %s\n') % ext_dir)
@@ -282,6 +302,7 @@ ui.debug(_('kiln: error updating extensions: %s\n') % e)
ui.debug(_('kiln: traceback: %s\n') % traceback.format_exc())
+
def is_dest_a_path(ui, dest):
paths = ui.configitems('paths')
for pathname, path in paths:
@@ -289,6 +310,7 @@ return True
return False
+
def is_dest_a_scheme(ui, dest):
destscheme = dest[:dest.find('://')]
if destscheme:
@@ -297,12 +319,14 @@ return True
return False
+
def create_match_list(matchlist):
ret = ''
for m in matchlist:
ret += ' ' + m + '\n'
return ret
+
def get_username(url):
url = re.sub(r'https?://', '', url)
url = re.sub(r'/.*', '', url)
@@ -316,6 +340,7 @@ # Didn't find anything...
return ''
+
def get_dest(ui):
from mercurial.dispatch import _parse
try:
@@ -332,6 +357,7 @@ dest = 'default'
return ui.expandpath(dest)
+
def check_kilnapi_token(ui, url):
tokenpath = _get_path('hgkiln')
@@ -355,6 +381,7 @@ fp.close()
return ret
+
def add_kilnapi_token(ui, url, fbToken):
if not fbToken:
return
@@ -369,12 +396,14 @@ fp.write(domain + ' ' + userhash + ' ' + fbToken + '\n')
fp.close()
+
def delete_kilnapi_tokens():
# deletes the hgkiln file
tokenpath = _get_path('hgkiln')
if os.path.exists(tokenpath) and not os.path.isdir(tokenpath):
os.remove(tokenpath)
+
def check_kilnauth_token(ui, url):
cookiepath = _get_path('hgcookies')
if (not os.path.exists(cookiepath)) or (not os.path.isdir(cookiepath)):
@@ -396,14 +425,17 @@ if cookie.name == 'fbToken':
return cookie.value
+
def remember_path(ui, repo, path, value):
'''appends the path to the working copy's hgrc and backs up the original'''
paths = dict(ui.configitems('paths'))
# This should never happen.
- if path in paths: return
+ if path in paths:
+ return
# ConfigParser only cares about these three characters.
- if re.search(r'[:=\s]', path): return
+ if re.search(r'[:=\s]', path):
+ return
try:
audit_path = scmutil.pathauditor(repo.root)
@@ -428,6 +460,7 @@ except IOError:
return
+
def unremember_path(ui, repo):
'''restores the working copy's hgrc'''
@@ -446,6 +479,7 @@ else:
os.remove(hgrc)
+
def guess_kilnpath(orig, ui, repo, dest=None, **opts):
if not dest:
return orig(ui, repo, **opts)
@@ -465,24 +499,24 @@
if (ndest.count('/') == 0 and
(ntarget[0] == ndest or
- ntarget[1] == ndest or
- ntarget[2] == ndest or
- ndest in aliases)):
+ ntarget[1] == ndest or
+ ntarget[2] == ndest or
+ ndest in aliases)):
matches.append(url)
elif (ndest.count('/') == 1 and
- '/'.join(ntarget[0:2]) == ndest or
- '/'.join(ntarget[1:3]) == ndest):
+ '/'.join(ntarget[0:2]) == ndest or
+ '/'.join(ntarget[1:3]) == ndest):
matches.append(url)
elif (ndest.count('/') == 2 and
- '/'.join(ntarget[0:3]) == ndest):
+ '/'.join(ntarget[0:3]) == ndest):
matches.append(url)
if (ntarget[0].startswith(ndest) or
- ntarget[1].startswith(ndest) or
- ntarget[2].startswith(ndest) or
- '/'.join(ntarget[0:2]).startswith(ndest) or
- '/'.join(ntarget[1:3]).startswith(ndest) or
- '/'.join(ntarget[0:3]).startswith(ndest)):
+ ntarget[1].startswith(ndest) or
+ ntarget[2].startswith(ndest) or
+ '/'.join(ntarget[0:2]).startswith(ndest) or
+ '/'.join(ntarget[1:3]).startswith(ndest) or
+ '/'.join(ntarget[0:3]).startswith(ndest)):
prefixmatches.append(url)
if len(matches) == 0:
@@ -503,6 +537,7 @@ finally:
unremember_path(ui, repo)
+
def get_tails(repo):
tails = []
for rev in xrange(repo['tip'].rev() + 1):
@@ -513,6 +548,7 @@ raise util.Abort(_('Path guessing is only enabled for non-empty repositories.'))
return tails
+
def get_targets(repo):
def get_kiln_repo_url_prefix(default_prefix):
'''Checks repo paths and returns server url for ssh:. For http(s) falls back to default_prefix.'''
@@ -536,9 +572,10 @@ related_repo['sProjectSlug'],
related_repo['sGroupSlug'],
related_repo['sSlug'],
- related_repo.get('rgAliases', [])] for related_repo in related_repos])
+ [a['sSlug'] for a in related_repo.get('rgAliases', [])]] for related_repo in related_repos])
return targets
+
def display_targets(repo):
targets = get_targets(repo)
repo.ui.write(_('The following Kiln targets are available for this repository:\n\n'))
@@ -549,6 +586,7 @@ alias_text = ''
repo.ui.write(' %s/%s/%s/%s%s\n' % (target[0], target[1], target[2], target[3], alias_text))
+
def get_token(ui, url):
'''Checks for an existing API token. If none, returns a new valid token.'''
token = check_kilnapi_token(ui, url)
@@ -560,6 +598,7 @@ add_kilnapi_token(ui, url, token)
return token
+
def get_api_url(url):
'''Given a URL, returns the URL of the Kiln installation.'''
if '/kiln/' in url.lower():
@@ -570,6 +609,7 @@ baseurl = url
return baseurl
+
class HTTPNoRedirectHandler(urllib2.HTTPRedirectHandler):
def http_error_302(self, req, fp, code, msg, headers):
# Doesn't allow multiple redirects so repo alias URLs will not
@@ -578,6 +618,7 @@
http_error_301 = http_error_303 = http_error_307 = http_error_302
+
def get_repo_record(repo, url, token=None):
'''Returns a Kiln repository record that corresponds to the given repo.'''
baseurl = get_api_url(url)
@@ -585,7 +626,7 @@ token = get_token(repo.ui, baseurl)
try:
- data = urllib.urlencode({ 'token': token }, doseq=True)
+ data = urllib.urlencode({'token': token}, doseq=True)
opener = urllib2.build_opener(HTTPNoRedirectHandler)
urllib2.install_opener(opener)
fd = urllib2.urlopen(url + '?' + data)
@@ -614,6 +655,7 @@ repo = find_slug(repo, group, 'repos')
return repo
+
def new_branch(repo, url, name):
'''Creates a new, decentralized branch off of the specified repo.'''
baseurl = get_api_url(url)
@@ -634,33 +676,40 @@ return
raise
+
def normalize_user(s):
'''Takes a Unicode string and returns an ASCII string.'''
return unicodedata.normalize('NFKD', s).encode('ASCII', 'ignore')
+
def encode_out(s):
'''Takes a Unicode string and returns a string encoded for output.'''
return s.encode(sys.stdout.encoding, 'ignore')
+
def record_base(ui, repo, node, **kwargs):
'''Stores the first changeset committed in the repo UI so we do not need to expensively recalculate.'''
repo.ui.setconfig('kiln', 'node', node)
+
def walk(repo, revs):
'''Returns revisions in repo specified by the string revs'''
return cmdutil.walkchangerevs(repo, match.always(repo.root, None), {'rev': [revs.encode('ascii', 'ignore')]}, lambda *args: None)
+
def print_list(ui, l, header):
'''Prints a list l to ui using list notation, with header being the first line'''
ui.write(_('%s\n' % header))
for item in l:
ui.write(_('- %s\n') % item)
+
def wrap_push(orig, ui, repo, dest=None, **opts):
'''Wraps `hg push' so a review will be created after path guessing and a successful push.'''
guess_kilnpath(orig, ui, repo, dest, **opts)
review(ui, repo, dest, opts)
+
def add_unique_reviewer(ui, reviewer, reviewers, name_to_ix, ix_to_name):
'''Adds a reviewer to reviewers if it is not already added. Otherwise, print an error.'''
if name_to_ix[reviewer] in reviewers:
@@ -669,6 +718,7 @@ reviewers.append(name_to_ix[reviewer])
print_list(ui, [ix_to_name[r] for r in reviewers], 'reviewers:')
+
def review(ui, repo, dest, opts):
'''Associates the pushed changesets with a new or existing Kiln review.'''
if not opts['review'] or not repo.ui.config('kiln', 'node'):
@@ -770,7 +820,7 @@ 'ixReviewers': reviewers,
'sTitle': '(Multiple changesets)' if len(revs) > 1 else repo[revs[0]].description(),
'sDescription': 'Review created from push.'
- }
+ }
r = Review(call_api(repo.ui, baseurl, 'Api/2.0/Review/Create', params, post=True))
ui.write(_('new review created: %s\n' % urljoin(baseurl, 'Review', r.key)))
else:
@@ -778,6 +828,7 @@ url = reviews[choice].associate(kiln_repo['ixRepo'], revs)
ui.write(_('updated review: %s\n' % url))
+
def dummy_command(ui, repo, dest=None, **opts):
'''dummy command to pass to guess_path() for hg kiln
@@ -786,6 +837,7 @@ '''
return opts['path'] != dest and dest or None
+
def _standin_expand(paths):
'''given a sequence of filenames, returns a set of filenames --
relative to the current working directory! -- prefixed with all
@@ -795,6 +847,7 @@ choices = [[p, os.path.join('.kbf', p), os.path.join('.hglf', p)] for p in paths]
return set(itertools.chain(*choices))
+
def _filename_match(repo, ctx, paths):
'''returns a set of filenames contained in both paths and the
ctx's manifest, accounting for standins'''
@@ -812,6 +865,7 @@ haystacks = set(map(os.path.normpath, haystacks))
return needles.intersection(haystacks)
+
def kiln(ui, repo, **opts):
'''show the relevant page of the repository in Kiln
@@ -894,6 +948,7 @@ if default or opts['changes']:
browse(url)
+
def uisetup(ui):
extensions.wrapcommand(commands.table, 'outgoing', guess_kilnpath)
extensions.wrapcommand(commands.table, 'pull', guess_kilnpath)
@@ -902,6 +957,7 @@ # Add --review as a valid flag to push's command table
push_cmd[1].extend([('', 'review', None, 'associate changesets with Kiln review')])
+
def reposetup(ui, repo):
try:
from mercurial.httprepo import httprepository
@@ -913,6 +969,7 @@ _upgradecheck(ui, repo)
repo.ui.setconfig('hooks', 'outgoing.kilnreview', 'python:kiln.record_base')
+
def extsetup(ui):
try:
g = extensions.find('gestalt')
@@ -943,4 +1000,4 @@ ('n', 'new-branch', '', _('asynchronously create a new branch from the current repository')),
('', 'logout', None, _('log out of Kiln sessions'))],
_('hg kiln [-p url] [-r rev|-a file|-f file|-c|-o|-s|-t|-n branchName|--logout]'))
- }
+}
|
|
@@ -0,0 +1,59 @@ + # Copyright (C) 2011, 2012 Fog Creek Software. All rights reserved.
+#
+# This extension is used internally by Kiln Importer.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+'''fix a _filecache invalidation bug so hg convert works
+
+See
+
+ * http://our.fogbugz.com/f/cases/2338914/importer-does-not-work-with-hg-2-3-1 and
+ * http://www.selenic.com/pipermail/mercurial-devel/2012-November/046159.html
+
+ for more details. Until Mercurial fixes this bug or accepts my patch,
+this bug affects any tags in this revset:
+
+ * hg log -r 'descendants(9f94358) and tag()' --template '{tags}\n'
+
+As of writing, that's every 2.3 release and 2.4-rc and 2.4.
+'''
+
+from mercurial import util
+
+def reposetup(ui, repo):
+ if util.version()[:3] < '2.3':
+ # This bug was first introduced in 9f94358f9f93, which
+ # occurred between 2.3-rc and 2.3.
+ return
+
+ from mercurial import localrepo
+ if not issubclass(repo.__class__, localrepo.localrepository):
+ return
+
+ class kilnfilecacherepo(repo.__class__):
+ def destroyed(self, *args, **kwargs):
+ # By saving _filecache before it's cleared, we can
+ # restore it and then call invalidate() afterward so
+ # localrepo can delattr() the relevant attributes off
+ # the repo object. See my mercurial-devel patch for
+ # more details.
+ oldfilecache = dict(self._filecache)
+ super(kilnfilecacherepo, self).destroyed(*args, **kwargs)
+ self._filecache = oldfilecache
+ self.invalidate()
+
+ repo.__class__ = kilnfilecacherepo
|
@@ -1,4 +1,4 @@ - # Copyright (C) 2009-2011 Fog Creek Software. All rights reserved.
+# Copyright (C) 2009-2013 Fog Creek Software. All rights reserved.
#
# To enable the "kilnpath" extension put these lines in your ~/.hgrc:
# [extensions]
@@ -18,7 +18,7 @@# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-'''Lets users identify repository paths by custom schemes like kiln://Project/Group/Repository.
+'''allow users identify repository paths by custom schemes like kiln://Project/Group/Repository (DEPRECATED)
This extensions knows how to expand Kiln repository paths from
kiln://Project/Group/Repository to
@@ -31,10 +31,36 @@To specify the Kiln path prefix, add a [kiln_scheme] section to your hg config like so:
[kiln_scheme]
kiln = https://your.fogbugz.com/kiln/Repo
+
+This extensions is deprecated, and does not work in Mercurial 2.4 and later.
+You should use the built-in `schemes` extension instead instead. Type `hg help schemes`
+for more information.
'''
-import os
-from mercurial import extensions, commands, hg, ui
+import re
+
+from mercurial import hg, ui, util, __version__
+
+
+@apply
+def _HG_VERSION():
+ '''return the mercurial version as a tuple rather than a string
+
+ Python does the right thing when comparing tuples, so the return
+ value can be used to compare and detect versions.
+ '''
+ version = [0, 0, 0]
+ parts = [re.match(r'\d+', v).group(0) for v in __version__.version.split('.')[:3]]
+ for i, part in enumerate(map(int, parts)):
+ version[i] = part
+ return tuple(version)
+
+
+if _HG_VERSION >= (2, 3, 0):
+ raise util.Abort(
+ 'kilnpath is deprecated, and does not work in Mercurial 2.3 or higher. '
+ 'Use the official schemes extension instead')
+
def urlcombine(pre, suff):
if pre.endswith("/"):
@@ -43,11 +69,13 @@ suff = "/" + suff
return pre + suff
+
def add_schemes(ui):
schemes = ui.configitems('kiln_scheme')
for scheme, prefix in schemes:
hg.schemes[scheme] = KilnRepo(scheme, prefix)
+
class KilnRepo(object):
def __init__(self, scheme, prefix):
self.scheme = scheme
@@ -70,9 +98,9 @@ # Mercurial <= 1.8
return hg._lookup(path_full).instance(ui, path_full, create)
+
def extsetup(*args):
if len(args) > 0:
add_schemes(args[0])
else:
add_schemes(ui.ui())
-
|
@@ -3,7 +3,8 @@ import zipfile
def compile_extensions():
- compileall.compile_dir(os.path.dirname(__file__), force=1)
+ curdir = os.path.dirname(os.path.abspath(__file__))
+ compileall.compile_dir(curdir, force=True)
def walk(dir_root):
for dirpath, dirnames, filenames in os.walk(dir_root):
|
Loading...