diff --git a/README b/README index dbb6a34..ac7a97d 100644 --- a/README +++ b/README @@ -2,8 +2,8 @@ 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. +svn subcommands (simplification and colorization). 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'. @@ -27,6 +27,11 @@ Implemented subcommands: - with -d, delete - otherwise, create a new branch from the current one - also switch to the new branch if -s is given + diff + - allow specifying ref1..ref2 syntax to show the diff between two references + - references can be tag names, branch names, or 'trunk' + - allow specifying ref1...ref2 syntax to show the diff between the common + ancestor of ref1 and ref2 and ref2's HEAD tag[s] [[-d] ] | [-m ] - with no arguments, list tags - with -d, delete @@ -36,6 +41,9 @@ Implemented subcommands: - switch to 'trunk', branch name, or tag name without having to specify the full URL - falls back to Subversion "switch" if doesn't exist + log + - allow specifying ref1..ref2 syntax to show the log entries for + ref2 which are not a part of ref1 merge - merge branch into the current WC path - falls back to Subversion "merge" if doesn't exist @@ -79,13 +87,6 @@ Implemented subcommands: - delete stash object - defaults to the newest stash object created -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. diff --git a/jsvn b/jsvn index 615bacd..23c3e60 100755 --- a/jsvn +++ b/jsvn @@ -320,6 +320,39 @@ def get_next_stash_idx(svn): idx = stash_ids[-1] + 1 return idx +# return (URL, path) tuple containing the full URL to the ref and the +# repo-relative path (ex: /branches/XXX) +# if ref is an actual file, URL is returned as an empty string +# tags resolve before branches +def resolve_reference(svn, ref): + if os.path.exists(ref): + return ('', ref) + if ref == 'trunk': + return (get_svn_root_url(svn) + '/trunk', '/trunk') + tl = get_svn_tag_list(svn) + if ref in tl: + path = '/tags/' + ref + return (get_svn_root_url(svn) + path, path) + bl = get_svn_branch_list(svn) + if ref in bl: + path = '/branches/' + ref + return (get_svn_root_url(svn) + path, path) + # ref was not an actual file, 'trunk', a tag name, or a branch name + return ('', ref) + +def find_branched_revision(svn, branch_url, branch_path, base_path): + p = Popen([svn, 'log', '-v', branch_url], stdout=PIPE) + search_path = branch_path + for line in iter(p.stdout.readline, ''): + m = re.match('\s+A\s+(.*?)\s+\(from\s+(.*?):(\d+)\)\s*$', line) + if m is not None: + new_path, old_path, rev = m.group(1, 2, 3) + if new_path == search_path: + if old_path == base_path: + return int(rev) + search_path = old_path + return -1 + ########################################################################### # Subcommand Handlers # ########################################################################### @@ -689,12 +722,50 @@ def lockable(argv, svn, out): return RET_OK def diff(argv, svn, out): + for i, v in enumerate(argv): + m = re.match('(.*?)(\.\.\.?)(.*)$', v) + if m is not None: + ref1, operator, ref2 = m.group(1, 2, 3) + url1, path1 = resolve_reference(svn, ref1) + if url1 == '': + continue + url2, path2 = resolve_reference(svn, ref2) + if url2 == '': + continue + if operator == '...': + # amend url1 to include the pegged revision from where ref2 + # originally branched from it + r = find_branched_revision(svn, url2, path2, path1) + if r < 0: + sys.stderr.write(('Could not find revision where "%s" ' + + 'branched from "%s"\n') % (ref2, ref1)) + return RET_ERR + url1 += '@%d' % r + argv = argv[:i] + [url1, url2] + argv[i + 1:] + break pout = Popen([svn] + argv, stdout=PIPE).stdout for line in iter(pout.readline, ''): colordiff(out, line) return RET_OK def log(argv, svn, out): + for i, v in enumerate(argv): + m = re.match('(.*)(\.\.)(.*)$', v) + if m is not None: + ref1, operator, ref2 = m.group(1, 2, 3) + url1, path1 = resolve_reference(svn, ref1) + if url1 == '': + continue + url2, path2 = resolve_reference(svn, ref2) + if url2 == '': + continue + r = find_branched_revision(svn, url2, path2, path1) + if r < 0: + sys.stderr.write(('Could not find revision where "%s" ' + + 'branched from "%s"\n') % (ref2, ref1)) + return RET_ERR + argv = argv[:i] + ['-rHEAD:%d' % (r + 1), url2] + argv[i + 1:] + break mode = 'normal' pout = Popen([svn] + argv, stdout=PIPE).stdout for line in iter(pout.readline, ''):