package org.tmatesoft.svn.core.internal.wc2.ng;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.*;
import org.tmatesoft.svn.core.internal.wc.admin.SVNTranslator;
import org.tmatesoft.svn.core.internal.wc.admin.SVNTranslatorInputStream;
import org.tmatesoft.svn.core.internal.wc17.SVNAmbientDepthFilterEditor17;
import org.tmatesoft.svn.core.internal.wc17.SVNReporter17;
import org.tmatesoft.svn.core.internal.wc17.SVNStatusEditor17;
import org.tmatesoft.svn.core.internal.wc17.SVNWCContext;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb;
import org.tmatesoft.svn.core.internal.wc17.db.Structure;
import org.tmatesoft.svn.core.internal.wc17.db.StructureFields;
import org.tmatesoft.svn.core.internal.wc2.SvnRepositoryAccess;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNCapability;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.ISVNDiffStatusHandler;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.core.wc2.ISvnObjectReceiver;
import org.tmatesoft.svn.core.wc2.SvnChecksum;
import org.tmatesoft.svn.core.wc2.SvnStatus;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import org.tmatesoft.svn.util.SVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class SvnNgDiffUtil {
public static void doDiffSummarizeReposWC(SvnTarget target1, SVNRevision revision1, SVNRevision pegRevision, SvnTarget target2, SVNRevision revision2, boolean reverse, SvnNgRepositoryAccess repositoryAccess, SVNWCContext context, boolean useGitDiffFormat, SVNDepth depth, boolean useAncestry, Collection<String> changelists, boolean showCopiesAsAdds, ISvnDiffGenerator generator, ISVNDiffStatusHandler handler, ISVNCanceller canceller) throws SVNException {
assert !target2.isURL();
SVNURL url1 = repositoryAccess.getTargetURL(target1);
String target = context.getActualTarget(target2.getFile());
File anchor;
if (target == null || target.length() == 0) {
anchor = target2.getFile();
} else {
anchor = SVNFileUtil.getParentFile(target2.getFile());
}
SVNURL anchorUrl = context.getNodeUrl(anchor);
if (anchorUrl == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "Directory ''{0}'' has no URL", anchor);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
// final ISvnDiffGenerator generator = getDiffGenerator();
if (pegRevision != SVNRevision.UNDEFINED) {
url1 = repositoryAccess.getLocations(null, target1, pegRevision, revision1, SVNRevision.UNDEFINED).get(SvnRepositoryAccess.LocationsInfo.startUrl);
if (generator != null) {
if (!reverse) {
generator.setOriginalTargets(SvnTarget.fromURL(url1), SvnTarget.fromURL(anchorUrl.appendPath(target, false)));
generator.setAnchors(SvnTarget.fromURL(url1), SvnTarget.fromURL(anchorUrl.appendPath(target, false)));
} else {
generator.setOriginalTargets(SvnTarget.fromURL(anchorUrl.appendPath(target, false)), SvnTarget.fromURL(url1));
generator.setAnchors(SvnTarget.fromURL(anchorUrl.appendPath(target, false)), SvnTarget.fromURL(url1));
}
}
} else {
if (generator != null) {
if (!reverse) {
generator.setOriginalTargets(target1, target2);
generator.setAnchors(target1, target2);
} else {
generator.setOriginalTargets(target2, target1);
generator.setAnchors(target2, target1);
}
}
}
ISvnDiffCallback callback = new SvnDiffSummarizeCallback(target1.getFile(), reverse, anchorUrl, anchor, handler);
SVNRepository repository2 = repositoryAccess.createRepository(anchorUrl, null, true);
if (useGitDiffFormat && generator != null) {
File wcRoot = context.getDb().getWCRoot(anchor);
generator.setRepositoryRoot(SvnTarget.fromFile(wcRoot));
}
boolean serverSupportsDepth = repository2.hasCapability(SVNCapability.DEPTH);
long revisionNumber1 = repositoryAccess.getRevisionNumber(repository2, url1.equals(target1.getURL()) ? null : target1, revision1, null).lng(SvnRepositoryAccess.RevisionsPair.revNumber);
// SvnDiffCallback callback = createDiffCallback(generator, reverse, revisionNumber1, -1);
SVNReporter17 reporter = new SVNReporter17(target2.getFile(), context, false, !serverSupportsDepth, depth, false, false, true, false, SVNDebugLog.getDefaultLog());
boolean revisionIsBase = isRevisionBase(revision2);
SvnDiffEditor svnDiffEditor = new SvnDiffEditor(anchor, target, callback, depth, context, reverse, revisionIsBase, showCopiesAsAdds, !useAncestry, changelists, useGitDiffFormat, canceller);
ISVNUpdateEditor updateEditor = svnDiffEditor;
if (!serverSupportsDepth && depth == SVNDepth.UNKNOWN) {
updateEditor = new SVNAmbientDepthFilterEditor17(updateEditor, context, anchor, target, revisionIsBase);
}
ISVNEditor editor = SVNCancellableEditor.newInstance(updateEditor, canceller, SVNDebugLog.getDefaultLog());
try{
repository2.diff(url1, revisionNumber1, revisionNumber1, target, !useAncestry, getDiffDepth(depth), true, reporter, editor);
} finally {
svnDiffEditor.cleanup();
}
}
public static void doDiffWCWC(File localAbsPath, SvnNgRepositoryAccess repositoryAccess, SVNWCContext context, SVNDepth depth, boolean useAncestry, Collection<String> changelists, boolean showCopiesAsAdds, boolean useGitDiffFormat, ISvnDiffGenerator generator, ISvnDiffCallback callback, ISVNCanceller canceller) throws SVNException {
assert SVNFileUtil.isAbsolute(localAbsPath);
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
SVNNodeKind kind = context.getDb().readKind(localAbsPath, false, true, false);
File anchorAbsPath;
if (kind == SVNNodeKind.DIR) {
anchorAbsPath = localAbsPath;
} else {
anchorAbsPath = SVNFileUtil.getParentFile(localAbsPath);
}
if (useGitDiffFormat) {
showCopiesAsAdds = true;
}
if (showCopiesAsAdds) {
useAncestry = true;
}
ISvnDiffCallback2 callback2 = new SvnDiffCallbackWrapper(callback, true, anchorAbsPath);
if (!showCopiesAsAdds && !useGitDiffFormat) {
callback2 = new SvnCopyAsChangedDiffCallback(callback2);
}
boolean getAll;
if (showCopiesAsAdds || useGitDiffFormat || useAncestry) {
getAll = true;
} else {
getAll = false;
}
DiffStatusCallback diffStatusCallback = new DiffStatusCallback(anchorAbsPath, !useAncestry, showCopiesAsAdds, changelists, callback2, context);
SVNStatusEditor17 statusEditor = new SVNStatusEditor17(localAbsPath, context, null, true, getAll, depth, diffStatusCallback);
statusEditor.walkStatus(localAbsPath, depth, getAll, true, false, null);
while (diffStatusCallback.currentNode != null) {
DiffStatusCallback.NodeState ns = diffStatusCallback.currentNode;
if (!ns.skip) {
if (ns.propChanges != null) {
callback2.dirChanged(result, ns.relPath, ns.leftSource, ns.rightSource, ns.leftProps, ns.rightProps, ns.propChanges, null);
} else {
callback2.dirClosed(result, ns.relPath, ns.leftSource, ns.rightSource, null);
}
}
diffStatusCallback.currentNode = ns.parent;
}
}
private static SVNDepth getDiffDepth(SVNDepth depth) {
return depth != SVNDepth.INFINITY ? depth : SVNDepth.UNKNOWN;
}
private static boolean isRevisionBase(SVNRevision revision2) {
return revision2 == SVNRevision.BASE;
}
private static class DiffStatusCallback implements ISvnObjectReceiver<SvnStatus> {
private final File anchorAbsPath;
private final boolean ignoreAncestry;
private final boolean showCopiesAsAdds;
private final Collection<String> changelists;
private final ISvnDiffCallback2 callback;
private final ISVNWCDb db;
private final SVNWCContext context;
private NodeState currentNode;
private DiffStatusCallback(File anchorAbsPath, boolean ignoreAncestry, boolean showCopiesAsAdds, Collection<String> changelists, ISvnDiffCallback2 callback, SVNWCContext context) {
this.anchorAbsPath = anchorAbsPath;
this.ignoreAncestry = ignoreAncestry;
this.showCopiesAsAdds = showCopiesAsAdds;
this.changelists = changelists;
this.callback = callback;
this.db = context.getDb();
this.context = context;
}
public void receive(SvnTarget target, SvnStatus status) throws SVNException {
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
File localAbsPath = target.getFile();
if (!status.isVersioned()) {
return;
}
if (status.getNodeStatus() == SVNStatusType.STATUS_CONFLICTED &&
status.getTextStatus() == SVNStatusType.STATUS_NONE &&
status.getPropertiesStatus() == SVNStatusType.STATUS_NONE) {
return;
}
if (status.getNodeStatus() == SVNStatusType.STATUS_NORMAL && !status.isCopied()) {
return;
}
while (currentNode != null && !SVNPathUtil.isAncestor(SVNFileUtil.getFilePath(currentNode.localAbsPath), SVNFileUtil.getFilePath(localAbsPath))) {
NodeState ns = currentNode;
if (!ns.skip) {
if (ns.propChanges != null) {
callback.dirChanged(result, ns.relPath, ns.leftSource, ns.rightSource, ns.leftProps, ns.rightProps, ns.propChanges, null);
} else {
callback.dirClosed(result, ns.relPath, ns.leftSource, ns.rightSource, null);
}
}
currentNode = ns.parent;
}
ensureState(SVNFileUtil.getParentFile(localAbsPath), false);
if (currentNode != null && currentNode.skipChildren) {
return;
}
if (changelists != null && (status.getChangelist() == null || !changelists.contains(status.getChangelist()))) {
return;
}
SVNNodeKind baseKind = SVNNodeKind.UNKNOWN;
SVNNodeKind dbKind = status.getKind();
SVNDepth depthBelowHere = SVNDepth.UNKNOWN;
File childAbsPath = localAbsPath;
File childRelPath = SVNFileUtil.skipAncestor(anchorAbsPath, localAbsPath);
boolean reposOnly = false;
boolean localOnly = false;
Structure<StructureFields.NodeInfo> nodeInfoStructure = db.readInfo(localAbsPath, StructureFields.NodeInfo.status, StructureFields.NodeInfo.haveBase);
ISVNWCDb.SVNWCDbStatus dbStatus = nodeInfoStructure.get(StructureFields.NodeInfo.status);
boolean haveBase = nodeInfoStructure.is(StructureFields.NodeInfo.haveBase);
ISVNWCDb.SVNWCDbStatus baseStatus;
if (!haveBase) {
localOnly = true;
} else if (dbStatus == ISVNWCDb.SVNWCDbStatus.Normal) {
baseKind = dbKind;
} else if (dbStatus == ISVNWCDb.SVNWCDbStatus.Deleted) {
reposOnly = true;
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(localAbsPath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.status, ISVNWCDb.WCDbBaseInfo.BaseInfoField.kind);
baseStatus = baseInfo.status;
baseKind = baseInfo.kind == ISVNWCDb.SVNWCDbKind.Dir ? SVNNodeKind.DIR : SVNNodeKind.FILE;
if (baseStatus != ISVNWCDb.SVNWCDbStatus.Normal) {
return;
}
} else {
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(localAbsPath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.status, ISVNWCDb.WCDbBaseInfo.BaseInfoField.kind);
baseStatus = baseInfo.status;
baseKind = baseInfo.kind == ISVNWCDb.SVNWCDbKind.Dir ? SVNNodeKind.DIR : SVNNodeKind.FILE;
if (baseStatus != ISVNWCDb.SVNWCDbStatus.Normal) {
localOnly = true;
} else if (baseKind != dbKind || !ignoreAncestry) {
reposOnly = true;
localOnly = true;
}
}
if (reposOnly) {
if (baseKind == SVNNodeKind.FILE) {
diffBaseOnlyFile(childAbsPath, childRelPath, SVNRepository.INVALID_REVISION, this.db, this.callback);
} else if (baseKind == SVNNodeKind.DIR) {
diffBaseOnlyDirectory(childAbsPath, childRelPath, SVNRepository.INVALID_REVISION, depthBelowHere, this.db, this.callback);
}
} else if (!localOnly) {
if (dbKind == SVNNodeKind.FILE) {
diffBaseWorkingDiff(childAbsPath, childRelPath, SVNRepository.INVALID_REVISION, changelists, false, context, callback);
} else if (dbKind == SVNNodeKind.DIR) {
ensureState(localAbsPath, false);
if (status.getPropertiesStatus() != SVNStatusType.STATUS_NONE && status.getPropertiesStatus() != SVNStatusType.STATUS_NORMAL) {
currentNode.leftProps = db.getBaseProps(localAbsPath);
currentNode.rightProps = db.readProperties(localAbsPath);
currentNode.propChanges = currentNode.leftProps.compareTo(currentNode.rightProps);
}
}
}
if (localOnly) {
if (dbKind == SVNNodeKind.FILE) {
diffLocalOnlyFile(childAbsPath, childRelPath, changelists, false, context, callback);
} else if (dbKind == SVNNodeKind.DIR) {
diffLocalOnlyDirectory(childAbsPath, childRelPath, depthBelowHere, changelists, false, context, callback);
}
}
if (dbKind == SVNNodeKind.DIR && (localOnly || reposOnly)) {
ensureState(localAbsPath, true);
}
}
private void ensureState(File localAbsPath, boolean recursiveSkip) throws SVNException {
if (currentNode == null) {
if (!SVNPathUtil.isAncestor(SVNFileUtil.getFilePath(anchorAbsPath), SVNFileUtil.getFilePath(localAbsPath))) {
return;
}
ensureState(SVNFileUtil.getParentFile(localAbsPath), false);
} else if (SVNPathUtil.isAncestor(SVNFileUtil.getFilePath(currentNode.localAbsPath), SVNFileUtil.getFilePath(localAbsPath))) {
ensureState(SVNFileUtil.getParentFile(localAbsPath), false);
} else {
return;
}
if (currentNode != null && currentNode.skipChildren) {
return;
}
NodeState ns = new NodeState();
ns.localAbsPath = localAbsPath;
ns.relPath = SVNFileUtil.skipAncestor(anchorAbsPath, ns.localAbsPath);
ns.parent = currentNode;
currentNode = ns;
if (recursiveSkip) {
ns.skip = true;
ns.skipChildren = true;
return;
}
long revision;
try {
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(localAbsPath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.revision);
revision = baseInfo.revision;
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND) {
throw e;
}
revision = 0;
}
ns.leftSource = new SvnDiffSource(revision);
ns.rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
callback.dirOpened(result, ns.relPath, ns.leftSource, ns.rightSource, null, null);
ns.skip = result.skip;
ns.skipChildren = result.skipChildren;
}
private static class NodeState {
private NodeState parent;
private File localAbsPath;
private File relPath;
private SvnDiffSource leftSource;
private SvnDiffSource rightSource;
private SvnDiffSource copySource;
private boolean skip;
private boolean skipChildren;
private SVNProperties leftProps;
private SVNProperties rightProps;
private SVNProperties propChanges;
}
}
protected static void diffBaseOnlyFile(File localAbsPath, File relPath, long revision, ISVNWCDb db, ISvnDiffCallback2 callback) throws SVNException {
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(localAbsPath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.status, ISVNWCDb.WCDbBaseInfo.BaseInfoField.kind,
ISVNWCDb.WCDbBaseInfo.BaseInfoField.revision, ISVNWCDb.WCDbBaseInfo.BaseInfoField.checksum, ISVNWCDb.WCDbBaseInfo.BaseInfoField.props);
ISVNWCDb.SVNWCDbStatus status = baseInfo.status;
ISVNWCDb.SVNWCDbKind kind = baseInfo.kind;
if (!SVNRevision.isValidRevisionNumber(revision)) {
revision = baseInfo.revision;
}
SvnChecksum checksum = baseInfo.checksum;
SVNProperties props = baseInfo.props;
assert status == ISVNWCDb.SVNWCDbStatus.Normal && kind == ISVNWCDb.SVNWCDbKind.File && checksum != null;
SvnDiffSource leftSource = new SvnDiffSource(revision);
result.reset();
callback.fileOpened(result, relPath, leftSource, null, null, false, null);
boolean skip = result.skip;
if (skip) {
return;
}
File pristineFile = db.getPristinePath(localAbsPath, checksum);
result.reset();
callback.fileDeleted(result, relPath, leftSource, pristineFile, props);
}
protected static void diffBaseOnlyDirectory(File localAbsPath, File relPath, long revision, SVNDepth depth, ISVNWCDb db, ISvnDiffCallback2 callback) throws SVNException {
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
long reportRevision = revision;
if (!SVNRevision.isValidRevisionNumber(reportRevision)) {
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(localAbsPath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.revision);
reportRevision = baseInfo.revision;
}
SvnDiffSource leftSource = new SvnDiffSource(reportRevision);
result.reset();
callback.dirOpened(result, relPath, leftSource, null, null, null);
boolean skip = result.skip;
boolean skipChildren = result.skipChildren;
if (!skipChildren && (depth == SVNDepth.UNKNOWN || depth.compareTo(SVNDepth.EMPTY) > 0)) {
Map<String, ISVNWCDb.WCDbBaseInfo> nodes = db.getBaseChildrenMap(localAbsPath, true);
List<String> children = new ArrayList<String>(nodes.keySet());
Collections.sort(children);
for (String name : children) {
ISVNWCDb.WCDbBaseInfo info = nodes.get(name);
if (info.status != ISVNWCDb.SVNWCDbStatus.Normal) {
continue;
}
File childAbsPath = SVNFileUtil.createFilePath(localAbsPath, name);
File childRelPath = SVNFileUtil.createFilePath(relPath, name);
switch (info.kind) {
case File:
case Symlink:
SvnNgDiffUtil.diffBaseOnlyFile(childAbsPath, childRelPath, revision, db, callback);
break;
case Dir:
if (depth.compareTo(SVNDepth.FILES) > 0 || depth == SVNDepth.UNKNOWN) {
SVNDepth depthBelowHere = depth;
if (depthBelowHere == SVNDepth.IMMEDIATES) {
depthBelowHere = SVNDepth.EMPTY;
}
diffBaseOnlyDirectory(childAbsPath, childRelPath, revision, depthBelowHere, db, callback);
}
break;
default:
break;
}
}
}
if (!skip) {
SVNProperties props = db.getBaseProps(localAbsPath);
result.reset();
callback.dirDeleted(result, relPath, leftSource, props, null);
}
}
protected static void diffLocalOnlyFile(File localAbsPath, File relPath, Collection<String> changelists, boolean diffPristine, SVNWCContext context, ISvnDiffCallback2 callback) throws SVNException {
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
ISVNWCDb db = context.getDb();
Structure<StructureFields.NodeInfo> nodeInfoStructure = db.readInfo(localAbsPath, StructureFields.NodeInfo.status, StructureFields.NodeInfo.kind, StructureFields.NodeInfo.revision,
StructureFields.NodeInfo.checksum,
StructureFields.NodeInfo.originalReposRelpath, StructureFields.NodeInfo.originalRevision,
StructureFields.NodeInfo.changelist,
StructureFields.NodeInfo.hadProps, StructureFields.NodeInfo.propsMod);
ISVNWCDb.SVNWCDbStatus status = nodeInfoStructure.get(StructureFields.NodeInfo.status);
ISVNWCDb.SVNWCDbKind kind = nodeInfoStructure.get(StructureFields.NodeInfo.kind);
long revision = nodeInfoStructure.lng(StructureFields.NodeInfo.revision);
SvnChecksum checksum = nodeInfoStructure.get(StructureFields.NodeInfo.checksum);
File originalReposRelPath = nodeInfoStructure.get(StructureFields.NodeInfo.originalReposRelpath);
long originalRevision = nodeInfoStructure.lng(StructureFields.NodeInfo.originalRevision);
String changelist = nodeInfoStructure.get(StructureFields.NodeInfo.changelist);
boolean hadProps = nodeInfoStructure.is(StructureFields.NodeInfo.hadProps);
boolean propMods = nodeInfoStructure.is(StructureFields.NodeInfo.propsMod);
assert kind == ISVNWCDb.SVNWCDbKind.File && (status == ISVNWCDb.SVNWCDbStatus.Normal || status == ISVNWCDb.SVNWCDbStatus.Added || (status == ISVNWCDb.SVNWCDbStatus.Deleted && diffPristine));
if (changelist != null && changelists != null && !changelists.contains(changelist)) {
return;
}
SVNProperties pristineProps;
if (status == ISVNWCDb.SVNWCDbStatus.Deleted) {
assert diffPristine;
Structure<StructureFields.PristineInfo> pristineInfoStructure = db.readPristineInfo(localAbsPath);
status = pristineInfoStructure.get(StructureFields.PristineInfo.status);
kind = pristineInfoStructure.get(StructureFields.PristineInfo.kind);
checksum = pristineInfoStructure.get(StructureFields.PristineInfo.checksum);
hadProps = pristineInfoStructure.is(StructureFields.PristineInfo.hadProps);
pristineProps = pristineInfoStructure.get(StructureFields.PristineInfo.props);
propMods = false;
} else if (!hadProps) {
pristineProps = new SVNProperties();
} else {
pristineProps = db.readPristineProperties(localAbsPath);
}
SvnDiffSource copyFromSource = null;
if (originalReposRelPath != null) {
copyFromSource = new SvnDiffSource(originalRevision);
copyFromSource.setReposRelPath(originalReposRelPath);
}
boolean fileMod = true;
SvnDiffSource rightSource;
if (propMods || !SVNRevision.isValidRevisionNumber(revision)) {
rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
} else {
if (diffPristine) {
fileMod = false;
} else {
fileMod = context.isTextModified(localAbsPath, false);
}
if (!fileMod) {
rightSource = new SvnDiffSource(revision);
} else {
rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
}
}
result.reset();
callback.fileOpened(result, relPath, null, rightSource, copyFromSource, false, null);
boolean skip = result.skip;
if (skip) {
return;
}
SVNProperties rightProps;
if (propMods && !diffPristine) {
rightProps = db.readProperties(localAbsPath);
} else {
rightProps = new SVNProperties(pristineProps);
}
File pristineFile;
if (checksum != null) {
pristineFile = db.getPristinePath(localAbsPath, checksum);
} else {
pristineFile = null;
}
File translatedFile;
if (diffPristine) {
translatedFile = pristineFile;
} else {
translatedFile = context.getTranslatedFile(localAbsPath, localAbsPath, true, false, true, false, false);
}
result.reset();
callback.fileAdded(result, relPath, copyFromSource, rightSource,
copyFromSource != null ? pristineFile : null,
translatedFile,
copyFromSource != null ? pristineProps : null,
rightProps);
}
protected static void diffLocalOnlyDirectory(File localAbsPath, File relPath, SVNDepth depth, Collection<String> changelists, boolean diffPristine, SVNWCContext context, ISvnDiffCallback2 callback) throws SVNException {
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
ISVNWCDb db = context.getDb();
boolean skip = false;
boolean skipChildren = false;
SvnDiffSource rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
SVNDepth depthBelowHere = depth;
result.reset();
callback.dirOpened(result, relPath, null, rightSource, null, null);
skip = result.skip;
skipChildren = result.skipChildren;
Map<String, ISVNWCDb.SVNWCDbInfo> nodes = new HashMap<String, ISVNWCDb.SVNWCDbInfo>();
db.readChildren(localAbsPath, nodes, new HashSet<String>());
if (depthBelowHere == SVNDepth.IMMEDIATES) {
depthBelowHere = SVNDepth.EMPTY;
}
List<String> children = new ArrayList<String>(nodes.keySet());
Collections.sort(children);
for (String name : children) {
File childAbsPath = SVNFileUtil.createFilePath(localAbsPath, name);
ISVNWCDb.SVNWCDbInfo info = nodes.get(name);
if (info.status.isNotPresent()) {
continue;
}
if (!diffPristine && info.status == ISVNWCDb.SVNWCDbStatus.Deleted) {
continue;
}
File childRelPath = SVNFileUtil.createFilePath(relPath, name);
switch (info.kind) {
case File:
case Symlink:
diffLocalOnlyFile(childAbsPath, childRelPath, changelists, diffPristine, context, callback);
break;
case Dir:
if (depth.compareTo(SVNDepth.FILES) > 0 || depth == SVNDepth.UNKNOWN) {
diffLocalOnlyDirectory(childAbsPath, childRelPath, depthBelowHere, changelists, diffPristine, context, callback);
}
break;
default:
break;
}
}
if (!skip) {
SVNProperties rightProps;
if (diffPristine) {
rightProps = db.readPristineProperties(localAbsPath);
} else {
rightProps = db.readProperties(localAbsPath);
}
result.reset();
callback.dirAdded(result, relPath, null, rightSource, null, rightProps, null);
}
}
protected static void diffBaseWorkingDiff(File localAbsPath, File relPath, long revision, Collection<String> changeists, boolean diffPristine, SVNWCContext context, ISvnDiffCallback2 callback) throws SVNException {
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
ISVNWCDb db = context.getDb();
Structure<StructureFields.NodeInfo> nodeInfoStructure = db.readInfo(localAbsPath, StructureFields.NodeInfo.status, StructureFields.NodeInfo.revision,
StructureFields.NodeInfo.checksum, StructureFields.NodeInfo.recordedSize, StructureFields.NodeInfo.recordedTime,
StructureFields.NodeInfo.changelist, StructureFields.NodeInfo.hadProps, StructureFields.NodeInfo.propsMod);
ISVNWCDb.SVNWCDbStatus status = nodeInfoStructure.get(StructureFields.NodeInfo.status);
long dbRevision = nodeInfoStructure.lng(StructureFields.NodeInfo.revision);
SvnChecksum workingChecksum = nodeInfoStructure.get(StructureFields.NodeInfo.checksum);
long recordedSize = nodeInfoStructure.lng(StructureFields.NodeInfo.recordedSize);
long recordedTime = nodeInfoStructure.lng(StructureFields.NodeInfo.recordedTime);
String changelist = nodeInfoStructure.get(StructureFields.NodeInfo.changelist);
boolean hadProps = nodeInfoStructure.is(StructureFields.NodeInfo.hadProps);
boolean propsMod = nodeInfoStructure.is(StructureFields.NodeInfo.propsMod);
SvnChecksum checksum = workingChecksum;
assert status == ISVNWCDb.SVNWCDbStatus.Normal || status == ISVNWCDb.SVNWCDbStatus.Added || (status == ISVNWCDb.SVNWCDbStatus.Deleted && diffPristine);
if (changeists != null && !changeists.contains(changelist)) {
return;
}
boolean filesSame = false;
if (status != ISVNWCDb.SVNWCDbStatus.Normal) {
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(localAbsPath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.status, ISVNWCDb.WCDbBaseInfo.BaseInfoField.revision, ISVNWCDb.WCDbBaseInfo.BaseInfoField.checksum, ISVNWCDb.WCDbBaseInfo.BaseInfoField.hadProps);
ISVNWCDb.SVNWCDbStatus baseStatus = baseInfo.status;
dbRevision = baseInfo.revision;
checksum = baseInfo.checksum;
hadProps = baseInfo.hadProps;
recordedSize = -1;
recordedTime = 0;
propsMod = true;
} else if (diffPristine) {
filesSame = true;
} else {
SVNNodeKind kind = SVNFileType.getNodeKind(SVNFileType.getType(localAbsPath));
if (kind != SVNNodeKind.FILE || (kind == SVNNodeKind.FILE && SVNFileUtil.getFileLength(localAbsPath) == recordedSize && SVNFileUtil.getFileLastModifiedMicros(localAbsPath) == recordedTime)) {
filesSame = true;
}
}
if (filesSame && !propsMod) {
return;
}
assert checksum != null;
if (!SVNRevision.isValidRevisionNumber(revision)) {
revision = dbRevision;
}
SvnDiffSource leftSource = new SvnDiffSource(revision);
SvnDiffSource rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
result.reset();
callback.fileOpened(result, relPath, leftSource, rightSource, null, false, null);
boolean skip = result.skip;
if (skip) {
return;
}
File pristineFile = db.getPristinePath(localAbsPath, checksum);
File localFile;
if (diffPristine) {
localFile = db.getPristinePath(localAbsPath, workingChecksum);
} else if (! (hadProps || propsMod)) {
localFile = localAbsPath;
} else if (filesSame) {
localFile = pristineFile;
} else {
localFile = context.getTranslatedFile(localAbsPath, localAbsPath, true, false, true, false, false);
}
if (!filesSame) {
filesSame = SVNFileUtil.compareFiles(localFile, pristineFile, null);
}
SVNProperties baseProps;
if (hadProps) {
baseProps = db.getBaseProps(localAbsPath);
} else {
baseProps = new SVNProperties();
}
SVNProperties localProps;
if (status == ISVNWCDb.SVNWCDbStatus.Normal && (diffPristine || !propsMod)) {
localProps = baseProps;
} else if (diffPristine) {
localProps = db.readPristineProperties(localAbsPath);
} else {
localProps = db.readProperties(localAbsPath);
}
SVNProperties propChanges = baseProps.compareTo(localProps);
if (propChanges.size() > 0 || !filesSame) {
result.reset();
callback.fileChanged(result, relPath, leftSource, rightSource, pristineFile, localFile, baseProps, localProps, !filesSame, propChanges);
} else {
result.reset();
callback.fileClosed(result, relPath, leftSource, rightSource);
}
}
public static void doArbitraryNodesDiff(SvnTarget target1, SvnTarget target2, SVNDepth depth, SVNWCContext context, ISvnDiffCallback callback, ISVNCanceller canceller) throws SVNException {
File path1 = target1.getFile();
File path2 = target2.getFile();
SVNNodeKind kind1 = SVNFileType.getNodeKind(SVNFileType.getType(path1));
SVNNodeKind kind2 = SVNFileType.getNodeKind(SVNFileType.getType(path2));
if (kind1 != kind2) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.NODE_UNEXPECTED_KIND, "''{0}'' is not the same node kind as ''{1}''", path1, path2);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
if (depth == SVNDepth.UNKNOWN) {
depth = SVNDepth.INFINITY;
}
if (kind1 == SVNNodeKind.FILE) {
doArbitraryFilesDiff(path1, path2, SVNFileUtil.createFilePath(SVNFileUtil.getFileName(path1)), false, false, null, context, callback, canceller);
} else if (kind1 == SVNNodeKind.DIR) {
doArbitraryDirsDiff(path1, path2, null, null, depth, context, callback, canceller);
} else {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.NODE_UNEXPECTED_KIND, "''{0}'' is not a file or directory", kind1 == SVNNodeKind.NONE ? path1 : path2);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
}
private static void doArbitraryFilesDiff(File localAbsPath1, File localAbsPath2, File path, boolean file1IsEmpty, boolean file2IsEmpty, SVNProperties originalPropertiesOverride, SVNWCContext context, ISvnDiffCallback callback, ISVNCanceller canceller) throws SVNException {
if (canceller != null) {
canceller.checkCancelled();
}
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
SVNProperties originalProps;
if (originalPropertiesOverride != null) {
originalProps = originalPropertiesOverride;
} else {
if (localAbsPath1 != null) {
try {
originalProps = context.getActualProps(localAbsPath1);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND && e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_WORKING_COPY) {
throw e;
}
originalProps = new SVNProperties();
}
} else {
originalProps = new SVNProperties();
}
}
SVNProperties modifiedProps;
if (localAbsPath2 != null) {
try {
modifiedProps = context.getActualProps(localAbsPath2);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND && e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_WORKING_COPY) {
throw e;
}
modifiedProps = new SVNProperties();
}
} else {
modifiedProps = new SVNProperties();
}
SVNProperties propChanges = originalProps.compareTo(modifiedProps);
String originalMimeType = originalProps.getStringValue(SVNProperty.MIME_TYPE);
if (!file1IsEmpty && originalMimeType == null) {
String mimeType = SVNFileUtil.detectMimeType(localAbsPath1, context.getOptions().getFileExtensionsToMimeTypes());
if (mimeType != null) {
originalMimeType = mimeType;
}
}
String modifiedMimeType = modifiedProps.getStringValue(SVNProperty.MIME_TYPE);
if (!file2IsEmpty && modifiedMimeType == null) {
String mimeType = SVNFileUtil.detectMimeType(localAbsPath2, context.getOptions().getFileExtensionsToMimeTypes());
if (mimeType != null) {
modifiedMimeType = mimeType;
}
}
if (file1IsEmpty && !file2IsEmpty) {
callback.fileAdded(result, path, localAbsPath1, localAbsPath2, SVNRepository.INVALID_REVISION, SVNRepository.INVALID_REVISION,
originalMimeType, modifiedMimeType, null, SVNRepository.INVALID_REVISION, propChanges, originalProps);
} else if (!file1IsEmpty && file2IsEmpty) {
callback.fileDeleted(result, path, localAbsPath1, localAbsPath2, originalMimeType, modifiedMimeType, originalProps);
} else {
InputStream inputStream1 = SVNFileUtil.openFileForReading(localAbsPath1);
InputStream inputStream2 = SVNFileUtil.openFileForReading(localAbsPath2);
try {
if (originalProps != null) {
byte[] eol = SVNTranslator.getEOL(originalProps.getStringValue(SVNProperty.EOL_STYLE), context.getOptions());
if (eol != null) {
inputStream1 = new SVNTranslatorInputStream(inputStream1, eol, true, null, false);
}
}
if (modifiedProps != null) {
byte[] eol = SVNTranslator.getEOL(modifiedProps.getStringValue(SVNProperty.EOL_STYLE), context.getOptions());
if (eol != null) {
inputStream2 = new SVNTranslatorInputStream(inputStream2, eol, true, null, false);
}
}
boolean same = SVNFileUtil.compare(inputStream1, inputStream2);
if (!same || propChanges.size() > 0) {
callback.fileChanged(result, path, same ? null : localAbsPath1, same ? null : localAbsPath2, SVNRepository.INVALID_REVISION, SVNRepository.INVALID_REVISION, originalMimeType, modifiedMimeType, propChanges, originalProps);
}
} finally {
SVNFileUtil.closeFile(inputStream1);
SVNFileUtil.closeFile(inputStream2);
}
}
}
private static void doArbitraryDirsDiff(File localAbsPath1, File localAbsPath2, File rootAbsPath1, File rootAbsPath2, SVNDepth depth, SVNWCContext context, ISvnDiffCallback callback, ISVNCanceller canceller) throws SVNException {
SVNNodeKind kind1 = null;
try {
kind1 = SVNFileType.getNodeKind(SVNFileType.getType(localAbsPath1.getCanonicalFile()));
} catch (IOException e) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
ArbitraryDiffWalker diffWalker = new ArbitraryDiffWalker();
diffWalker.recursingWithinAddedSubtree = (kind1 != SVNNodeKind.DIR);
diffWalker.root1AbsPath = rootAbsPath1 != null ? rootAbsPath1 : localAbsPath1;
diffWalker.root2AbsPath = rootAbsPath2 != null ? rootAbsPath2 : localAbsPath2;
diffWalker.recursingWithinAdmDir = false;
if (depth.compareTo(SVNDepth.IMMEDIATES) <= 0) {
arbitraryDiffThisDir(diffWalker, localAbsPath1, depth, context, callback, canceller);
} else if (depth == SVNDepth.INFINITY) {
walkDirectory(diffWalker.recursingWithinAddedSubtree ? localAbsPath2 : localAbsPath1, diffWalker, context, callback, canceller);
}
}
private static void arbitraryDiffThisDir(ArbitraryDiffWalker diffWalker, File localAbsPath, SVNDepth depth, SVNWCContext context, ISvnDiffCallback callback, ISVNCanceller canceller) throws SVNException {
SvnDiffCallbackResult result = new SvnDiffCallbackResult();
if (diffWalker.recursingWithinAdmDir) {
if (SVNFileUtil.skipAncestor(diffWalker.admDirAbsPath, localAbsPath) != null) {
return;
} else {
diffWalker.recursingWithinAdmDir = false;
diffWalker.admDirAbsPath = null;
}
} else if (SVNFileUtil.getFileName(localAbsPath).equals(SVNFileUtil.getAdminDirectoryName())) {
diffWalker.recursingWithinAdmDir = true;
diffWalker.admDirAbsPath = localAbsPath;
return;
}
File childRelPath;
if (diffWalker.recursingWithinAddedSubtree) {
childRelPath = SVNFileUtil.skipAncestor(diffWalker.root2AbsPath, localAbsPath);
} else {
childRelPath = SVNFileUtil.skipAncestor(diffWalker.root1AbsPath, localAbsPath);
}
if (childRelPath == null) {
return;
}
File localAbsPath1 = SVNFileUtil.createFilePath(diffWalker.root1AbsPath, childRelPath);
SVNNodeKind kind1 = null;
try {
kind1 = SVNFileType.getNodeKind(SVNFileType.getType(localAbsPath1.getCanonicalFile()));
} catch (IOException e) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
File localAbsPath2 = SVNFileUtil.createFilePath(diffWalker.root2AbsPath, childRelPath);
SVNNodeKind kind2 = null;
try {
kind2 = SVNFileType.getNodeKind(SVNFileType.getType(localAbsPath2.getCanonicalFile()));
} catch (IOException e) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
File[] children1 = null;
if (depth.compareTo(SVNDepth.EMPTY) > 0) {
if (kind1 == SVNNodeKind.DIR) {
children1 = SVNFileListUtil.listFiles(localAbsPath1);
} else {
children1 = new File[0];
}
}
File[] children2 = null;
if (kind2 == SVNNodeKind.DIR) {
SVNProperties originalProps;
try {
originalProps = context.getActualProps(localAbsPath1);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND && e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_WORKING_COPY) {
throw e;
}
originalProps = new SVNProperties();
}
SVNProperties modifiedProps;
try {
modifiedProps = context.getActualProps(localAbsPath2);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND && e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_WORKING_COPY) {
throw e;
}
modifiedProps = new SVNProperties();
}
SVNProperties propChanges = originalProps.compareTo(modifiedProps);
if (propChanges.size() > 0) {
callback.dirPropsChanged(result, childRelPath, false, propChanges, originalProps);
}
if (depth.compareTo(SVNDepth.EMPTY) > 0) {
children2 = SVNFileListUtil.listFiles(localAbsPath2);
}
} else if (depth.compareTo(SVNDepth.EMPTY) > 0) {
children2 = new File[0];
}
if (depth.compareTo(SVNDepth.EMPTY) <= 0) {
return;
}
Set<String> mergedChildren = new HashSet<String>();
if (children1 != null) {
for (File child1 : children1) {
mergedChildren.add(SVNFileUtil.getFileName(child1));
}
}
if (children2 != null) {
for (File child2 : children2) {
mergedChildren.add(SVNFileUtil.getFileName(child2));
}
}
List<String> sortedChildren = new ArrayList<String>(mergedChildren);
Collections.sort(sortedChildren);
for (String name : sortedChildren) {
if (canceller != null) {
canceller.checkCancelled();
}
if (name.equals(SVNFileUtil.getAdminDirectoryName())) {
continue;
}
File childAbsPath1 = SVNFileUtil.createFilePath(localAbsPath1, name);
File childAbsPath2 = SVNFileUtil.createFilePath(localAbsPath2, name);
SVNNodeKind childKind1 = null;
SVNNodeKind childKind2 = null;
try {
childKind1 = SVNFileType.getNodeKind(SVNFileType.getType(childAbsPath1.getCanonicalFile()));
childKind2 = SVNFileType.getNodeKind(SVNFileType.getType(childAbsPath2.getCanonicalFile()));
} catch (IOException e) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
if (childKind1 == SVNNodeKind.DIR && childKind2 == SVNNodeKind.DIR) {
if (depth == SVNDepth.IMMEDIATES) {
doArbitraryDirsDiff(childAbsPath1, childAbsPath2, diffWalker.root1AbsPath, diffWalker.root2AbsPath, SVNDepth.EMPTY, context, callback, canceller);
} else {
continue;
}
}
if (childKind1 == SVNNodeKind.FILE && (childKind2 == SVNNodeKind.DIR || childKind2 == SVNNodeKind.NONE)) {
doArbitraryFilesDiff(childAbsPath1, null, SVNFileUtil.createFilePath(childRelPath, name), false, true, null, context, callback, canceller);
}
if (childKind2 == SVNNodeKind.FILE && (childKind1 == SVNNodeKind.DIR || childKind1 == SVNNodeKind.NONE)) {
SVNProperties originalProps;
try {
originalProps = context.getActualProps(childAbsPath1);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND && e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_WORKING_COPY) {
throw e;
}
originalProps = new SVNProperties();
}
doArbitraryFilesDiff(null, childAbsPath2, SVNFileUtil.createFilePath(childRelPath, name), true, false, originalProps, context, callback, canceller);
}
if (childKind1 == SVNNodeKind.FILE && childKind2 == SVNNodeKind.FILE) {
doArbitraryFilesDiff(childAbsPath1, childAbsPath2, SVNFileUtil.createFilePath(childRelPath, name), false, false, null, context, callback, canceller);
}
if (depth.compareTo(SVNDepth.FILES) > 0 && childKind2 == SVNNodeKind.DIR && (childKind1 == SVNNodeKind.FILE || childKind1 == SVNNodeKind.NONE)) {
doArbitraryDirsDiff(childAbsPath1, childAbsPath2, diffWalker.root1AbsPath, diffWalker.root2AbsPath, depth.compareTo(SVNDepth.IMMEDIATES) <= 0 ? SVNDepth.EMPTY : SVNDepth.INFINITY, context, callback, canceller);
}
}
}
private static void walkDirectory(File localAbsPath, ArbitraryDiffWalker diffWalker, SVNWCContext context, ISvnDiffCallback callback, ISVNCanceller canceller) throws SVNException {
visit(localAbsPath, SVNFileType.DIRECTORY, diffWalker, context, callback, canceller);
File[] children = SVNFileListUtil.listFiles(localAbsPath);
if (children != null) {
for (File child : children) {
SVNFileType type = SVNFileType.getType(child);
if (type == SVNFileType.DIRECTORY) {
walkDirectory(child, diffWalker, context, callback, canceller);
} else if (type == SVNFileType.FILE || type == SVNFileType.SYMLINK) {
visit(child, type, diffWalker, context, callback, canceller);
}
}
}
}
private static void visit(File localAbsPath, SVNFileType type, ArbitraryDiffWalker diffWalker, SVNWCContext context, ISvnDiffCallback callback, ISVNCanceller canceller) throws SVNException {
if (canceller != null) {
canceller.checkCancelled();
}
if (type != SVNFileType.DIRECTORY) {
return;
}
arbitraryDiffThisDir(diffWalker, localAbsPath, SVNDepth.INFINITY, context, callback, canceller);
}
private static class ArbitraryDiffWalker {
private File root1AbsPath;
private File root2AbsPath;
private boolean recursingWithinAddedSubtree;
private boolean recursingWithinAdmDir;
private File admDirAbsPath;
}
}