stash: rework to do per-hunk processing
This commit is contained in:
parent
3c0375891a
commit
0587d73472
227
jsvn
227
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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user