Skip to content

Commit

Permalink
Ignore Commits only consisting of changed properties on dirs.
Browse files Browse the repository at this point in the history
- introduced a new property (checkbox)
- changed XMLParser to include "kind" attribute into changelog
- paths containing kind:dir and action:M (which must be a property
change)
  are ignored during notifyCommit and not part of SCM.ChangeLog,
  i.e. they are ignored in Maven Builds.
  • Loading branch information
pauxus committed Aug 3, 2012
1 parent a7d6b39 commit 13a1cc8
Show file tree
Hide file tree
Showing 12 changed files with 322 additions and 15 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
/.project
*.iml
.idea
/work
148 changes: 148 additions & 0 deletions src/main/java/hudson/scm/DirAwareSVNXMLLogHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package hudson.scm;

import java.util.Iterator;
import java.util.LinkedList;

import org.tmatesoft.svn.core.ISVNLogEntryHandler;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.internal.util.SVNDate;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.wc.xml.AbstractXMLHandler;
import org.tmatesoft.svn.core.wc.xml.SVNXMLLogHandler;
import org.tmatesoft.svn.util.ISVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

/**
* Special XML Log Handler that includes the "kind" attribute for path nodes which are ommited by the original.
*
* This is a lot of copy paste code
* @author qxa4177
*/
public class DirAwareSVNXMLLogHandler extends SVNXMLLogHandler implements ISVNLogEntryHandler {

public static final String KIND_ATTR = "kind";

private boolean myIsOmitLogMessage;

private LinkedList myMergeStack;

public DirAwareSVNXMLLogHandler(ContentHandler contentHandler, ISVNDebugLog log) {
super(contentHandler, log);
}

public DirAwareSVNXMLLogHandler(ContentHandler contentHandler) {
super(contentHandler);
}

/**
* Sets whether log messages must be omitted or not.
*
* @param omitLogMessage <span class="javakeyword">true</span> to omit;
* otherwise <span class="javakeyword">false</span>
*/
public void setOmitLogMessage(boolean omitLogMessage) {
myIsOmitLogMessage = omitLogMessage;
}

/**
* Handles a next log entry producing corresponding xml.
*
* @param logEntry log entry
* @throws SVNException
*/
public void handleLogEntry(SVNLogEntry logEntry) throws SVNException {
try {
sendToHandler(logEntry);
} catch (SAXException e) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.XML_MALFORMED, e.getLocalizedMessage());
SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
}
}

// unfortunately, the original method is private, so we need to copy / paste it
// copied from SVNXMLLogHandler
//
protected void sendToHandler(SVNLogEntry logEntry) throws SAXException {
if (logEntry.getRevision() == 0 && logEntry.getMessage() == null) {
return;
}
addAttribute(REVISION_ATTR, logEntry.getRevision() + "");
openTag(LOGENTRY_TAG);
if (logEntry.getAuthor() != null) {
addTag(AUTHOR_TAG, logEntry.getAuthor());
}
if (logEntry.getDate() != null && logEntry.getDate().getTime() != 0) {
addTag(DATE_TAG, SVNDate.formatDate(logEntry.getDate()));
}
if (logEntry.getChangedPaths() != null && !logEntry.getChangedPaths().isEmpty()) {
openTag(PATHS_TAG);
for (Iterator paths = logEntry.getChangedPaths().keySet().iterator(); paths.hasNext();) {
String key = (String) paths.next();
SVNLogEntryPath path = (SVNLogEntryPath) logEntry.getChangedPaths().get(key);
addAttribute(ACTION_ATTR, path.getType() + "");
if (path.getCopyPath() != null) {
addAttribute(COPYFROM_PATH_ATTR, path.getCopyPath());
addAttribute(COPYFROM_REV_ATTR, path.getCopyRevision() + "");
}
if (path.getKind() != null) {
addAttribute(KIND_ATTR, path.getKind().toString());
}
addTag(PATH_TAG, path.getPath());
}
closeTag(PATHS_TAG);
}

if (!myIsOmitLogMessage) {
String message = logEntry.getMessage();
message = message == null ? "" : message;
addTag(MSG_TAG, message);
}

if (myMergeStack != null && !myMergeStack.isEmpty()) {
MergeFrame frame = (MergeFrame) myMergeStack.getLast();
frame.myNumberOfChildrenRemaining--;
}

//TODO: FIXME

This comment has been minimized.

Copy link
@kutzi

kutzi Aug 5, 2012

What should be fixed here?

This comment has been minimized.

Copy link
@pauxus

pauxus Aug 5, 2012

Author Owner

Ups, that was a copy - paste from the original handler.mI will remove it.

if (logEntry.hasChildren()) {
MergeFrame frame = new MergeFrame();
//frame.myNumberOfChildrenRemaining = logEntry.getNumberOfChildren();
if (myMergeStack == null) {
myMergeStack = new LinkedList();
}
myMergeStack.addLast(frame);
} else {
while(myMergeStack != null && !myMergeStack.isEmpty()) {
MergeFrame frame = (MergeFrame) myMergeStack.getLast();
if (frame.myNumberOfChildrenRemaining == 0) {
closeTag(LOGENTRY_TAG);
myMergeStack.removeLast();
} else {
break;
}
}
closeTag(LOGENTRY_TAG);
}
}
private class MergeFrame {
private long myNumberOfChildrenRemaining;
}

}
2 changes: 1 addition & 1 deletion src/main/java/hudson/scm/SubversionChangeLogBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public boolean run(Collection<SubversionSCM.External> externals, Result changeLo
SVNLogClient svnlc = manager.getLogClient();
TransformerHandler th = createTransformerHandler();
th.setResult(changeLog);
SVNXMLLogHandler logHandler = new SVNXMLLogHandler(th);
SVNXMLLogHandler logHandler = new DirAwareSVNXMLLogHandler(th);
// work around for http://svnkit.com/tracker/view.php?id=175
th.setDocumentLocator(DUMMY_LOCATOR);
logHandler.startDocument();
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/hudson/scm/SubversionChangeLogParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@
* @author Kohsuke Kawaguchi
*/
public class SubversionChangeLogParser extends ChangeLogParser {

private boolean ignoreDirPropChanges;

@Deprecated
public SubversionChangeLogParser() {
this(false);
}

public SubversionChangeLogParser(boolean ignoreDirPropChanges) {
this.ignoreDirPropChanges = ignoreDirPropChanges;
}

public SubversionChangeLogSet parse(@SuppressWarnings("rawtypes") AbstractBuild build, File changelogFile) throws IOException, SAXException {
// http://svn.apache.org/repos/asf/subversion/trunk/subversion/svn/schema/log.rnc

Expand Down Expand Up @@ -71,7 +83,7 @@ public SubversionChangeLogSet parse(@SuppressWarnings("rawtypes") AbstractBuild
for (LogEntry e : r) {
e.finish();
}
return new SubversionChangeLogSet(build,r);
return new SubversionChangeLogSet(build,r, ignoreDirPropChanges);
}

}
47 changes: 45 additions & 2 deletions src/main/java/hudson/scm/SubversionChangeLogSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import hudson.model.AbstractBuild;
import hudson.model.User;
import hudson.scm.SubversionChangeLogSet.LogEntry;
import hudson.scm.SubversionChangeLogSet.Path;
import hudson.scm.SubversionSCM.ModuleLocation;

import java.io.IOException;
Expand Down Expand Up @@ -59,8 +60,16 @@ public final class SubversionChangeLogSet extends ChangeLogSet<LogEntry> {
*/
private Map<String,Long> revisionMap;

private boolean ignoreDirPropChanges;

@Deprecated
/*package*/ SubversionChangeLogSet(AbstractBuild<?,?> build, List<LogEntry> logs) {
this(build, logs, false);
}

/*package*/ SubversionChangeLogSet(AbstractBuild<?,?> build, List<LogEntry> logs, boolean ignoreDirPropChanges) {
super(build);
this.ignoreDirPropChanges = ignoreDirPropChanges;
this.logs = prepareChangeLogEntries(logs);
}

Expand Down Expand Up @@ -90,6 +99,9 @@ public synchronized Map<String,Long> getRevisionMap() throws IOException {

private List<LogEntry> prepareChangeLogEntries(List<LogEntry> items) {
items = removeDuplicatedEntries(items);

if (ignoreDirPropChanges) items = removePropertyOnlyChanges(items);

// we want recent changes first
Collections.sort(items, new ReverseByRevisionComparator());
for (LogEntry log : items) {
Expand All @@ -98,6 +110,17 @@ private List<LogEntry> prepareChangeLogEntries(List<LogEntry> items) {
return Collections.unmodifiableList(items);
}

static List<LogEntry> removePropertyOnlyChanges(List<LogEntry> items) {

for (Iterator<LogEntry> it = items.iterator(); it.hasNext(); ) {
LogEntry next = it.next();

next.removePropertyOnlyPaths();
}

return items;
}

/**
* Removes duplicate entries, e.g. those coming form svn:externals.
*
Expand Down Expand Up @@ -147,6 +170,13 @@ public SubversionChangeLogSet getParent() {
return (SubversionChangeLogSet)super.getParent();
}

protected void removePropertyOnlyPaths() {
for (Iterator<Path> it = paths.iterator(); it.hasNext();) {
Path path = it.next();
if (path.isPropOnylChange()) it.remove();
}
}

// because of the classloader difference, we need to extend this method to make it accessible
// to the rest of SubversionSCM
@Override
Expand Down Expand Up @@ -342,7 +372,8 @@ public static class Path implements AffectedFile {
private LogEntry entry;
private char action;
private String value;

private String kind;

/**
* Gets the {@link LogEntry} of which this path is a member.
*/
Expand Down Expand Up @@ -379,7 +410,19 @@ public String getPath() {
public void setValue(String value) {
this.value = value;
}


public boolean isPropOnylChange() {

This comment has been minimized.

Copy link
@kutzi

kutzi Nov 22, 2012

Typo 'onyl' instead of 'only'.

return action == 'M' && "dir".equals(kind);
}

public String getKind() {
return kind;
}

public void setKind(String kind) {
this.kind = kind;
}

@Exported
public EditType getEditType() {
if( action=='A' )
Expand Down
42 changes: 39 additions & 3 deletions src/main/java/hudson/scm/SubversionSCM.java
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ public class SubversionSCM extends SCM implements Serializable {
@Deprecated
private Boolean doRevert;

private boolean ignoreDirPropChanges;


/**
* @deprecated as of 1.286
Expand Down Expand Up @@ -283,10 +285,20 @@ public SubversionSCM(List<ModuleLocation> locations,
browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages, includedRegions);
}

@DataBoundConstructor
/**
*
* @deprecated as of ...
*/
public SubversionSCM(List<ModuleLocation> locations, WorkspaceUpdater workspaceUpdater,
SubversionRepositoryBrowser browser, String excludedRegions, String excludedUsers, String excludedRevprop, String excludedCommitMessages,
String includedRegions) {
this(locations, workspaceUpdater, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages, includedRegions, false);
}

@DataBoundConstructor
public SubversionSCM(List<ModuleLocation> locations, WorkspaceUpdater workspaceUpdater,
SubversionRepositoryBrowser browser, String excludedRegions, String excludedUsers, String excludedRevprop, String excludedCommitMessages,
String includedRegions, boolean ignoreDirPropChanges) {
for (Iterator<ModuleLocation> itr = locations.iterator(); itr.hasNext();) {
ModuleLocation ml = itr.next();
String remote = Util.fixEmptyAndTrim(ml.remote);
Expand All @@ -301,6 +313,7 @@ public SubversionSCM(List<ModuleLocation> locations, WorkspaceUpdater workspaceU
this.excludedRevprop = excludedRevprop;
this.excludedCommitMessages = excludedCommitMessages;
this.includedRegions = includedRegions;
this.ignoreDirPropChanges = ignoreDirPropChanges;
}

/**
Expand Down Expand Up @@ -513,6 +526,11 @@ private Pattern[] getExcludedCommitMessagesPatterns() {
return patterns;
}

@Exported
public boolean isIgnoreDirPropChanges() {
return ignoreDirPropChanges;
}

/**
* Sets the <tt>SVN_REVISION</tt> environment variable during the build.
*/
Expand Down Expand Up @@ -1275,7 +1293,7 @@ public boolean findNonExcludedChanges(SVNURL url, long from, long to, ISVNAuthen
* Is there any exclusion rule?
*/
private boolean hasExclusionRule() {
return excludedPatterns.length>0 || !excludedUsers.isEmpty() || excludedRevprop != null || excludedCommitMessages.length>0 || includedPatterns.length>0;
return excludedPatterns.length>0 || !excludedUsers.isEmpty() || excludedRevprop != null || excludedCommitMessages.length>0 || includedPatterns.length>0 || ignoreDirPropChanges;
}

/**
Expand Down Expand Up @@ -1338,6 +1356,24 @@ private boolean checkLogEntry(SVNLogEntry logEntry) {
return false;
}

// dirPropChanges are changes that modifiy ('M') a directory, i.e. only
// exclude if there are NO changes on files or Adds/Removals
if (ignoreDirPropChanges) {
boolean contentChanged = false;
for (SVNLogEntryPath path : changedPaths.values()) {
if (path.getType() != 'M' || path.getKind() != SVNNodeKind.DIR) {
contentChanged = true;
break;
}
}
if (!contentChanged) {
listener.getLogger().println(Messages.SubversionSCM_pollChanges_ignoredRevision(
logEntry.getRevision(),
Messages.SubversionSCM_pollChanges_ignoredRevision_onlydirprops()));
return false;
}
}

// If there are included patterns, see which paths are included
List<String> includedPaths = new ArrayList<String>();
if (includedPatterns.length > 0) {
Expand Down Expand Up @@ -1390,7 +1426,7 @@ private boolean checkLogEntry(SVNLogEntry logEntry) {
}

public ChangeLogParser createChangeLogParser() {
return new SubversionChangeLogParser();
return new SubversionChangeLogParser(ignoreDirPropChanges);
}


Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/hudson/scm/SubversionSCM/config.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ THE SOFTWARE.
</table>
</f:repeatable>
</f:entry>
<f:entry title="${%Ignore Property changes}" field="ignoreDirPropChanges">
<f:checkbox />
</f:entry>

<f:dropdownDescriptorSelector title="${%Check-out Strategy}" field="workspaceUpdater" descriptors="${descriptor.getWorkspaceUpdaterDescriptors()}"/>

<t:listScmBrowsers name="svn.browser" />
Expand Down
Loading

0 comments on commit 13a1cc8

Please sign in to comment.