From ee592451b55b7878b41d5eca176eb4d45da68bb6 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 13:24:52 -0400 Subject: [PATCH 01/13] change stash subcommands to require "--" prefix --- jsvn | 46 +++++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/jsvn b/jsvn index fbe75a9..1ca7bea 100755 --- a/jsvn +++ b/jsvn @@ -1233,12 +1233,7 @@ def revert_h(argv, svn, out, config): break return RET_OK if did_something else RET_REEXEC -def stash_save_h(argv, svn, out, config): - keep_wc = False - options, args = getopt.getopt(argv, 'k') - for opt, val in options: - if opt == '-k': - keep_wc = True +def stash_save_h(args, svn, out, config, keep_wc): owd = os.getcwd() wc_dir = get_svn_wc_root(svn) os.chdir(wc_dir) @@ -1380,12 +1375,7 @@ def stash_list_h(argv, svn, out, config): out.write('\n') return RET_OK -def stash_pop_h(argv, svn, out, config): - keep = False - options, args = getopt.getopt(argv, 'k') - for opt, val in options: - if opt == '-k': - keep = True +def stash_pop_h(args, svn, out, config, keep): owd = os.getcwd() wc_dir = get_svn_wc_root(svn) os.chdir(wc_dir) @@ -1443,23 +1433,21 @@ def stash_drop_h(argv, svn, out, config): def stash_h(argv, svn, out, config): argv = argv[1:] # strip 'stash' command - action = 'save' - if len(argv) >= 1: - if not argv[0].startswith('-'): - action = argv[0] - argv = argv[1:] - # now argv only contains options/arguments to the stash subcommand itself - actions = { - 'save': stash_save_h, - 'list': stash_list_h, - 'pop': stash_pop_h, - 'show': stash_show_h, - 'drop': stash_drop_h - } - if action in actions: - return actions[action](argv, svn, out, config) - sys.stderr.write('Unknown action "%s"\n' % action) - return RET_ERR + opts, args = getopt.getopt(argv, 'k', + ['list', 'pop', 'show', 'drop']) + keep = False + for opt, arg in opts: + if opt == '--list': + return stash_list_h(args, svn, out, config) + if opt == '--pop': + return stash_pop_h(args, svn, out, config, keep) + if opt == '--show': + return stash_show_h(args, svn, out, config) + if opt == '--drop': + return stash_drop_h(args, svn, out, config) + if opt == '-k': + keep = True + return stash_save_h(args, svn, out, config, keep) def root_h(argv, svn, out, config): out.write(get_svn_root_url(svn) + '\n') From 3c0375891ab4036ac4ba0c88cdbc5d55afb0713c Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 13:50:42 -0400 Subject: [PATCH 02/13] stash: add --keep, -p, --patch options --- jsvn | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/jsvn b/jsvn index 1ca7bea..0a93c9f 100755 --- a/jsvn +++ b/jsvn @@ -1233,7 +1233,7 @@ def revert_h(argv, svn, out, config): break return RET_OK if did_something else RET_REEXEC -def stash_save_h(args, svn, out, config, keep_wc): +def stash_save_h(args, svn, out, config, keep_wc, patch): owd = os.getcwd() wc_dir = get_svn_wc_root(svn) os.chdir(wc_dir) @@ -1433,21 +1433,24 @@ def stash_drop_h(argv, svn, out, config): def stash_h(argv, svn, out, config): argv = argv[1:] # strip 'stash' command - opts, args = getopt.getopt(argv, 'k', - ['list', 'pop', 'show', 'drop']) + opts, args = getopt.getopt(argv, 'kp', + ['list', 'pop', 'show', 'drop', 'keep', 'patch']) keep = False + patch = False for opt, arg in opts: if opt == '--list': return stash_list_h(args, svn, out, config) - if opt == '--pop': + elif opt == '--pop': return stash_pop_h(args, svn, out, config, keep) - if opt == '--show': + elif opt == '--show': return stash_show_h(args, svn, out, config) - if opt == '--drop': + elif opt == '--drop': return stash_drop_h(args, svn, out, config) - if opt == '-k': + elif opt in ('-k', '--keep'): keep = True - return stash_save_h(args, svn, out, config, keep) + elif opt in ('-p', '--patch'): + patch = True + return stash_save_h(args, svn, out, config, keep, patch) def root_h(argv, svn, out, config): out.write(get_svn_root_url(svn) + '\n') From 0587d734722e62b439df49bf37a910a839670796 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 20:49:11 -0400 Subject: [PATCH 03/13] stash: rework to do per-hunk processing --- jsvn | 227 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 149 insertions(+), 78 deletions(-) diff --git a/jsvn b/jsvn index 0a93c9f..6c6ade7 100755 --- a/jsvn +++ b/jsvn @@ -16,6 +16,7 @@ import types import getopt import signal import platform +import tempfile STATUS_LINE_REGEX = r'[ACDIMRX?!~ ][CM ][L ][+ ][SX ][KOTB ]..(.+)' @@ -1233,88 +1234,158 @@ def revert_h(argv, svn, out, config): break return RET_OK if did_something else RET_REEXEC -def stash_save_h(args, svn, out, config, keep_wc, patch): +def get_svn_contents_to_stash(targets, svn, out, keep, patch): + s_fd, s_fname = tempfile.mkstemp(prefix = 'svn.stash.') + r_fd, r_fname = tempfile.mkstemp(prefix = 'svn.stash.') + os.close(s_fd) + os.close(r_fd) + s_fh = open(s_fname, 'w') + r_fh = open(r_fname, 'w') + + svars = { + 'revert_list': [], + 'skip_all': not patch, + 'skip_file': True, + 'answer': 'y', + 'index_header': '', + 'hunk_buildup': '', + 'index_fname': '', + 'prompted_for_index': '', + 'quit': False, + 'wrote_index_sf': False, + 'wrote_index_rf': False, + 'binary_file': False, + 'n_insertions': 0, + 'n_deletions': 0, + } + def update_answer(): + if not patch: + svars['answer'] = 'y' + return + if svars['skip_file'] or svars['skip_all']: + return + if not svars['prompted_for_index']: + out.write(svars['index_header']) + svars['prompted_for_index'] = True + for li in svars['hunk_buildup'].rstrip().split('\n'): + colordiff(out, li) + answer = '' + answers = ('y', 'n', 'yf', 'nf', 'ya', 'na', 'q', '?') + while answer not in answers: + out.write('Stash this hunk (%s)? ' % ','.join(answers)) + answer = sys.stdin.readline().rstrip().lower() + if answer == '?': + out.write('''y: yes, stash this hunk +n: no, do not stash this hunk +yf: yes, and stash every hunk from the rest of this file +nf: no, and do not stash any hunk from the rest of this file +ya: yes, and stash every remaining hunk +na: no, and do not stash any remaining hunks +q: quit and abort stash +?: show this help +''') + answer = '' + if answer == 'q': + svars['quit'] = True + elif answer[1:] == 'a': + svars['skip_all'] = True + elif answer[1:] == 'f': + svars['skip_file'] = True + svars['answer'] = answer[:1] + + def flush_hunk(): + if svars['hunk_buildup'] != '': + update_answer() + if svars['answer'] == 'y': + if not svars['wrote_index_sf']: + s_fh.write(svars['index_header']) + svars['wrote_index_sf'] = True + s_fh.write(svars['hunk_buildup']) + elif svars['answer'] == 'n': + if not svars['wrote_index_rf']: + r_fh.write(svars['index_header']) + svars['wrote_index_rf'] = True + r_fh.write(svars['hunk_buildup']) + svars['hunk_buildup'] = '' + + def flush_file(new_file_name): + if svars['binary_file']: + ansi_color(out, 'yellow', bold=True) + out.write('Warning: not stashing binary file %s' % svars['index_fname']) + ansi_reset(out) + out.write('\n') + else: + flush_hunk() + if svars['wrote_index_sf']: + svars['revert_list'].append(svars['index_fname']) + svars['skip_file'] = False + svars['hunk_buildup'] = '' + svars['index_fname'] = new_file_name + svars['binary_file'] = False + + diff_proc = Popen([svn, 'diff'] + targets, stdout=PIPE) + for line in iter(diff_proc.stdout.readline, ''): + m = re.match(r'Index: (.*)', line) + if m is not None: + flush_file(m.group(1)) + svars['index_header'] = line + elif (re.match(r'=+$', line) or + re.match(r'--- ', line) or + re.match(r'\+\+\+ ', line)): + svars['index_header'] += line + elif (re.match(r'@@ ', line) or re.match(r'Property.changes.on:', line)): + flush_hunk() + svars['hunk_buildup'] = line + elif re.match(r'Cannot display: file.marked.as.a.binary.type', line): + svars['binary_file'] = True + else: + svars['hunk_buildup'] += line + if svars['quit']: + break + flush_file('') + + s_fh.close() + r_fh.close() + if svars['quit']: + svars['revert_list'] = [] + + return s_fname, r_fname, svars['revert_list'], svars['n_insertions'], svars['n_deletions'] + +def stash_save_h(args, svn, out, config, keep, patch): owd = os.getcwd() wc_dir = get_svn_wc_root(svn) os.chdir(wc_dir) - stash_idx = get_next_stash_idx(svn) - stash_fname = get_stash_fname(svn, stash_idx) - fh = open(stash_fname, 'w') - proc = Popen([svn, 'diff'], stdout=PIPE) - wrote_something = False - found_binary_file = False - n_insertions = 0 - n_deletions = 0 - for line in iter(proc.stdout.readline, ''): - if re.match(r'Cannot.display..file.marked.as.a.binary.type', - line, flags=re.I): - found_binary_file = True - if line.startswith('-') and not line.startswith('---'): - n_deletions += 1 - if line.startswith('+') and not line.startswith('+++'): - n_insertions += 1 - fh.write(line) - wrote_something = True - proc.wait() - if found_binary_file: - out.write('Error: cannot stash with changes to binary files\n') - fh.close() - os.unlink(stash_fname) - elif not wrote_something: - out.write('Error: no changes to stash!\n') - fh.close() - os.unlink(stash_fname) + s_fname, r_fname, revert_list, n_insertions, n_deletions = \ + get_svn_contents_to_stash(args, svn, out, keep, patch) + if len(revert_list) == 0: + out.write('No changes stashed.\n') else: - # the stash is good, now revert the working copy changes - status_lines = Popen([svn, 'status', '--ignore-externals'], - stdout=PIPE).communicate()[0].split('\n') - files_changed = {} - for line in reversed(status_lines): - m = re.match(STATUS_LINE_REGEX, line) - if m is not None: - target = m.group(1) - action = line[0] - prop_action = line[1] - if (action in ('A', 'M', 'D') - or prop_action == 'M'): - if action != ' ': - files_changed[target] = action - else: - files_changed[target] = prop_action - if not keep_wc: - # do the actual revert if -k not given - Popen([svn, 'revert', target], stdout=PIPE).wait() - if action == 'A': - # a file was added, so to stash it we must - # remove it in addition to reverting the add - if os.path.isfile(target): - os.unlink(target) - elif os.path.isdir(target): - if len(os.listdir(target)) == 0: - os.rmdir(target) - else: - raise ValueError('unhandled target type') - # write stash info - if len(files_changed) == 1: - fname = files_changed.keys()[0] - fh.write('#info: %s: %s\n' % (files_changed[fname], fname)) - else: - num_actions = {'A': 0, 'M': 0, 'D': 0} - for k in files_changed: - if files_changed[k] in num_actions: - num_actions[files_changed[k]] += 1 - for a in num_actions: - if num_actions[a] > 0: - fh.write('#info: %s: %d\n' % (a, num_actions[a])) - if n_deletions > 0: - fh.write('#info: -%d\n' % n_deletions) - if n_insertions > 0: - fh.write('#info: +%d\n' % n_insertions) - now = datetime.datetime.now() - fh.write('#info: @%04d-%02d-%02d %02d:%02d\n' % - (now.year, now.month, now.day, now.hour, now.minute)) - fh.close() - out.write('Created stash %d\n' % stash_idx) + for rf in revert_list: + Popen([svn, 'revert', rf], stdout=PIPE).wait() + if r_fname != '': + Popen([svn, 'patch', r_fname], stdout=PIPE).wait() + if s_fname != '': + stash_idx = get_next_stash_idx(svn) + stash_fname = get_stash_fname(svn, stash_idx) + stash_fh = open(stash_fname, 'w') + s_fh = open(s_fname, 'r') + for line in iter(s_fh.readline, ''): + stash_fh.write(line) + s_fh.close() + # write stash info + if n_deletions > 0: + stash_fh.write('#info: -%d\n' % n_deletions) + if n_insertions > 0: + stash_fh.write('#info: +%d\n' % n_insertions) + now = datetime.datetime.now() + stash_fh.write('#info: @%04d-%02d-%02d %02d:%02d\n' % + (now.year, now.month, now.day, now.hour, now.minute)) + stash_fh.close() + out.write('Created stash %d\n' % stash_idx) + if s_fname != '': + os.unlink(s_fname) + if r_fname != '': + os.unlink(r_fname) os.chdir(owd) return RET_OK From 6f4a88737df1c7029e23e89ef1895c6c08a8655a Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 20:51:56 -0400 Subject: [PATCH 04/13] stash: respect -k flag again --- jsvn | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/jsvn b/jsvn index 6c6ade7..1314398 100755 --- a/jsvn +++ b/jsvn @@ -1360,10 +1360,11 @@ def stash_save_h(args, svn, out, config, keep, patch): if len(revert_list) == 0: out.write('No changes stashed.\n') else: - for rf in revert_list: - Popen([svn, 'revert', rf], stdout=PIPE).wait() - if r_fname != '': - Popen([svn, 'patch', r_fname], stdout=PIPE).wait() + if not keep: + for rf in revert_list: + Popen([svn, 'revert', rf], stdout=PIPE).wait() + if r_fname != '': + Popen([svn, 'patch', r_fname], stdout=PIPE).wait() if s_fname != '': stash_idx = get_next_stash_idx(svn) stash_fname = get_stash_fname(svn, stash_idx) From e8fe0efcdb3215f2ef0ce4249ecf06101ddcddd2 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 21:22:03 -0400 Subject: [PATCH 05/13] stash: rework bugfixes --- jsvn | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jsvn b/jsvn index 1314398..e2719a2 100755 --- a/jsvn +++ b/jsvn @@ -1322,6 +1322,8 @@ q: quit and abort stash svars['hunk_buildup'] = '' svars['index_fname'] = new_file_name svars['binary_file'] = False + svars['wrote_index_sf'] = False + svars['wrote_index_rf'] = False diff_proc = Popen([svn, 'diff'] + targets, stdout=PIPE) for line in iter(diff_proc.stdout.readline, ''): @@ -1342,7 +1344,8 @@ q: quit and abort stash svars['hunk_buildup'] += line if svars['quit']: break - flush_file('') + if not svars['quit']: + flush_file('') s_fh.close() r_fh.close() From 40aeec7bea78777d1e7a279f3390de1463225acf Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 21:24:42 -0400 Subject: [PATCH 06/13] stash: properly display new Index line when file skipped --- jsvn | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jsvn b/jsvn index e2719a2..c0912ef 100755 --- a/jsvn +++ b/jsvn @@ -1250,7 +1250,7 @@ def get_svn_contents_to_stash(targets, svn, out, keep, patch): 'index_header': '', 'hunk_buildup': '', 'index_fname': '', - 'prompted_for_index': '', + 'prompted_for_index': False, 'quit': False, 'wrote_index_sf': False, 'wrote_index_rf': False, @@ -1324,6 +1324,7 @@ q: quit and abort stash svars['binary_file'] = False svars['wrote_index_sf'] = False svars['wrote_index_rf'] = False + svars['prompted_for_index'] = False diff_proc = Popen([svn, 'diff'] + targets, stdout=PIPE) for line in iter(diff_proc.stdout.readline, ''): From 6d547299cd834a954279547d9971f10785c8ea5e Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 21:26:03 -0400 Subject: [PATCH 07/13] stash: colorize Index line while patch-prompting --- jsvn | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jsvn b/jsvn index c0912ef..05a6de7 100755 --- a/jsvn +++ b/jsvn @@ -1265,7 +1265,8 @@ def get_svn_contents_to_stash(targets, svn, out, keep, patch): if svars['skip_file'] or svars['skip_all']: return if not svars['prompted_for_index']: - out.write(svars['index_header']) + for line in svars['index_header'].rstrip().split('\n'): + colordiff(out, line) svars['prompted_for_index'] = True for li in svars['hunk_buildup'].rstrip().split('\n'): colordiff(out, li) From a215aac2f6cea41754a0b18244bd90c48c27d7da Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 21:31:05 -0400 Subject: [PATCH 08/13] stash: color the patch prompt line --- jsvn | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jsvn b/jsvn index 05a6de7..dde2306 100755 --- a/jsvn +++ b/jsvn @@ -1273,7 +1273,9 @@ def get_svn_contents_to_stash(targets, svn, out, keep, patch): answer = '' answers = ('y', 'n', 'yf', 'nf', 'ya', 'na', 'q', '?') while answer not in answers: + ansi_color(out, 'magenta', bold=True) out.write('Stash this hunk (%s)? ' % ','.join(answers)) + ansi_reset(out) answer = sys.stdin.readline().rstrip().lower() if answer == '?': out.write('''y: yes, stash this hunk From f2678b5f88c141e0efe7d72aa80dfc6ed8c2dc08 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 22:06:18 -0400 Subject: [PATCH 09/13] stash: add -e/--externals option to also stash externals --- jsvn | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/jsvn b/jsvn index dde2306..bb4c3f6 100755 --- a/jsvn +++ b/jsvn @@ -1234,7 +1234,7 @@ def revert_h(argv, svn, out, config): break return RET_OK if did_something else RET_REEXEC -def get_svn_contents_to_stash(targets, svn, out, keep, patch): +def get_svn_contents_to_stash(targets, svn, out, keep, patch, externals): s_fd, s_fname = tempfile.mkstemp(prefix = 'svn.stash.') r_fd, r_fname = tempfile.mkstemp(prefix = 'svn.stash.') os.close(s_fd) @@ -1242,6 +1242,14 @@ def get_svn_contents_to_stash(targets, svn, out, keep, patch): s_fh = open(s_fname, 'w') r_fh = open(r_fname, 'w') + if externals and len(targets) == 0: + targets = ['.'] + proc = Popen([svn, 'status'], stdout=PIPE) + for line in iter(proc.stdout.readline, ''): + m = re.match(r"Performing status on external item at '(.*)':", line) + if m is not None: + targets.append(m.group(1)) + svars = { 'revert_list': [], 'skip_all': not patch, @@ -1358,12 +1366,12 @@ q: quit and abort stash return s_fname, r_fname, svars['revert_list'], svars['n_insertions'], svars['n_deletions'] -def stash_save_h(args, svn, out, config, keep, patch): +def stash_save_h(args, svn, out, config, keep, patch, externals): owd = os.getcwd() wc_dir = get_svn_wc_root(svn) os.chdir(wc_dir) s_fname, r_fname, revert_list, n_insertions, n_deletions = \ - get_svn_contents_to_stash(args, svn, out, keep, patch) + get_svn_contents_to_stash(args, svn, out, keep, patch, externals) if len(revert_list) == 0: out.write('No changes stashed.\n') else: @@ -1512,10 +1520,11 @@ def stash_drop_h(argv, svn, out, config): def stash_h(argv, svn, out, config): argv = argv[1:] # strip 'stash' command - opts, args = getopt.getopt(argv, 'kp', - ['list', 'pop', 'show', 'drop', 'keep', 'patch']) + opts, args = getopt.getopt(argv, 'ekp', + ['list', 'pop', 'show', 'drop', 'externals', 'keep', 'patch']) keep = False patch = False + externals = False for opt, arg in opts: if opt == '--list': return stash_list_h(args, svn, out, config) @@ -1529,7 +1538,9 @@ def stash_h(argv, svn, out, config): keep = True elif opt in ('-p', '--patch'): patch = True - return stash_save_h(args, svn, out, config, keep, patch) + elif opt in ('-e', '--externals'): + externals = True + return stash_save_h(args, svn, out, config, keep, patch, externals) def root_h(argv, svn, out, config): out.write(get_svn_root_url(svn) + '\n') From 821d27863d2b12ac8b6c388b777f7dcc2ebffe8b Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 22:12:36 -0400 Subject: [PATCH 10/13] stash: update README for stash rework --- README | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/README b/README index 5abc5ad..0f72321 100644 --- a/README +++ b/README @@ -56,27 +56,34 @@ Implemented subcommands: - revert all affected files under given target path(s) root - output root URL (for use on shell such as "svn log $(svn root)/tags") - stash [command] + stash [options] [file...] - 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 [-k] (default if not specified): - - save changes as a "stash" object - - revert changes from working copy unless -k (keep working copy) given - - this only works with text files, not binary files - list: - - show a list of all stash objects - pop [-k] [id]: - - apply the stash object back to the working copy - - the stash object is removed unless -k (keep) given - - defaults to the newest stash object created - show [id]: - - display the diff stored in stash with ID - - defaults to the newest stash object created - drop [id]: - - delete stash object - - defaults to the newest stash object created + - the stashes behaves as a "stack" where stashing pushes a new stash object + and popping removes the newest one from the top of the stack + - binary files are ignored (a warning is printed) and not stashed + options: + -e, --externals + - also stash changes in externals (if no explicit targets given) + -k, --keep + - create the stash object, but keep the changes locally as well + - with --pop, do not remove the stash object after reapplying it + -p, --patch + - interactively prompt for whether to stash each hunk + --list + - show a list of all stash objects + --pop [id] + - apply the stash object back to the working copy + - the stash object is removed unless -k (keep) given + - defaults to the newest stash object created + --show [id] + - display the diff stored in stash with ID + - defaults to the newest stash object created + --drop [id] + - delete stash object + - defaults to the newest stash object created + - if none of --list, --pop, --show, or --drop is given, a new stash object + is created containing the chosen differences + - if file is given, only the changes from the listed files will be stashed switch - switch to 'trunk', branch name, or tag name without having to specify the full URL From d6f74de0346b102e3a9965331f3ceff963c79710 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 22:21:05 -0400 Subject: [PATCH 11/13] stash: add --noexternals option, stash_externals configuration flag --- README | 6 ++++++ jsvn | 10 +++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README b/README index 0f72321..db4b4e0 100644 --- a/README +++ b/README @@ -64,6 +64,10 @@ Implemented subcommands: options: -e, --externals - also stash changes in externals (if no explicit targets given) + - this option is implicitly on if the configuration value + 'stash_externals' is set to True + --noexternals + - reverse --externals (or the configuration value 'stash_externals') -k, --keep - create the stash object, but keep the changes locally as well - with --pop, do not remove the stash object after reapplying it @@ -151,6 +155,8 @@ Available configuration variables: for newly added files which should not automatically have the svn:executable property added for them even if the files are executable. The default value is ['.c', '.cc', '.h', '.txt']. + stash_externals: True or False to enable/disable whether '-e' is implicitly + on for 'stash' subcommand. Defaults to False. Configuration Examples: pager = 'less -FRXi' # enable case-insensitive searching in less diff --git a/jsvn b/jsvn index bb4c3f6..4687778 100755 --- a/jsvn +++ b/jsvn @@ -73,7 +73,8 @@ def get_config(svn): 'branches': 'branch'}, 'svn': '', 'ignore_executable_extensions': - ['.c', '.cc', '.h', '.txt'] + ['.c', '.cc', '.h', '.txt'], + 'stash_externals': False, } global_user_config_fname = os.path.expanduser('~/.jsvn') @@ -1521,10 +1522,11 @@ def stash_drop_h(argv, svn, out, config): def stash_h(argv, svn, out, config): argv = argv[1:] # strip 'stash' command opts, args = getopt.getopt(argv, 'ekp', - ['list', 'pop', 'show', 'drop', 'externals', 'keep', 'patch']) + ['list', 'pop', 'show', 'drop', 'externals', 'noexternals', + 'keep', 'patch']) keep = False patch = False - externals = False + externals = config['stash_externals'] for opt, arg in opts: if opt == '--list': return stash_list_h(args, svn, out, config) @@ -1540,6 +1542,8 @@ def stash_h(argv, svn, out, config): patch = True elif opt in ('-e', '--externals'): externals = True + elif opt == '--noexternals': + externals = False return stash_save_h(args, svn, out, config, keep, patch, externals) def root_h(argv, svn, out, config): From 32574bdd23ef22fbf351af777697d420379ba9b3 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 22:25:30 -0400 Subject: [PATCH 12/13] stash: re-add # of insertions/deletions to stash info --- jsvn | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jsvn b/jsvn index 4687778..b1fb51c 100755 --- a/jsvn +++ b/jsvn @@ -1355,6 +1355,10 @@ q: quit and abort stash svars['binary_file'] = True else: svars['hunk_buildup'] += line + if line.startswith('+'): + svars['n_insertions'] += 1 + elif line.startswith('-'): + svars['n_deletions'] += 1 if svars['quit']: break if not svars['quit']: From a34b0f5edf760decc41c437a2ba09f2a8e2a127c Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 11 Mar 2013 22:34:44 -0400 Subject: [PATCH 13/13] stash: add info for file name changed or # of files changed --- jsvn | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/jsvn b/jsvn index b1fb51c..ea4305e 100755 --- a/jsvn +++ b/jsvn @@ -1398,6 +1398,10 @@ def stash_save_h(args, svn, out, config, keep, patch, externals): stash_fh.write('#info: -%d\n' % n_deletions) if n_insertions > 0: stash_fh.write('#info: +%d\n' % n_insertions) + if len(revert_list) == 1: + stash_fh.write('#info: F: %s\n' % revert_list[0]) + else: + stash_fh.write('#info: F: %d files\n' % len(revert_list)) now = datetime.datetime.now() stash_fh.write('#info: @%04d-%02d-%02d %02d:%02d\n' % (now.year, now.month, now.day, now.hour, now.minute)) @@ -1418,6 +1422,7 @@ def stash_list_h(argv, svn, out, config): add_text = '' modify_text = '' delete_text = '' + summary_text = '' date = '' stash_fname = get_stash_fname(svn, si) fh = open(stash_fname, 'r') @@ -1431,6 +1436,8 @@ def stash_list_h(argv, svn, out, config): modify_text = info elif info.startswith('D:'): delete_text = info + elif info.startswith('F:'): + summary_text = info[3:] elif info.startswith('-'): del_text = info elif info.startswith('+'): @@ -1444,6 +1451,7 @@ def stash_list_h(argv, svn, out, config): (add_text, 'green'), (modify_text, 'yellow'), (delete_text, 'red'), + (summary_text, 'magenta'), ] for elem, color in elements: if elem != '':