initial release

This commit is contained in:
Josh Holtrop 2012-03-22 14:00:57 -04:00
commit bb97b312e6
2 changed files with 891 additions and 0 deletions

87
README Normal file
View File

@ -0,0 +1,87 @@
Josh's SVN wrapper script
This wrapper script to Subversion supplements normal svn behavior by
adding additional functionality or modifying the output of the default
svn subcommands. Much of the functionality implemented here was inspired
by the way that git works.
It is recommended to put this script in your $PATH as 'jsvn' or something
other than 'svn' and to make an alias svn='jsvn'.
For example, this .bash_aliases stanza will check if 'jsvn' is in your
path and automatically alias 'svn' to it if so:
if [[ "$(which jsvn 2>/dev/null)" != "" ]]; then
alias svn='jsvn'
fi
Implemented subcommands:
add <file>...
- add files as usual; add recursive contents of directories
branch[es] [[-d] <branch_name>]
- with no arguments, list branches with '*' by the current one
- with -d, delete <branch>
- otherwise, create a new branch from the current one
tag[s] [[-d] <tag_name>] | [-m <old_tag_name> <new_tag_name>]
- with no arguments, list tags
- with -d, delete <tag>
- with -m, rename <old_tag_name> to <new_tag_name>
- otherwise, create a new tag from the current branch
switch <short_name>
- switch to 'trunk', branch name, or tag name without having to specify
the full URL
- falls back to Subversion "switch" if <short_name> doesn't exist
merge <branch>
- merge branch <branch> into the current WC path
- falls back to Subversion "merge" if <branch> doesn't exist
root
- output root URL (for use on shell such as "svn log $(svn root)/tags")
url
- output repository URL of current working directory
watch-lock
- block until the lock on a file/URL is released
users
- show a list of contributing users to a SVN path
binaries [--set-lock]
- show a list of versioned binary files under the current path, with
a prepended '*' for those with svn:needs-lock set
- with --set-lock, set svn:needs-lock to '*' for binaries
lockable [--remove] | [--status] <file[s]>
- with no switches, set svn:needs-lock to '*' for file[s]
- with --remove, remove svn:needs-lock' for file[s]
- with --status, prepended '*' for those with svn:needs-lock set
externals
- print a list of the externals in the repository
The following subcommands are executed using their native handler, but
have their output simplified and/or colorized:
- diff
- log
- status
- update
If the subcommand name begins with two leading underscores ("__"), the
underscores will be stripped and the command will be handled by native
Subversion without any jsvn processing.
Configuration:
jsvn will execute the file ~/.jsvn, if it exists, as a Python script.
Variables written to will be used as configuration directives.
Available configuration directives:
use_color: True or False to enable/disable colorization of svn output
use_pager: True or False to enable/disable automatic piping of svn
output to a pager program
pager: A string specifying the pager program (and args) to execute
aliases['XXX']: A string or list defining the alias 'XXX'. A string
can be used if the alias expands to a single argument. A
list must be used to pass multiple arguments to svn.
Configuration Examples:
pager = 'less -FRXi' # enable case-insensitive searching in less
aliases['revert'] = ['revert', '-R'] # default to recursive reverts
aliases['s'] = ['status', '--ignore-externals']
aliases['status'] = '__status' # ignore jsvn processing of status command
Author: Josh Holtrop
History:
v1.0 - functional release on github

804
jsvn Executable file
View File

@ -0,0 +1,804 @@
#!/usr/bin/env python
# Josh's SVN wrapper script
#
# This wrapper script to Subversion supplements normal svn behavior by
# adding additional functionality or modifying the output of the default
# svn subcommands. Much of the functionality implemented here was inspired
# by the way that git works.
#
# It is recommended to put this script in your $PATH as 'jsvn' or something
# other than 'svn' and to make an alias svn='jsvn'.
# For example, this .bash_aliases stanza will check if 'jsvn' is in your
# path and automatically alias 'svn' to it if so:
# if [[ "$(which jsvn 2>/dev/null)" != "" ]]; then
# alias svn='jsvn'
# fi
#
# Implemented subcommands:
# add <file>...
# - add files as usual; add recursive contents of directories
# branch[es] [[-d] <branch_name>]
# - with no arguments, list branches with '*' by the current one
# - with -d, delete <branch>
# - otherwise, create a new branch from the current one
# tag[s] [[-d] <tag_name>] | [-m <old_tag_name> <new_tag_name>]
# - with no arguments, list tags
# - with -d, delete <tag>
# - with -m, rename <old_tag_name> to <new_tag_name>
# - otherwise, create a new tag from the current branch
# switch <short_name>
# - switch to 'trunk', branch name, or tag name without having to specify
# the full URL
# - falls back to Subversion "switch" if <short_name> doesn't exist
# merge <branch>
# - merge branch <branch> into the current WC path
# - falls back to Subversion "merge" if <branch> doesn't exist
# root
# - output root URL (for use on shell such as "svn log $(svn root)/tags")
# url
# - output repository URL of current working directory
# watch-lock
# - block until the lock on a file/URL is released
# users
# - show a list of contributing users to a SVN path
# binaries [--set-lock]
# - show a list of versioned binary files under the current path, with
# a prepended '*' for those with svn:needs-lock set
# - with --set-lock, set svn:needs-lock to '*' for binaries
# lockable [--remove] | [--status] <file[s]>
# - with no switches, set svn:needs-lock to '*' for file[s]
# - with --remove, remove svn:needs-lock' for file[s]
# - with --status, prepended '*' for those with svn:needs-lock set
# externals
# - print a list of the externals in the repository
#
# The following subcommands are executed using their native handler, but
# have their output simplified and/or colorized:
# - diff
# - log
# - status
# - update
#
# If the subcommand name begins with two leading underscores ("__"), the
# underscores will be stripped and the command will be handled by native
# Subversion without any jsvn processing.
#
# Configuration:
#
# jsvn will execute the file ~/.jsvn, if it exists, as a Python script.
# Variables written to will be used as configuration directives.
# Available configuration directives:
# use_color: True or False to enable/disable colorization of svn output
# use_pager: True or False to enable/disable automatic piping of svn
# output to a pager program
# pager: A string specifying the pager program (and args) to execute
# aliases['XXX']: A string or list defining the alias 'XXX'. A string
# can be used if the alias expands to a single argument. A
# list must be used to pass multiple arguments to svn.
#
# Configuration Examples:
# pager = 'less -FRXi' # enable case-insensitive searching in less
# aliases['revert'] = ['revert', '-R'] # default to recursive reverts
# aliases['s'] = ['status', '--ignore-externals']
# aliases['status'] = '__status' # ignore jsvn processing of status command
#
# Author: Josh Holtrop
#
# History:
# v1.0 - functional release on github
import sys
import os
import re
import time
from subprocess import *
import traceback
STATUS_LINE_REGEX = r'[ACDIMRX?!~ ][CM ][L ][+ ][SX ][KOTB ]'
###########################################################################
# Subcommand Handler Return Values #
###########################################################################
RET_OK = 0
RET_ERR = 1
RET_REEXEC = 2
###########################################################################
# ANSI escape color code values #
###########################################################################
COLORS = {
'black': 0,
'red': 1,
'green': 2,
'yellow': 3,
'blue': 4,
'magenta': 5,
'cyan': 6,
'white': 7,
}
using_color = False
###########################################################################
# Configuration #
###########################################################################
def get_config():
config = {
'pager': 'less -FRX',
'use_pager': True,
'use_color': True,
'aliases': {},
'svn': '',
}
pth = os.path.expanduser('~/.jsvn')
if os.path.exists(pth):
fh = open(pth, 'r')
script = fh.read()
fh.close()
try:
exec(script, config)
except:
sys.stderr.write('Configuration file error in "%s":\n' % pth)
traceback.print_exception(sys.exc_info()[0], sys.exc_info()[1],
None)
tb = traceback.extract_tb(sys.exc_info()[2])
for ent in tb[1:]:
lineno, fn = ent[1:3]
sys.stderr.write(' File "%s", line %d, in %s\n'
% (pth, lineno, fn))
return config
def apply_aliases(config, argv):
if not argv[0] in config['aliases']:
return argv
alias = config['aliases'][argv[0]]
if type(alias) == str:
return [alias] + argv[1:]
elif type(alias) == list:
return alias + argv[1:]
sys.stderr.write('Unsupported type for alias "%s"\n' % alias)
return argv
###########################################################################
# Utility Functions #
###########################################################################
def ansi_color(out, fg=None, bg=None, bold=False):
if using_color:
bc = 1 if bold else 0
if fg is not None:
out.write('\033[%d;%dm' % (bc, 30 + COLORS[fg]))
if bg is not None:
out.write('\033[%d;%dm' % (bc, 40 + COLORS[bg]))
def ansi_reset(out):
if using_color:
out.write('\033[0m')
def colordiff(out, line):
if re.match(r'Index:\s', line):
ansi_color(out, 'yellow')
out.write(line)
ansi_reset(out)
return
if re.match(r'={67}', line):
ansi_color(out, 'yellow')
out.write(line)
ansi_reset(out)
return
if re.match(r'-', line):
ansi_color(out, 'red')
out.write(line)
ansi_reset(out)
return
elif re.match(r'\+', line):
ansi_color(out, 'green')
out.write(line)
ansi_reset(out)
return
m = re.match(r'(@@.*@@)(.*)', line)
if m is None:
m = re.match(r'(##.*##)(.*)', line)
if m is not None:
ansi_color(out, 'cyan')
out.write(m.group(1))
ansi_reset(out)
out.write(m.group(2))
out.write('\n')
return
out.write(line)
def findInPath(cmd):
path_entries = os.environ['PATH'].split(os.pathsep)
for p in path_entries:
full_path = os.path.join(p, cmd)
if os.path.exists(full_path):
return full_path
return ''
def getSVNURL(svn):
for line in Popen([svn, 'info'], stdout=PIPE).communicate()[0].split('\n'):
m = re.match(r'^URL:\s*(.*?)\s*$', line)
if m is not None:
return m.group(1)
return ''
def getSVNRoot(svn):
url = getSVNURL(svn)
parts = url.split('/')
for i in range(0, len(parts)):
if parts[i] in ('trunk', 'tags', 'branches'):
return '/'.join(parts[:i])
return ''
def getSVNRelPath(svn):
url = getSVNURL(svn)
parts = url.split('/')
for i in range(0, len(parts) - 1):
if parts[i] == 'trunk' or i > 0 and parts[i-1] in ('tags', 'branches'):
return '/' + '/'.join(parts[i+1:])
return '/'
def getSVNTopLevel(svn):
url = getSVNURL(svn)
parts = url.split('/')
for i in range(0, len(parts)):
if parts[i] == 'trunk' or i > 0 and parts[i-1] in ('tags', 'branches'):
return '/'.join(parts[:i+1])
return ''
def getSVNBranchList(svn):
colist = []
root = getSVNRoot(svn)
lines = Popen([svn, 'ls', root + '/branches'],
stdout=PIPE, stderr=PIPE).communicate()[0].split('\n')
for line in lines:
if re.match(r'^\s*$', line) is None:
colist.append(re.sub(r'/$', '', line))
return colist
def getSVNTagList(svn):
colist = []
root = getSVNRoot(svn)
lines = Popen([svn, 'ls', root + '/tags'],
stdout=PIPE, stderr=PIPE).communicate()[0].split('\n')
for line in lines:
if re.match(r'^\s*$', line) is None:
colist.append(re.sub(r'/$', '', line))
return colist
def getSVNProperty(svn, prop, path):
return Popen([svn, 'propget', prop, path], stdout=PIPE).communicate()[0]
def setSVNProperty(svn, prop, val, path):
Popen([svn, 'propset', prop, val, path], stdout=PIPE).wait()
def delSVNProperty(svn, prop, path):
Popen([svn, 'propdel', prop, path], stdout=PIPE).wait()
def filter_update(pout, out):
external = ''
external_printed = True
any_external_printed = False
for line in iter(pout.readline, ''):
m = re.match(r"Fetching external item into '(.*)':", line)
if m is not None:
external = m.group(1)
external_printed = False
continue
if re.match(r'\s*$', line):
continue
if re.match(r'External at revision ', line):
if external_printed:
out.write(line)
continue
if re.match(r'(Updated.to|At) revision', line):
if any_external_printed:
out.write('\n')
out.write(line)
continue
# anything not matched yet will cause an external to be shown
if not external_printed:
out.write("\nExternal '%s':\n" % external)
external_printed = True
any_external_printed = True
if re.match(r'[ADUCGER ]{2}[B ][C ] ', line):
action = line[0]
if action == 'A':
ansi_color(out, 'green')
elif action == 'D':
ansi_color(out, 'red')
elif action == 'C':
ansi_color(out, 'yellow')
elif action == 'G':
ansi_color(out, 'cyan')
out.write(line)
ansi_reset(out)
continue
out.write(line)
def get_unknowns(svn):
unknowns = []
pout = Popen([svn, 'status'], stdout=PIPE).stdout
for line in iter(pout.readline, ''):
m = re.match(r'\? (.*)$', line)
if m is not None:
unknowns.append(m.group(1))
return unknowns
def descendant_path(child, parent):
if child[0] != '/' and parent[0] == '/':
child = os.getcwd() + '/' + child
elif child[0] == '/' and parent[0] != '/':
parent = os.getcwd() + '/' + parent
if child == parent:
return True
if child.startswith(parent):
if child[len(parent)] == '/':
return True
return False
###########################################################################
# Subcommand Handlers #
###########################################################################
def add(argv, svn, out):
if len(argv) < 2:
# do not handle if no targets are passed
return RET_REEXEC
if len(filter(lambda x: x.startswith('-'), argv)) != 0:
# do not handle if any options are passed
return RET_REEXEC
# for each target specified, check if there are unversioned items
# underneath it (for directories) and add them as well
# if none are found, fall back to the native svn add
unknowns = get_unknowns(svn)
for path in argv[1:]:
if path == '.':
path = os.getcwd()
if path.endswith('/'):
path = path[:-1]
found_one = False
for u in unknowns:
if descendant_path(u, path):
Popen([svn, 'add', u], stdout=out).wait()
found_one = True
if not found_one:
Popen([svn, 'add', path], stdout=out).wait()
return RET_OK
def branch(argv, svn, out):
origin = getSVNTopLevel(svn)
root = getSVNRoot(svn)
if origin == '' or root == '':
sys.stderr.write("Could not determine origin/root URL\n")
return RET_ERR
if len(argv) < 2:
bl = ['trunk'] + getSVNBranchList(svn)
current = getSVNTopLevel(svn).split('/')[-1]
bl.sort()
for b in bl:
if b == current:
out.write('*')
ansi_color(out, 'green')
else:
out.write(' ')
out.write(b + '\n')
if b == current:
ansi_reset(out)
return RET_OK
branch_name = argv[-1]
if len(argv) >= 3 and argv[1] == "-d":
# delete branch in argv[2]
Popen([svn, 'rm', root + '/branches/' + argv[2], '-m',
"Removed branch '%s'" % branch_name], stdout=out).wait()
return RET_OK
comment = "Created '%s' branch" % branch_name
branch_path = root + '/branches/' + branch_name
Popen([svn, 'copy', origin, branch_path, '-m', comment], stdout=out).wait()
return RET_OK
def tag(argv, svn, out):
origin = getSVNTopLevel(svn)
root = getSVNRoot(svn)
if origin == '' or root == '':
sys.stderr.write("Could not determine origin/root URL\n")
return RET_ERR
tl = getSVNTagList(svn)
if len(argv) < 2:
tl.sort()
for t in tl:
out.write(t + '\n')
return RET_OK
tag_name = argv[-1]
if len(argv) == 4 and argv[1] == '-m':
old_tag_name = argv[2]
if not old_tag_name in tl:
sys.stderr.write('Tag %s not found!\n' % old_tag_name)
return RET_ERR
Popen([svn, 'mv',
root + '/tags/' + old_tag_name, root + '/tags/' + tag_name,
'-m', "Renamed tag '%s' to '%s'" % (old_tag_name, tag_name)],
stdout=out).wait()
return RET_OK
if len(argv) >= 3 and argv[1] == "-d":
if not tag_name in tl:
sys.stderr.write('Tag %s not found!\n' % tag_name)
return RET_ERR
# delete tag in argv[2]
Popen([svn, 'rm', root + '/tags/' + tag_name, '-m',
"Removed tag '%s'" % tag_name], stdout=out).wait()
return RET_OK
comment = "Created '%s' tag" % tag_name
tag_path = root + '/tags/' + tag_name
Popen([svn, 'copy', origin, tag_path, '-m', comment], stdout=out).wait()
return RET_OK
def switch(argv, svn, out):
if len(argv) < 2:
return RET_REEXEC
switched = False
root = getSVNRoot(svn)
path = getSVNRelPath(svn)
while True:
if argv[1] == 'trunk':
pout = Popen([svn, 'switch', root + '/trunk' + path],
stdout=PIPE).stdout
filter_update(pout, out)
switched = True
break
bl = getSVNBranchList(svn)
if argv[1] in bl:
pout = Popen([svn, 'switch', root + '/branches/' + argv[1] + path],
stdout=PIPE).stdout
filter_update(pout, out)
switched = True
break
tl = getSVNTagList(svn)
if argv[1] in tl:
pout = Popen([svn, 'switch', root + '/tags/' + argv[1] + path],
stdout=PIPE).stdout
filter_update(pout, out)
switched = True
break
# argument is not a tag/branch name
break
if switched:
url = getSVNURL(svn)
out.write('URL: %s\n' % url)
return RET_OK
pout = Popen([svn] + argv, stdout=PIPE).stdout
filter_update(pout, out)
return RET_OK
def merge(argv, svn, out):
if len(argv) < 2:
return RET_REEXEC
root = getSVNRoot(svn)
branches = getSVNBranchList(svn)
if not argv[1] in branches:
return RET_REEXEC
lines = Popen([svn, 'log', '--stop-on-copy', root + '/branches/' + argv[1]],
stdout=PIPE).communicate()[0].split('\n')
rev = 0
for line in lines:
m = re.match(r'^r(\d+)\s', line)
if m is not None:
rev = m.group(1)
if rev == 0:
sys.stderr.write('Could not get first branch revision\n')
return RET_ERR
path = getSVNRelPath(svn)
Popen([svn, 'merge', '-r%s:HEAD' % rev,
root + '/branches/' + argv[1] + path, '.'], stdout=out).wait()
return RET_OK
def watch_lock(argv, svn, out):
if len(argv) < 2:
return RET_ERR
path = argv[1]
if os.path.exists(path):
# Get the repository URL of the file being watched
p = Popen([svn, 'info', path], stdout=PIPE)
lines = p.communicate()[0].split('\n')
for line in lines:
m = re.match(r'URL: (.*)', line)
if m is not None:
path = m.group(1)
break
last_lock_owner = ''
while True:
lock_owner = ''
p = Popen([svn, 'info', path], stdout=PIPE)
lines = p.communicate()[0].split('\n')
for line in lines:
m = re.match(r'Lock\sOwner:\s*(.*)', line)
if m is not None:
lock_owner = m.group(1)
break
if lock_owner == '':
break
if lock_owner != last_lock_owner:
out.write('Locked by: %s\n' % lock_owner)
last_lock_owner = lock_owner
time.sleep(60)
out.write('''
_ _ _ _ _ _
| | | |_ __ | | ___ ___| | _____ __| | |
| | | | '_ \| |/ _ \ / __| |/ / _ \/ _` | |
| |_| | | | | | (_) | (__| < __/ (_| |_|
\___/|_| |_|_|\___/ \___|_|\_\___|\__,_(_)
''')
return RET_OK
def users(argv, svn, out):
path = '.'
if len(argv) > 1:
path = argv[1]
users = {}
p = Popen([svn, 'log', '-q', path], stdout=PIPE)
for line in iter(p.stdout.readline, ''):
m = re.match('r\d+\s*\|([^|]+)\|', line)
if m is not None:
user = m.group(1).strip()
if not user.lower() in users:
users[user.lower()] = [user, 1]
else:
users[user.lower()][1] += 1
values = users.values()
values.sort(key = lambda x: x[1], reverse = True)
for v in values:
out.write("%8d %s\n" % (v[1], v[0]))
return RET_OK
def binaries(argv, svn, out, base_path = '.'):
for ent in os.listdir(base_path):
if ent in ('.', '..', '.svn'):
continue
ent_path = os.sep.join([base_path, ent])
if os.path.isfile(ent_path):
mime_type = getSVNProperty(svn, 'svn:mime-type', ent_path)
if mime_type != '' and not re.match(r'text/.*', mime_type):
# we found a binary file
needs_lock = getSVNProperty(svn, 'svn:needs-lock', ent_path)
if needs_lock:
out.write('* ')
elif len(argv) >= 2 and argv[1] == '--set-lock':
setSVNProperty(svn, 'svn:needs-lock', '*', ent_path)
out.write('S ')
else:
out.write(' ')
out.write(ent_path)
out.write('\n')
elif os.path.isdir(ent_path):
binaries(argv, svn, out, os.sep.join([base_path, ent]))
return RET_OK
def lockable(argv, svn, out):
if len(argv) >= 2 and argv[1] == '--status':
for ob in argv[2:]:
ob_path = os.sep.join([base_path, ob])
needs_lock = getSVNProperty(svn, 'svn:needs-lock', ob_path)
if needs_lock:
out.write('* ')
else:
out.write(' ')
out.write(ob_path)
out.write('\n')
elif len(argv) >= 2 and argv[1] == '--remove':
for ob in argv[2:]:
ob_path = os.sep.join([base_path, ob])
delSVNProperty(svn, 'svn:needs-lock', ob_path)
else:
# note this is the default assumed operation
for ob in argv[1:]:
ob_path = os.sep.join([base_path, ob])
setSVNProperty(svn, 'svn:needs-lock', '*', ob_path)
return RET_OK
def diff(argv, svn, out):
pout = Popen([svn] + argv, stdout=PIPE).stdout
for line in iter(pout.readline, ''):
colordiff(out, line)
return RET_OK
def log(argv, svn, out):
mode = 'normal'
pout = Popen([svn] + argv, stdout=PIPE).stdout
for line in iter(pout.readline, ''):
if mode == 'normal' and re.match(r'(r\d+)\s+\|', line):
parts = line.split('|')
if len(parts) == 4:
ansi_color(out, 'blue', bold=True)
out.write(parts[0])
ansi_reset(out)
out.write('|')
ansi_color(out, 'cyan')
out.write(parts[1])
ansi_reset(out)
out.write('|')
ansi_color(out, 'magenta')
out.write(parts[2])
ansi_reset(out)
out.write('|')
out.write(parts[3])
else:
out.write(line)
elif mode == 'normal' and re.match(r'Changed.paths:', line):
out.write(line)
mode = 'cp'
elif mode == 'cp' and re.match(r' [ADM] ', line):
action = line[3]
if action == 'A':
ansi_color(out, 'green')
elif action == 'D':
ansi_color(out, 'red')
elif action == 'M':
ansi_color(out, 'yellow')
out.write(line)
ansi_reset(out)
elif re.match(r'-{72}', line):
ansi_color(out, 'yellow')
out.write(line)
ansi_reset(out)
mode = 'normal'
elif re.match(r'={67}', line):
ansi_color(out, 'yellow')
out.write(line)
ansi_reset(out)
mode = 'diff'
elif mode == 'diff':
colordiff(out, line)
elif re.match(r'Index:\s', line):
ansi_color(out, 'yellow')
out.write(line)
ansi_reset(out)
else:
out.write(line)
return RET_OK
def update(argv, svn, out):
pout = Popen([svn] + argv, stdout=PIPE).stdout
filter_update(pout, out)
return RET_OK
def status(argv, svn, out):
external = ''
external_printed = True
pout = Popen([svn] + argv, stdout=PIPE).stdout
for line in iter(pout.readline, ''):
m = re.match(r"Performing status on external item at '(.*)':", line)
if m is not None:
external = m.group(1)
external_printed = False
continue
if re.match(r'\s*$', line):
continue
# anything not matched yet will cause an external to be shown
if not external_printed:
out.write("\nExternal '%s':\n" % external)
external_printed = True
if re.match(STATUS_LINE_REGEX, line):
action = line[0]
if action == 'A' or action == 'M':
ansi_color(out, 'green')
elif action == 'C':
ansi_color(out, 'yellow')
elif action == 'D':
ansi_color(out, 'red')
elif action == 'R':
ansi_color(out, 'magenta')
elif action == 'X':
continue # don't print externals
out.write(line)
ansi_reset(out)
continue
out.write(line)
return RET_OK
def externals(argv, svn, out):
pout = Popen([svn, 'status'], stdout=PIPE).stdout
for line in iter(pout.readline, ''):
if re.match(STATUS_LINE_REGEX, line):
if line[0] == 'X':
out.write(line[8:])
return RET_OK
def root(argv, svn, out):
out.write(getSVNRoot(svn) + '\n')
return RET_OK
def url(argv, svn, out):
out.write(getSVNURL(svn) + '\n')
return RET_OK
###########################################################################
# Main #
###########################################################################
def main(argv):
global using_color
config = get_config()
realsvn = config['svn'] if config['svn'] != '' else findInPath('svn')
out = sys.stdout
argv = apply_aliases(config, argv)
using_pager = False
using_color = sys.stdout.isatty() and config['use_color']
if sys.stdout.isatty() and config['use_pager']:
if (len(argv) >= 1 and argv[0] in
('blame', 'praise', 'annotate', 'ann',
'cat',
'diff', 'di',
'help',
'list', 'ls',
'log',
'propget', 'pget', 'pg',
'proplist', 'plist', 'pl')):
pager = config['pager']
if 'PAGER' in os.environ and os.environ['PAGER'] != '':
pager = os.environ['PAGER']
pager_proc = Popen(pager, shell=True, stdin=PIPE)
out = pager_proc.stdin
using_pager = True
if realsvn == '':
sys.stderr.write("Error: 'svn' not found in path\n")
return 1
handlers = {
'add': add,
'branch': branch,
'branches': branch,
'externals': externals,
'switch': switch,
'sw': switch,
'merge': merge,
'tag': tag,
'tags': tag,
'diff': diff,
'di': diff,
'log': log,
'root': root,
'up': update,
'update': update,
'url' : url,
'watch-lock': watch_lock,
'users': users,
'binaries': binaries,
'lockable': lockable,
'st': status,
'stat': status,
'status': status,
}
do_normal_exec = True
if len(argv) >= 1:
if argv[0] in handlers:
r = handlers[argv[0]](argv, realsvn, out)
if r == RET_OK or r == RET_ERR:
do_normal_exec = False
elif argv[0].startswith('__'):
# allow double-underscore commands to execute the native
# subversion command (e.g. "__st")
argv[0] = argv[0][2:]
if do_normal_exec:
Popen([realsvn] + argv, stdout=out).wait()
if using_pager:
out.close()
pager_proc.wait()
return 0
if __name__ == "__main__":
rc = 0
try:
rc = main(sys.argv[1:])
except IOError:
pass
sys.exit(rc)