Export Subversion Files Changed between Two Revision Numbers
Subversion, JavaUpdate 6/2/2008: in response to Mike's comment, here is a code snippet for how to use the SVNChangedFiles class, which will export the files between the two revision numbers from a Subversion branch into the directory you specify:
SVNChangedFiles ecf = new SVNChangedFiles( [SVNbranchURL], [SVNusername], [SVNpassword], [SVNstartRevisionNumber], [SVNendRevisionNumber], [PathToLocalDirectory], [useAncestry] ); ecf.export();
The way the product I work on is delivered sometimes requires being able to issue just a subset of files that have changed since a full release as a package. Subversion as-is doesn't really want to help you with this problem. You can do something like:
svn diff -r[###]:[###] http://[URL to Repository]/[branch] --summarize
But then you are left to do something with those results in order to actually pull those particular files.
The Tortois SVN client can help you with this problem, but nothing something I want to script from Ant build files. Fortunately SVN has an API that allows you hack things the way you like. And there are some good Java Subversion client libraries for this purpose, such as SVNKit.
So here is something I hacked together for pulling changed files that can also work as an Ant task.
SVNChangedFiles
import java.io.File;
import java.util.ArrayList;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.wc.ISVNDiffStatusHandler;
import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNDiffStatus;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
public class SVNChangedFiles {
private SVNURL branchURL;
private String username;
private String password;
private SVNRevision startingRevision;
private SVNRevision endingRevision;
private String destinationDirectory;
private ISVNAuthenticationManager authManager;
/**
* <p>
* Tested with SVNKit 1.1.4
* </p>
* <p>
* Use to export added or modified files from a Subversion repository at
* branchURL between startingRevision and endingRevision revisions to the specified
* destinationDirectory.
* </p>
*
* @param branchURL fully qualified URL of the SVN repository (i.e. "http://[domain]/[path-to-svn]/[branch]")
* @param username SVN username
* @param password SVN password
* @param startingRevision Starting revision number for the branch specified
* @param endingRevision Ending revision number for the branch specified
* @param destinationDirectory Root directory where to export files. <i>Files with same name will be overwritten without warning.</i>
*/
public SVNChangedFiles(String branchURL, String username, String password,
long startingRevision, long endingRevision,
String destinationDirectory) {
super();
DAVRepositoryFactory.setup();
try {
this.branchURL = SVNURL.parseURIEncoded(branchURL);
this.username = username;
this.password = password;
this.startingRevision = SVNRevision.create(startingRevision);
this.endingRevision = SVNRevision.create(endingRevision);
this.destinationDirectory = destinationDirectory;
this.authManager = SVNWCUtil.createDefaultAuthenticationManager(this.username, this.password);
} catch (SVNException e) {
throw new RuntimeException(e);
}
}
/**
* <p>Calls {@link SVNChangedFiles#export(ArrayList)} passing in a null.
*/
public void export() { export(null); }
/**
* <p>
* Exports files added or modified between the startingRevision and endingRevision revision numbers,
* including creating sub-directories, relative to the destinationDirectory.</p>
* </p>
* <p>
* <i>Files of the same name will be overwritten without warning.</i>
* </p>
*
* @param changes ArrayList of SVNDiffStatus objects for export, passing null will populate with the results of {@link SVNChangedFiles#list()}
*/
public void export(ArrayList changes) {
if (changes == null) changes = list();
SVNUpdateClient updateClient = new SVNUpdateClient(authManager, SVNWCUtil.createDefaultOptions(true));
try {
for (int idx = 0; idx < changes.size(); idx++) {
SVNDiffStatus change = (SVNDiffStatus) changes.get(idx);
File destination = new File(destinationDirectory + "\\" + change.getPath());
updateClient.doExport(change.getURL(), destination, this.endingRevision, this.endingRevision, null, true, false);
}
} catch(Exception e) {
throw new RuntimeException(e);
}
}
/**
* <p>
* Returns an {@link java.util.ArrayList} of {@link org.tmatesoft.svn.core.wc.SVNDiffStatus} objects
* representing all files exported or modified on the given branchURL between the startingRevision and endingRevision
* revisions.
* </p>
* @return {@link java.util.ArrayList} of {@link org.tmatesoft.svn.core.wc.SVNDiffStatus}
*/
public ArrayList list() {
try {
SVNDiffClient diffClient = new SVNDiffClient(authManager, SVNWCUtil.createDefaultOptions(true));
ArrayList changes = new ArrayList();
// adds to changes
ImplISVNDiffStatusHandler handler = new ImplISVNDiffStatusHandler(changes);
diffClient.doDiffStatus(this.branchURL, this.startingRevision,
this.branchURL, this.endingRevision, true, false, handler);
return changes;
} catch (SVNException e) {
throw new RuntimeException(e);
}
}
private static class ImplISVNDiffStatusHandler implements ISVNDiffStatusHandler {
private ArrayList changes;
public ImplISVNDiffStatusHandler(ArrayList changes) {
this.changes = changes;
}
public void handleDiffStatus(SVNDiffStatus status) throws SVNException {
if (status.getKind() == SVNNodeKind.FILE && (status.getModificationType() == SVNStatusType.STATUS_ADDED || status.getModificationType() == SVNStatusType.STATUS_MODIFIED))
changes.add(status);
}
}
}
Instantiate and call the export() method to pull the files changed on the branch between the starting and ending revision numbers into the specified destination directory.
Building this requires the SVNKit library.
Ant Task
import java.util.ArrayList;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.tmatesoft.svn.core.wc.SVNDiffStatus;
public class SVNExportChangedFilesAntTask extends Task {
private String branch;
private String username;
private String password;
private long start;
private long end;
private String destination;
public void execute() throws BuildException {
// TODO Auto-generated method stub
super.execute();
SVNChangedFiles changedFiles = new SVNChangedFiles(getBranch(), getUsername(), getPassword(), getStart(), getEnd(), getDestination());
System.out.println("Export Root Directory:\t" + getDestination());
ArrayList changes = changedFiles.list();
for (int idx = 0; idx < changes.size(); idx++) {
SVNDiffStatus change = (SVNDiffStatus) changes.get(idx);
ArrayList singleChange = new ArrayList();
singleChange.add(change);
changedFiles.export(singleChange);
System.out.println("\tExported:\t" + change.getFile());
}
}
public String getBranch() {
return branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public long getStart() {
return start;
}
public void setStart(long start) {
this.start = start;
}
public long getEnd() {
return end;
}
public void setEnd(long end) {
this.end = end;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
}
I leave configuring this with Ant as an excercise for the reader. Once configured it is called as such:
<exportChangedFiles branch="[Branch URL]" start="[Start Revision Number]" end="[End Revision Number]" destination="[Full path of directory to export files to]" password="[Password]" username="[Username]" />
Building this requires the Apache Ant library.




Loading....