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()