add "stash" subcommand - fix #1

This commit is contained in:
Josh Holtrop 2012-03-26 11:38:18 -04:00
parent 40775d6d72
commit ce8b679910
2 changed files with 105 additions and 10 deletions

13
README
View File

@ -50,6 +50,19 @@ Implemented subcommands:
- with --status, prepended '*' for those with svn:needs-lock set - with --status, prepended '*' for those with svn:needs-lock set
externals externals
- print a list of the externals in the repository - print a list of the externals in the repository
stash [command]
- allow temporarily saving changes to the working copy without committing
- the stashes behaves as a "stack" where "save" pushes a new stash object
and "pop" pops the newest one from the top of the stack
commands:
save (default if not specified):
- save changes as a "stash" object and revert them from working copy
- this currently only works with changes to already-versioned files
list:
- show a list of all stash objects
pop:
- apply the stash object back to the working copy
- the stash object is removed if it was successfully applied
The following subcommands are executed using their native handler, but The following subcommands are executed using their native handler, but
have their output simplified and/or colorized: have their output simplified and/or colorized:

82
jsvn
View File

@ -52,6 +52,19 @@
# - with --status, prepended '*' for those with svn:needs-lock set # - with --status, prepended '*' for those with svn:needs-lock set
# externals # externals
# - print a list of the externals in the repository # - print a list of the externals in the repository
# stash [command]
# - allow temporarily saving changes to the working copy without committing
# - the stashes behaves as a "stack" where "save" pushes a new stash object
# and "pop" pops the newest one from the top of the stack
# commands:
# save (default if not specified):
# - save changes as a "stash" object and revert them from working copy
# - this currently only works with changes to already-versioned files
# list:
# - show a list of all stash objects
# pop:
# - apply the stash object back to the working copy
# - the stash object is removed if it was successfully applied
# #
# The following subcommands are executed using their native handler, but # The following subcommands are executed using their native handler, but
# have their output simplified and/or colorized: # have their output simplified and/or colorized:
@ -265,6 +278,13 @@ def getSVNRoot(svn):
return '/'.join(parts[:i]) return '/'.join(parts[:i])
return '' return ''
def get_svn_wc_root(svn):
for line in Popen([svn, 'info'], stdout=PIPE).communicate()[0].split('\n'):
m = re.match(r'Working Copy Root Path: (.*)$', line)
if m is not None:
return m.group(1)
return ''
def getSVNRelPath(svn): def getSVNRelPath(svn):
url = getSVNURL(svn) url = getSVNURL(svn)
parts = url.split('/') parts = url.split('/')
@ -373,6 +393,32 @@ def descendant_path(child, parent):
return True return True
return False return False
def get_stashes_dir(svn):
stashes_dir = get_svn_wc_root(svn) + '/.svn/stashes'
if not os.path.isdir(stashes_dir):
os.mkdir(stashes_dir)
return stashes_dir
def get_stash_ids(svn):
stashes_dir = get_stashes_dir(svn)
stash_files = os.listdir(stashes_dir)
stash_ids = {}
for sf in stash_files:
m = re.match('stash\.(\d+)$', sf)
if m is not None:
stash_ids[int(m.group(1))] = 1
return sorted(stash_ids.keys())
def get_stash_fname(svn, idx):
return get_stashes_dir(svn) + '/stash.%d' % idx
def get_next_stash_idx(svn):
stash_ids = get_stash_ids(svn)
idx = 1
if len(stash_ids) > 0:
idx = stash_ids[-1] + 1
return idx
########################################################################### ###########################################################################
# Subcommand Handlers # # Subcommand Handlers #
########################################################################### ###########################################################################
@ -745,6 +791,41 @@ def externals(argv, svn, out):
out.write(line[8:]) out.write(line[8:])
return RET_OK return RET_OK
def stash(argv, svn, out):
action = 'save'
if len(argv) >= 2:
if not argv[1].startswith('-'):
action = argv[1]
if action == 'save':
stash_idx = get_next_stash_idx(svn)
stash_fname = get_stash_fname(svn, stash_idx)
fh = open(stash_fname, 'w')
Popen([svn, 'diff'], stdout=fh).wait()
fh.close()
Popen([svn, 'revert', '--depth=infinity', get_svn_wc_root(svn)],
stdout=PIPE).wait()
out.write('Created stash %d\n' % stash_idx)
elif action == 'list':
stash_ids = get_stash_ids(svn)
for si in reversed(stash_ids):
out.write('%d\n' % si)
elif action == 'pop':
stash_ids = get_stash_ids(svn)
if len(stash_ids) > 0:
stash_idx = stash_ids[-1]
stash_fname = get_stash_fname(svn, stash_idx)
rc = Popen([svn, 'patch', stash_fname]).wait()
if rc == 0:
os.unlink(stash_fname)
out.write('Popped stash %d\n' % stash_idx)
else:
out.write('Error popping stash %d\n' % stash_idx)
else:
out.write('No stashes to pop\n')
else:
out.write('Unknown action "%s"\n' % action)
return RET_OK
def root(argv, svn, out): def root(argv, svn, out):
out.write(getSVNRoot(svn) + '\n') out.write(getSVNRoot(svn) + '\n')
return RET_OK return RET_OK
@ -797,6 +878,7 @@ def main(argv):
'binaries': binaries, 'binaries': binaries,
'lockable': lockable, 'lockable': lockable,
'status': status, 'status': status,
'stash': stash,
} }
do_normal_exec = True do_normal_exec = True