A few days ago I asked on the BlogCFC blog about a way to display a report from Subversion. I knew how to get a report of the latest updates for one file, but not for a project as a whole. A few people recommended Trac, but being the kind of guy I am, I wanted to build something myself.

Scott P (who has contributed some good changes to BlogCFC) told me what the SVN command was:

svn log -v svn:// --limit 10

This command will give you a nice report of the last ten changes to the subversion repository. I was about to hook this up to CFEXECUTE and start writing a string parser when I had the brilliant idea of actually checking the documentation. Turns out if you add --xml to the command, you actually get the report back in XML:

svn log -v svn:// --limit 10 --xml

No string parsing necessary! So I quickly whipped up some code (included below) and added the report to BlogCFC. You can find the SVN info here:


Nice design, eh? Hard to believe I'm just a developer. The code is a work in progress, and not encapsulated into a CFC, but for those who want to add this to your site, I've included it below. Some notes:

  • I'm not parsing the dates yet. It's UTC, so I just need to add the offset which I can get from getTimeZoneInfo().
  • You could make the files linkable if you wanted, but you always need to be extra-super-anal-etc when writing code that will dump another file live on the web. In fact, I'd probably just not recommend doing this unless the entire application is very secure.
  • SVN also reports what happened to the file. So for example, I think it uses M to signify that the file was modified. I bet it uses A for Add and D for Delete, but I haven't confirmed this. I'd like to update my code to not just show the files but what the change was.
  • And as I said above, this should be rewritten into a little UDF or CFC.
<!--- path to svn ---> <cfset svnPath = "svn">

<!--- whats the url? ---> <cfset svnURL = "svn://">

<!--- how many entries ---> <cfset top = 10>

<!--- args ---> <cfset args = "log -v #svnURL# --limit #top# --xml">

<!--- run it ---> <cfexecute name="#svnPath#" arguments="#args#" variable="result" timeout="30" />

<!--- parse to xml ---> <cfset data = xmlparse(result)>

<!--- get entries ---> <cfset entries = xmlSearch(data, "//logentry")>

<cfset logEntries = arrayNew(1)>

<cfloop index="x" from="1" to="#arrayLen(entries)#"> <cfset entry = entries[x]> <cfset logEntry = structNew()> <cfset logEntry.revision = entry.xmlAttributes.revision> <cfset logEntry.files = arrayNew(1)> <cfloop index="y" from="1" to="#arrayLen(entry.xmlChildren)#"> <cfset xmlChild = entry.xmlChildren[y]>

	&lt;cfswitch expression="#xmlChild.xmlName#"&gt;
		&lt;cfcase value="author,msg,date"&gt;
			&lt;cfset logEntry[xmlChild.xmlName] = xmlChild.xmlText&gt;

		&lt;cfcase value="paths"&gt;
			&lt;cfloop index="z" from="1" to="#arrayLen(xmlChild.xmlChildren)#"&gt;
				&lt;cfset thisFile = xmlChild.xmlChildren[z].xmlText&gt;
				&lt;cfset arrayAppend(logEntry.files, thisFile)&gt;

&lt;cfset arrayAppend(logEntries, logEntry)&gt;	


<cfdump var="#logEntries#">