Python Script for Subversion Status
.I find in my use of Subversion that I often want to see a side-by-side list of files that aren’t under version control and files that have some other status. I also want these lists to be sorted alphabetically. Naturally, I ended up writing a Python script for this.
I suspect it could be cleaner, but it works and I’m trying to keep in mind that “the perfect is the enemy of the good”.
(Script updated 06 Apr 2010, and it now has a home at bitbucket).
#! /usr/bin/python2.6
# -*- coding: utf-8 -*-
# Displays added and modified files in two columns (terminal space permitting)
import subprocess
class SubversionSortedStatus(object):
STATUS = "svn st"
def cli_main(self):
self.get_raw_st()
self.list_unversioned()
self.list_versioned()
self.get_widths()
print self.output_string()
def get_raw_st(self):
cmd_proc = subprocess.Popen(
[self.STATUS], shell=True, stdout=subprocess.PIPE)
self.raw_st = cmd_proc.communicate()[0] or u""
def list_unversioned(self):
self.unversioned = sorted([
l[7:] for l in self.raw_st.split(u"\n") if l.startswith("?")
]) or ["No new files"]
def list_versioned(self):
def namesort(x, y): return cmp(x[6:], y[6:])
self.versioned = sorted([
l for l in self.raw_st.split(u"\n") if l and not l.startswith("?")
], cmp=namesort) or ["No modified files"]
def get_widths(self):
self.unversioned_width = self.get_files_width(self.unversioned)
self.versioned_width = self.get_files_width(self.versioned)
self.terminal_width = self.terminal_size()[0]
self.terminal_midpoint = self.get_terminal_midpoint()
def get_files_width(self, files):
return max([len(line) for line in files]) if files else 0
def get_terminal_midpoint(self):
return self.unversioned_width + 1 if (self.terminal_width > (
self.unversioned_width + self.versioned_width + 1
)) else 0
def output_string(self):
if not self.terminal_midpoint:
return u"{0}\n-----\n{1}".format(u"\n".join(self.unversioned),
u"\n".join(self.versioned))
ver, unv = self.versioned, self.unversioned
def pad(l, maxlength): return l + ([""] * (maxlength - len(l)))
def sep(mid, col_a): return u" " * (mid - len(col_a))
def line(cols, mid):
return u"{0}{1}{2}".format(cols[0], sep(mid, cols[0]), cols[1])
maxlength = max(len(unv), len(ver))
ver, unv = pad(ver, maxlength), pad(unv, maxlength)
return u"\n".join(
[line(cols, self.terminal_midpoint) for cols in zip(unv,ver)])
"""
The next two functions are from Chuck Blake's categorized ls,
found at http://pdos.csail.mit.edu/~cblake/cls/cls.py
"""
def ioctl_GWINSZ(self, fd): #### TABULATION FUNCTIONS
try: ### Discover terminal width
import fcntl, termios, struct, os
cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
except:
return None
return cr
def terminal_size(self): ### decide on *some* terminal size
import os
env = os.environ
cr = self.ioctl_GWINSZ(0) or self.ioctl_GWINSZ(1) or self.ioctl_GWINSZ(2) # try open fds
if not cr: # ...then ctty
try:
fd = os.open(os.ctermid(), os.O_RDONLY)
cr = self.ioctl_GWINSZ(fd)
os.close(fd)
except:
pass
if not cr: # env vars or finally defaults
try:
cr = (env['LINES'], env['COLUMNS'])
except:
cr = (25, 80)
return int(cr[1]), int(cr[0]) # reverse rows, cols
if __name__ == '__main__':
svnstatus = SubversionSortedStatus()
svnstatus.cli_main()