Lines 59-65
COLORS = {
Link Here
|
59 |
'lightcyan' : '\x1b[1;36m', |
59 |
'lightcyan' : '\x1b[1;36m', |
60 |
} |
60 |
} |
61 |
|
61 |
|
62 |
# Keys for revision control probe, diff and log with diff |
62 |
# Keys for revision control probe, diff and log (optional) with diff |
63 |
VCS_INFO = { |
63 |
VCS_INFO = { |
64 |
'Git': { |
64 |
'Git': { |
65 |
'probe': ['git', 'rev-parse'], |
65 |
'probe': ['git', 'rev-parse'], |
Lines 71-76
VCS_INFO = {
Link Here
|
71 |
'diff': ['hg', 'diff'], |
71 |
'diff': ['hg', 'diff'], |
72 |
'log': ['hg', 'log', '--patch'], |
72 |
'log': ['hg', 'log', '--patch'], |
73 |
}, |
73 |
}, |
|
|
74 |
'Perforce': { |
75 |
'probe': ['p4', 'dirs', '.'], |
76 |
'diff': ['p4', 'diff'], |
77 |
'log': None, |
78 |
}, |
74 |
'Svn': { |
79 |
'Svn': { |
75 |
'probe': ['svn', 'info'], |
80 |
'probe': ['svn', 'info'], |
76 |
'diff': ['svn', 'diff'], |
81 |
'diff': ['svn', 'diff'], |
Lines 79-84
VCS_INFO = {
Link Here
|
79 |
} |
84 |
} |
80 |
|
85 |
|
81 |
|
86 |
|
|
|
87 |
def revision_control_probe(): |
88 |
"""Returns version control name (key in VCS_INFO) or None.""" |
89 |
for vcs_name, ops in VCS_INFO.items(): |
90 |
if check_command_status(ops.get('probe')): |
91 |
return vcs_name |
92 |
|
93 |
|
94 |
def revision_control_diff(vcs_name, args): |
95 |
"""Return diff from revision control system.""" |
96 |
cmd = VCS_INFO[vcs_name]['diff'] |
97 |
return subprocess.Popen(cmd + args, stdout=subprocess.PIPE).stdout |
98 |
|
99 |
|
100 |
def revision_control_log(vcs_name, args): |
101 |
"""Return log from revision control system or None.""" |
102 |
cmd = VCS_INFO[vcs_name].get('log') |
103 |
if cmd is not None: |
104 |
return subprocess.Popen(cmd + args, stdout=subprocess.PIPE).stdout |
105 |
|
106 |
|
82 |
def colorize(text, start_color, end_color='reset'): |
107 |
def colorize(text, start_color, end_color='reset'): |
83 |
return COLORS[start_color] + text + COLORS[end_color] |
108 |
return COLORS[start_color] + text + COLORS[end_color] |
84 |
|
109 |
|
Lines 299-306
class PatchStream(object):
Link Here
|
299 |
def __iter__(self): |
324 |
def __iter__(self): |
300 |
for line in self._stream_header: |
325 |
for line in self._stream_header: |
301 |
yield line |
326 |
yield line |
302 |
for line in self._diff_hdl: |
327 |
try: |
303 |
yield line |
328 |
for line in self._diff_hdl: |
|
|
329 |
yield line |
330 |
except RuntimeError: |
331 |
return |
304 |
|
332 |
|
305 |
|
333 |
|
306 |
class PatchStreamForwarder(object): |
334 |
class PatchStreamForwarder(object): |
Lines 715-724
def markup_to_pager(stream, opts):
Link Here
|
715 |
See issue #30 (https://github.com/ymattw/ydiff/issues/30) for more |
743 |
See issue #30 (https://github.com/ymattw/ydiff/issues/30) for more |
716 |
information. |
744 |
information. |
717 |
""" |
745 |
""" |
718 |
pager_cmd = ['less'] |
746 |
pager_cmd = [opts.pager] |
719 |
if not os.getenv('LESS'): |
747 |
pager_opts = (opts.pager_options.split(' ') |
720 |
# Args stolen from git source: github.com/git/git/blob/master/pager.c |
748 |
if opts.pager_options is not None |
721 |
pager_cmd.extend(['-FRSX', '--shift 1']) |
749 |
else None) |
|
|
750 |
|
751 |
if opts.pager is None: |
752 |
pager_cmd = ['less'] |
753 |
if not os.getenv('LESS') and not opts.pager_options: |
754 |
# Args stolen from git source: |
755 |
# github.com/git/git/blob/master/pager.c |
756 |
pager_opts = ['-FRSX', '--shift 1'] |
757 |
|
758 |
pager_opts = pager_opts if pager_opts is not None else [] |
759 |
pager_cmd.extend(pager_opts) |
722 |
pager = subprocess.Popen( |
760 |
pager = subprocess.Popen( |
723 |
pager_cmd, stdin=subprocess.PIPE, stdout=sys.stdout) |
761 |
pager_cmd, stdin=subprocess.PIPE, stdout=sys.stdout) |
724 |
|
762 |
|
Lines 743-764
def check_command_status(arguments):
Link Here
|
743 |
return False |
781 |
return False |
744 |
|
782 |
|
745 |
|
783 |
|
746 |
def revision_control_diff(args): |
|
|
747 |
"""Return diff from revision control system.""" |
748 |
for _, ops in VCS_INFO.items(): |
749 |
if check_command_status(ops['probe']): |
750 |
return subprocess.Popen( |
751 |
ops['diff'] + args, stdout=subprocess.PIPE).stdout |
752 |
|
753 |
|
754 |
def revision_control_log(args): |
755 |
"""Return log from revision control system.""" |
756 |
for _, ops in VCS_INFO.items(): |
757 |
if check_command_status(ops['probe']): |
758 |
return subprocess.Popen( |
759 |
ops['log'] + args, stdout=subprocess.PIPE).stdout |
760 |
|
761 |
|
762 |
def decode(line): |
784 |
def decode(line): |
763 |
"""Decode UTF-8 if necessary.""" |
785 |
"""Decode UTF-8 if necessary.""" |
764 |
if isinstance(line, unicode): |
786 |
if isinstance(line, unicode): |
Lines 815-822
def main():
Link Here
|
815 |
rargs.insert(parsed_num, '--') |
837 |
rargs.insert(parsed_num, '--') |
816 |
OptionParser._process_args(self, largs, rargs, values) |
838 |
OptionParser._process_args(self, largs, rargs, values) |
817 |
|
839 |
|
818 |
supported_vcs = sorted(VCS_INFO.keys()) |
|
|
819 |
|
820 |
usage = """%prog [options] [file|dir ...]""" |
840 |
usage = """%prog [options] [file|dir ...]""" |
821 |
parser = PassThroughOptionParser( |
841 |
parser = PassThroughOptionParser( |
822 |
usage=usage, description=META_INFO['description'], |
842 |
usage=usage, description=META_INFO['description'], |
Lines 840-845
def main():
Link Here
|
840 |
parser.add_option( |
860 |
parser.add_option( |
841 |
'', '--wrap', action='store_true', |
861 |
'', '--wrap', action='store_true', |
842 |
help='wrap long lines in side-by-side view') |
862 |
help='wrap long lines in side-by-side view') |
|
|
863 |
parser.add_option( |
864 |
'-p', '--pager', metavar='M', |
865 |
help="""pager application, suggested values are 'less' """ |
866 |
"""or 'cat'""") |
867 |
parser.add_option( |
868 |
'-o', '--pager-options', metavar='M', |
869 |
help="""options to supply to pager application""") |
843 |
|
870 |
|
844 |
# Hack: use OptionGroup text for extra help message after option list |
871 |
# Hack: use OptionGroup text for extra help message after option list |
845 |
option_group = OptionGroup( |
872 |
option_group = OptionGroup( |
Lines 864-885
def main():
Link Here
|
864 |
|
891 |
|
865 |
opts, args = parser.parse_args(ydiff_opts + sys.argv[1:]) |
892 |
opts, args = parser.parse_args(ydiff_opts + sys.argv[1:]) |
866 |
|
893 |
|
867 |
if opts.log: |
894 |
if not sys.stdin.isatty(): |
868 |
diff_hdl = revision_control_log(args) |
|
|
869 |
if not diff_hdl: |
870 |
sys.stderr.write(('*** Not in a supported workspace, supported ' |
871 |
'are: %s\n') % ', '.join(supported_vcs)) |
872 |
return 1 |
873 |
elif sys.stdin.isatty(): |
874 |
diff_hdl = revision_control_diff(args) |
875 |
if not diff_hdl: |
876 |
sys.stderr.write(('*** Not in a supported workspace, supported ' |
877 |
'are: %s\n\n') % ', '.join(supported_vcs)) |
878 |
parser.print_help() |
879 |
return 1 |
880 |
else: |
881 |
diff_hdl = (sys.stdin.buffer if hasattr(sys.stdin, 'buffer') |
895 |
diff_hdl = (sys.stdin.buffer if hasattr(sys.stdin, 'buffer') |
882 |
else sys.stdin) |
896 |
else sys.stdin) |
|
|
897 |
else: |
898 |
vcs_name = revision_control_probe() |
899 |
if vcs_name is None: |
900 |
supported_vcs = ', '.join(sorted(VCS_INFO.keys())) |
901 |
sys.stderr.write('*** Not in a supported workspace, supported are:' |
902 |
' %s\n' % supported_vcs) |
903 |
return 1 |
904 |
|
905 |
if opts.log: |
906 |
diff_hdl = revision_control_log(vcs_name, args) |
907 |
if diff_hdl is None: |
908 |
sys.stderr.write('*** %s does not support log command.\n' % |
909 |
vcs_name) |
910 |
return 1 |
911 |
else: |
912 |
# 'diff' is a must have feature. |
913 |
diff_hdl = revision_control_diff(vcs_name, args) |
883 |
|
914 |
|
884 |
stream = PatchStream(diff_hdl) |
915 |
stream = PatchStream(diff_hdl) |
885 |
|
916 |
|