Changeset 105bd3eb46a4…
Parent 0b1d58ee5c34…
by Kevin Gessner <kevin@fogcreek.com>
Changes to 4 files · Browse files at 105bd3eb46a4 Showing diff from parent 0b1d58ee5c34 Diff from another changeset...
|
|
- # Copyright (C) 2009-2011 Fog Creek Software. All rights reserved.
+# Copyright (C) 2009-2012 Fog Creek Software. All rights reserved.
#
# To enable the "gestalt" extension put these lines in your ~/.hgrc:
# [extensions]
# gestalt = /path/to/gestalt.py
#
# For help on the usage of "hg gestalt" use:
# hg help gestalt
#
# 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
+# 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.
'''provides a general overview of your repository state
This extension attempts to help new Mercurial users by providing
several commands to help learn how Mercurial works. The primary
command provided is "hg next", which shows an overview of your
local repository, its relationship and status to its parent,
and what next actions you may wish to consider performing.
'''
import re
import mercurial.__version__
from mercurial import bundlerepo, changegroup, cmdutil, commands, demandimport, hg, node, url, util
from mercurial.i18n import _
from mercurial.error import RepoError
##
# Compatibility shims
# remoteui moved from cmdutil to hg in 1.6.
if hasattr(cmdutil, 'remoteui'):
remoteui = cmdutil.remoteui
else:
remoteui = hg.remoteui
# findoutgoing and findcommonincoming moved from localrepo to
# discovery in 1.6.
demandimport.disable()
try:
from mercurial import localrepo
findcommonincoming = localrepo.localrepository.findcommonincoming
findoutgoing = localrepo.localrepository.findoutgoing
except AttributeError:
from mercurial import discovery
findcommonincoming = discovery.findcommonincoming
try:
# Mercurial <= 1.8
findoutgoing = discovery.findoutgoing
except AttributeError:
# Mercurial >= 1.9
def findoutgoing(repo, remote, force=False):
common, _anyinc, _heads = discovery.findcommonincoming(repo, remote, force=force)
return repo.changelog.findmissing(common)
demandimport.enable()
@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 mercurial.__version__.version.split('.')[:3]]
for i, part in enumerate(map(int, parts)):
version[i] = part
return tuple(version)
def parseurl(source):
'''wrap hg.parseurl to work on 1.5 and 1.6
1.5 redefined parseurl()'s return values, and 1.6 split up the
branches parameter into a two-tuple.
'''
- url, branches = hg.parseurl(source, None)[:2]
+ uri, branches = hg.parseurl(source, None)[:2]
if _HG_VERSION >= (1, 6, 0):
# branches will be None because we passed None into
- # parseurl(), so we can ignore that safely.
+ # parseuri(), so we can ignore that safely.
hashbranch, branches = branches
else:
# branches will contain one element or fewer because we passed
- # None into parseurl().
+ # None into parseuri().
hashbranch = branches and branches[0] or None
- return url, hashbranch
+ 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
1.5 added the call. 1.6 split up the revs parameter into a
two-tuple.
'''
if _HG_VERSION < (1, 6, 0):
branches = hashbranch and [hashbranch] or []
revs, checkout = hg.addbranchrevs(lrepo, repo, branches, None)
else:
branches = hashbranch, []
revs, checkout = hg.addbranchrevs(lrepo, repo, branches, None)
return revs
##
# Private utility functions for determining advice output
def _isrepo(ui, repo, files, opts):
if not repo:
ui.status(_("""You need to create a Mercurial repository.
Run -> hg init
"""))
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:
ui.status(_('''You have changes in your working copy that should be committed
before updating your local or remote repositories:
Run -> hg commit
'''))
return True
return False
def _shouldmerge(ui, repo, files, opts):
heads = repo.branchheads(closed=False)
if len(heads) > 1:
ui.status(_('''You have two heads in your local repository. To resolve this,
you should merge:
Run -> hg merge
'''))
return True
return False
def _shouldsync(ui, repo, files, opts):
source, hashbranch = parseurl(ui.expandpath('default'))
# grab incoming and outgoing changesets
try:
other = hg.repository(remoteui(repo, opts), source)
except RepoError:
ui.status(_('''You have not set a default repository in your configuration file.
Edit your configuration file, .hg/hgrc, and add a default entry in
the [paths] section.
'''))
return True
revs = addbranchrevs(repo, other, hashbranch)
ui.pushbuffer()
common, incoming, rheads = findcommonincoming(repo, other, heads=revs, force=False)
ui.popbuffer()
if incoming:
ui.status(_('''There are changes in your remote repository that haven't been
included in your local repository. To get your copy up-to-date you should:
Run -> hg pull
'''))
return True
- target = ui.config('paths', 'default-push') and ui.expandpath('default-push') or source
source, hashbranch = parseurl(source)
other = hg.repository(remoteui(repo, opts), source)
revs = addbranchrevs(repo, other, hashbranch)
ui.pushbuffer()
outgoing = findoutgoing(repo, other, force=False)
if outgoing:
outgoing = repo.changelog.nodesbetween(outgoing, revs)[0]
ui.popbuffer()
if outgoing:
ui.status(_('''You have changes in your local repository that aren't in your
remote repository. If you want to share your changes, you should:
Run -> hg push
'''))
return True
return False
def _istip(ui, repo, files, opts):
tip = repo['tip']
cwd = repo['.']
if tip != cwd:
ui.status(_('''You are not at a head. You probably want to update to tip
before making any changes:
Run -> hg up
'''))
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):
'''return a list of outgoing changesets'''
out = findoutgoing(repo, origin)
if out:
out = repo.changelog.nodesbetween(out, None)[0]
return out
def incoming(repo, origin, revs):
'''return a list of incoming changesets'''
if revs:
revs = [origin.lookup(rev) for rev in revs]
common, incoming, rheads = findcommonincoming(repo, origin, heads=revs, force=False)
if not incoming:
return []
if not origin.local():
# create a bundle (uncompressed if other repo is not local)
if not revs and origin.capable('changegroupsubset'):
revs = rheads
if not revs:
cg = origin.changegroup(incoming, 'incoming')
else:
cg = origin.changegroupsubset(incoming, revs, 'incoming')
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):
'''provides a general overview of your repository state
This command combines the output of the hg incomng, hg outgoing,
hg status, and hg id commands into an easily human-readable explanation
of the entire state of your current working repository.
'''
if not repo:
return
originurl = ui.expandpath(source or 'default')
targeturl = ui.expandpath(source or 'default-push', source or 'default')
origin, hashbranch = parseurl(originurl)
try:
origin = hg.repository(remoteui(repo, opts), origin)
except RepoError:
return
target, hashbranch = parseurl(targeturl)
target = hg.repository(remoteui(repo, opts), target)
try:
# Mercurial >= 1.9
hidepassword = util.hidepassword
except AttributeError:
# Mercurial <= 1.8
hidepassword = url.hidepassword
if originurl == targeturl:
ui.status(_('parent repository: %s\n') % hidepassword(originurl))
else:
ui.status(_('source repository: %s\n') % hidepassword(getattr(origin, 'root', origin.url())))
ui.status(_('destination repository: %s\n') % hidepassword(getattr(target, 'root', target.url())))
ui.pushbuffer()
out = outgoing(repo, target)
inc = incoming(repo, origin, filter(bool, [hashbranch]))
ui.popbuffer()
changed = any(repo.status())
if changed:
status = _('uncommitted changes')
else:
status = _('working copy up-to-date')
# grab heads
heads = repo.branchheads(None, closed=False)
if len(heads) > 1:
merge = 'merge required'
else:
merge = ''
ui.status(_('| Remote | << %s | Local | %s\n') % (str(len(out)).center(5), merge))
ui.status(_('| Repository | %s >> | Repository | %s\n') % (str(len(inc)).center(5), status))
if opts['detail']:
if len(out) > 0:
ui.status(_('\noutgoing changes:\n'))
for rev in out:
ui.status('%s %s\n' % (repo[rev],
repo[rev].description().strip().split('\n')[0]))
if len(inc) > 0:
ui.status(_('\nincoming changes:\n'))
for rev in inc:
ui.status('%s %s\n' % (repo[rev],
repo[rev].description().strip().split('\n')[0]))
if changed:
ui.status(_('\nlocal files:\n'))
ui.pushbuffer()
commands.status(ui, repo, '', **opts)
status = ui.popbuffer()
for l in status.splitlines():
print ' %s' % l
def advice(ui, repo, *files, **opts):
'''provides a suggestion of your next step
This command attempts to help new Mercurial users by suggesting
what your next step should be. These steps are suggestions only,
and do not provide an exhaustive list of all possible actions that
may be appropriate, but should nevertheless help you if you are
unsure how to proceed.
'''
checks = [_isrepo,
_ismerging,
_haschanges,
_shouldmerge,
_shouldsync,
_istip,
_shouldwritemorecode]
for fun in checks:
if fun(ui, repo, files, opts):
return
def next(ui, repo, *files, **opts):
'''provides an overview and explanation of what to do next
This command shows you a graphical representation of the
current state of your repository and its parent, and suggests
what your next step should be based on the picture.'''
overview(ui, repo, *files, **opts)
advice(ui, repo, *files, **opts)
cmdtable = {
'overview':
(overview,
[('d', 'detail', None, _('provide verbose output'))],
_('hg gestalt [OPTION] [REMOTE REPOSITORY]')),
'advice':
(advice, [], _('hg next')),
'next|wtf':
(next,
[('d', 'detail', None, _('provide verbose output'))],
_('hg next [OPTION] [REMOTE REPOSITORY]')),
}
commands.optionalrepo += 'wtf advice overview next'
|
|
@@ -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
import unicodedata
@@ -573,7 +574,7 @@ return call_api(repo.ui, baseurl, 'Api/1.0/Repo/Create', params, post=True)
except APIError, e:
if 'RepoNameAlreadyUsed' in e.errors:
- repo.ui.write_err(_('error: a repo with this name already exists: %s\n') % name)
+ repo.ui.warn(_('error: kiln: a repo with this name already exists: %s\n') % name)
return
raise
@@ -612,13 +613,19 @@ reviewers.append(name_to_ix[reviewer])
print_list(ui, [ix_to_name[r] for r in reviewers], 'reviewers:')
-def review(ui, repo, pats, opts):
+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'):
return
- url = repo.ui.expandpath(pats[0] if pats else 'default-push', default='default')
+ url = repo.ui.expandpath(dest if dest else 'default-push')
+ if url == 'default-push':
+ raise util.Abort(_('kiln: please specify a default-push path before using --review'))
+
baseurl = get_api_url(url)
+ if baseurl == url:
+ ui.write_err(_('kiln: warning: this does not appear to be a Kiln URL: %s\n') % baseurl)
+
token = get_token(ui, baseurl)
kiln_repo = get_repo_record(repo, url, token)
@@ -731,6 +738,32 @@ '''
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
+ possible standin prefixes e.g. .hglf or .kbf in addition to the
+ originals'''
+ paths = [os.path.relpath(os.path.abspath(p), os.getcwd()) for p in paths]
+ 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'''
+ try:
+ match = scmutil.match(ctx, paths)
+ match.bad = lambda *a: None
+ paths = set(ctx.walk(match))
+ return paths
+ except ImportError:
+ # Make every path normalized and relative to the current
+ # working directory, similar to scmutil.
+ needles = set(map(os.path.normpath, paths))
+ haystacks = [os.path.relpath(os.path.join(repo.root, p), os.getcwd())
+ for p in ctx.manifest().iterkeys()]
+ 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
@@ -760,14 +793,20 @@ default = True
def files(key):
- allpaths = []
- for f in opts[key]:
- paths = [path for path in repo['.'].manifest().iterkeys() if re.search(match._globre(f) + '$', path)]
- paths = [re.sub(r'^\.kbf', '', path) for path in paths]
- if not paths:
- ui.warn(_('cannot find %s') % f)
- allpaths += paths
- return allpaths
+ paths = _filename_match(repo, repo['.'], _standin_expand(opts[key]))
+ if not paths:
+ ui.warn(_('error: kiln: cannot find any paths matching %s\n') % ', '.join(opts[key]))
+ if len(paths) > 5:
+ # If we're passed a directory, we should technically open
+ # a tab for each file in that directory because that's how
+ # other hg commands e.g. cat work. However, since that's
+ # quite annoying to do by accident when opening browsers,
+ # let's prompt. (This is only relevant when scmutil
+ # exists.)
+ char = ui.prompt(_('about to open %d browser tabs or windows, abort? [Yn]') % len(paths)).lower()
+ if char != 'n':
+ raise SystemExit(0)
+ return paths
if opts['rev']:
default = False
|
@@ -90,10 +90,8 @@ if before != after:
try:
os.rename(self.__temporary_path, self.__original_path)
- except WindowsError:
+ except (IOError, OSError, WindowsError):
shutil.copyfile(self.__temporary_path, self.__original_path)
- except (IOError, OSError):
- pass
def get_cookiejar(ui):
global current_user
@@ -117,13 +115,18 @@ try:
cj = CookieJar(cookie_path)
if not os.path.exists(cookie_path):
- cj.save()
+ try:
+ cj.save()
+ except Exception, e:
+ ui.warn(_('unable to save cookies: %s') % str(e))
+ # save() destroys the tempfile; get a new one
+ cj = CookieJar(cookie_path)
if os.name == 'posix':
os.chmod(cookie_path, stat.S_IREAD | stat.S_IWRITE)
cj.load(ignore_discard=True, ignore_expires=True)
return cj
- except IOError:
- ui.warn(_('Cookie file %s exists, but could not be opened.\nContinuing without cookie authentication.\n') % cookie_path)
+ except IOError, e:
+ ui.warn(_('Cookie file %s exists, but could not be opened (%s).\nContinuing without cookie authentication.\n') % (cookie_path, e))
return MozillaCookieJar(tempfile.NamedTemporaryFile().name)
def make_cookie(request, name, value):
@@ -193,7 +196,10 @@ cj.add_cookie_header(request)
response = func(*args, **kwargs)
cj.extract_cookies(response, args[0])
- cj.save(ignore_discard=True, ignore_expires=True)
+ try:
+ cj.save(ignore_discard=True, ignore_expires=True)
+ except Exception, e:
+ ui.warn(_('unable to save cookies: %s') % str(e))
else:
response = func(*args, **kwargs)
return response
|
@@ -2,50 +2,41 @@ import os
import zipfile
-folders = ['bfiles', '_custom']
-extensions = ['.py']
-excludes = ['setup.py']
-
def compile_extensions():
compileall.compile_dir(os.path.dirname(__file__), force=1)
-def check_item(item):
- for exclude in excludes:
- if item[1].endswith(exclude):
- return False
- if item[1] in folders:
- return True
- for extension in extensions:
- if item[1].endswith(extension):
- return True
- return False
+def walk(dir_root):
+ for dirpath, dirnames, filenames in os.walk(dir_root):
+ dirnames_set = set(dirnames)
+ if 'tests' in dirnames_set:
+ dirnames.remove('tests')
-def list_files(dir_root, zip_root):
- subdirs = []
- items = []
- for item in os.listdir(dir_root):
- path = os.path.join(dir_root, item)
- zip_path = os.path.join(zip_root, item)
- if os.path.isfile(path):
- items.append([path, zip_path])
- else:
- subdirs.append([path, zip_path])
- for subdir in subdirs:
- items.extend(list_files(subdir[0], subdir[1]))
- return [f for f in items if check_item(f)]
+ for filename in filenames:
+ ignore, ext = os.path.splitext(filename)
+ if filename == 'setup.py':
+ continue
+ if ext not in ('.py', '.pyc'):
+ continue
+ yield os.path.join(dirpath, filename)
def build_release():
- compile_extensions()
- dir = os.path.dirname(__file__)
- absdir = os.path.abspath(dir)
- files = list_files(absdir, '.')
+ absdir = os.path.abspath(os.path.dirname(__file__))
+ target = os.path.join(absdir, 'kiln_extensions.zip')
print 'Creating ZIP archive...'
- zip = zipfile.ZipFile(os.path.join(absdir, 'kiln_extensions.zip'), 'w')
- for file in files:
- zip.write(file[0], file[1])
- zip.close()
+ f = None
+ try:
+ f = zipfile.ZipFile(target, 'w')
+ for filename in walk(absdir):
+ zip_filename = os.path.join(
+ 'kiln_extensions',
+ os.path.relpath(filename, absdir))
+ print ' %s' % zip_filename
+ f.write(filename, zip_filename)
+ finally:
+ if f: f.close()
print 'Success!'
if __name__ == '__main__':
+ compile_extensions()
build_release()
|
Loading...