package org.tmatesoft.svn.core.internal.wc2.ng;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.db.SVNSqlJetDb;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.ISVNUpdateEditor;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc17.SVNWCContext;
import org.tmatesoft.svn.core.internal.wc17.db.*;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.diff.SVNDeltaProcessor;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import org.tmatesoft.svn.core.wc2.SvnChecksum;
import org.tmatesoft.svn.util.SVNLogType;
public class SvnDiffEditor implements ISVNEditor, ISVNUpdateEditor {
//immutable
private SVNDepth depth;
private SVNWCContext context;
private ISVNWCDb db;
private File anchorAbspath;
private String target;
private boolean showCopiesAsAdds;
private ISvnDiffCallback2 callback;
private Collection<String> changelists;
private boolean ignoreAncestry;
private boolean useGitDiffFormat;
private ISVNCanceller canceller;
private boolean reverseOrder; //actually, has the opposite meaning
private boolean localBeforeRemote;
private boolean diffPristine;
//mutable
private long revision;
private boolean rootOpened;
private Entry rootEntry;
private Entry currentEntry;
private Collection<File> tempFiles;
private SVNWCDbRoot wcRoot;
//once initialized
private final SVNDeltaProcessor deltaProcessor;
private final SvnDiffCallbackResult result;
public SvnDiffEditor(File anchorAbspath, String target, ISvnDiffCallback callback, SVNDepth depth, SVNWCContext context, boolean reverseOrder, boolean useTextBase, boolean showCopiesAsAdds, boolean ignoreAncestry, Collection<String> changelists, boolean useGitDiffFormat, ISVNCanceller canceller) {
if (useGitDiffFormat) {
showCopiesAsAdds = true;
}
this.depth = depth;
this.context = context;
this.db = context.getDb();
this.anchorAbspath = anchorAbspath;
this.target = target;
this.diffPristine = useTextBase;
this.showCopiesAsAdds = showCopiesAsAdds;
this.callback = new SvnDiffCallbackWrapper(callback, true, anchorAbspath);
if (!showCopiesAsAdds) {
this.callback = new SvnCopyAsChangedDiffCallback(this.callback);
}
if (reverseOrder) {
this.callback = new SvnReverseOrderDiffCallback(this.callback, null);
}
this.changelists = changelists;
if (showCopiesAsAdds) {
ignoreAncestry = false;
}
this.ignoreAncestry = ignoreAncestry;
this.useGitDiffFormat = useGitDiffFormat;
this.canceller = canceller;
this.reverseOrder = reverseOrder;
this.deltaProcessor = new SVNDeltaProcessor();
this.tempFiles = new ArrayList<File>();
this.result = new SvnDiffCallbackResult();
}
public SvnDiffEditor() {
this.deltaProcessor = new SVNDeltaProcessor();
this.result = new SvnDiffCallbackResult();
}
public void targetRevision(long revision) throws SVNException {
this.revision = revision;
}
public void openRoot(long revision) throws SVNException {
this.rootOpened = true;
rootEntry = new Entry(false, "", null, false, depth, anchorAbspath);
currentEntry = rootEntry;
if (target.length() == 0) {
currentEntry.leftSource = new SvnDiffSource(this.revision);
currentEntry.rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
callback.dirOpened(result, new File(""), currentEntry.leftSource, currentEntry.rightSource, null, null);
currentEntry.skip = result.skip;
currentEntry.skipChildren = result.skipChildren;
} else {
currentEntry.skip = true;
}
}
public void deleteEntry(String path, long revision) throws SVNException {
File localAbspath = getLocalAbspath(path);
String name = SVNPathUtil.tail(path);
Entry pb = currentEntry;
if (pb.deletes == null) {
pb.deletes = new HashSet<String>();
}
pb.deletes.add(name);
}
public void absentDir(String path) throws SVNException {
}
public void absentFile(String path) throws SVNException {
}
public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException {
SVNDepth subdirDepth = (depth == SVNDepth.IMMEDIATES) ? SVNDepth.EMPTY : depth;
Entry pb = currentEntry;
currentEntry = new Entry(false, path, currentEntry, true, subdirDepth, getLocalAbspath(path));
if (pb.reposOnly || !ignoreAncestry) {
currentEntry.reposOnly = true;
} else {
pb.ensureLocalInfo();
ISVNWCDb.SVNWCDbInfo info = pb.localInfo.get(currentEntry.name);
if (info == null || info.kind != ISVNWCDb.SVNWCDbKind.Dir || info.status.isNotPresent()) {
currentEntry.reposOnly = true;
}
if (!currentEntry.reposOnly && info.status != ISVNWCDb.SVNWCDbStatus.Added) {
currentEntry.reposOnly = true;
}
if (!currentEntry.reposOnly) {
currentEntry.rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
currentEntry.ignoringAncestry = true;
if (pb.compared == null) {
pb.compared = new HashSet<String>();
}
pb.compared.add(currentEntry.name);
}
}
currentEntry.leftSource = new SvnDiffSource(this.revision);
if (localBeforeRemote && !currentEntry.reposOnly && !currentEntry.ignoringAncestry) {
handleLocalOnly(pb, currentEntry.name);
}
result.reset();
callback.dirOpened(result, new File(currentEntry.path), currentEntry.leftSource, currentEntry.rightSource, null, null);
currentEntry.skip = result.skip;
currentEntry.skipChildren = result.skipChildren;
}
public void openDir(String path, long revision) throws SVNException {
SVNDepth subdirDepth = (depth == SVNDepth.IMMEDIATES) ? SVNDepth.EMPTY : depth;
Entry pb = currentEntry;
currentEntry = new Entry(false, path, currentEntry, false, subdirDepth, getLocalAbspath(path));
if (pb.reposOnly) {
currentEntry.reposOnly = true;
} else {
pb.ensureLocalInfo();
ISVNWCDb.SVNWCDbInfo info = pb.localInfo.get(currentEntry.name);
if (info == null || info.kind != ISVNWCDb.SVNWCDbKind.Dir || info.status.isNotPresent()) {
currentEntry.reposOnly = true;
}
if (!currentEntry.reposOnly) {
switch (info.status) {
case Normal:
break;
case Deleted:
currentEntry.reposOnly = true;
if (!info.haveMoreWork) {
if (pb.compared == null) {
pb.compared = new HashSet<String>();
}
pb.compared.add(currentEntry.name);
}
break;
case Added:
if (ignoreAncestry) {
currentEntry.ignoringAncestry = true;
} else {
currentEntry.reposOnly = true;
}
break;
default:
SVNErrorManager.assertionFailure(false, null, SVNLogType.WC);
break;
}
}
if (!currentEntry.reposOnly) {
currentEntry.rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
if (pb.compared == null) {
pb.compared = new HashSet<String>();
}
pb.compared.add(currentEntry.name);
}
}
currentEntry.leftSource = new SvnDiffSource(this.revision);
if (localBeforeRemote && !currentEntry.reposOnly && !currentEntry.ignoringAncestry) {
handleLocalOnly(pb, currentEntry.name);
}
callback.dirOpened(result, new File(currentEntry.path), currentEntry.leftSource, currentEntry.rightSource, null, null);
currentEntry.skip = result.skip;
currentEntry.skipChildren = result.skipChildren;
}
public void closeDir() throws SVNException {
try {
boolean reportedClosed = false;
Entry pb = currentEntry.parent;
if (!currentEntry.skipChildren && currentEntry.deletes != null && currentEntry.deletes.size() > 0) {
List<String> children = new ArrayList<String>(currentEntry.deletes);
Collections.sort(children);
for (String name : children) {
handleLocalOnly(currentEntry, name);
if (currentEntry.compared == null) {
currentEntry.compared = new HashSet<String>();
}
currentEntry.compared.add(name);
}
}
if (!currentEntry.reposOnly && !currentEntry.skipChildren) {
walkLocalNodesDiff(currentEntry.localAbspath, currentEntry.path, currentEntry.depth, currentEntry.compared);
}
if (currentEntry.skip) {
} else if (currentEntry.propChanges.size() > 0 || currentEntry.reposOnly || currentEntry.changePropertyCalled) {
SVNProperties reposProps;
if (currentEntry.added) {
reposProps = new SVNProperties();
} else {
reposProps = db.getBaseProps(currentEntry.localAbspath);
}
if (currentEntry.propChanges.size() > 0 || currentEntry.changePropertyCalled) {
reposProps.putAll(currentEntry.propChanges);
reposProps.removeNullValues();
}
if (currentEntry.reposOnly) {
result.reset();
callback.dirDeleted(result, new File(currentEntry.path), currentEntry.leftSource, reposProps, null);
reportedClosed = true;
} else {
SVNProperties localProps;
if (diffPristine) {
Structure<StructureFields.PristineInfo> pristineInfoStructure = db.readPristineInfo(currentEntry.localAbspath);
localProps = pristineInfoStructure.get(StructureFields.PristineInfo.props);
} else {
localProps = db.readProperties(currentEntry.localAbspath);
}
SVNProperties propChanges = reposProps.compareTo(localProps);
if (propChanges.size() > 0) {
result.reset();
callback.dirChanged(result, new File(currentEntry.path), currentEntry.leftSource, currentEntry.rightSource, reposProps, localProps, propChanges, null);
reportedClosed = true;
}
}
}
if (!reportedClosed && !currentEntry.skip) {
result.reset();
callback.dirClosed(result, new File(currentEntry.path), currentEntry.leftSource, currentEntry.rightSource, null);
}
if (pb != null && !localBeforeRemote && !currentEntry.reposOnly && !currentEntry.ignoringAncestry) {
handleLocalOnly(pb, currentEntry.name);
}
} finally {
currentEntry = currentEntry.parent;
}
}
public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException {
Entry pb = currentEntry;
currentEntry = new Entry(true, path, currentEntry, true, SVNDepth.UNKNOWN, getLocalAbspath(path));
if (pb.skipChildren) {
currentEntry.skip = true;
return;
} else if (pb.reposOnly || !ignoreAncestry) {
currentEntry.reposOnly = true;
} else {
pb.ensureLocalInfo();
ISVNWCDb.SVNWCDbInfo info = pb.localInfo.get(currentEntry.name);
if (info == null || info.kind != ISVNWCDb.SVNWCDbKind.File || info.status.isNotPresent()) {
currentEntry.reposOnly = true;
}
if (!currentEntry.reposOnly && info.status != ISVNWCDb.SVNWCDbStatus.Added) {
currentEntry.reposOnly = true;
}
if (!currentEntry.reposOnly) {
currentEntry.rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
currentEntry.ignoringAncestry = true;
if (pb.compared == null) {
pb.compared = new HashSet<String>();
}
pb.compared.add(currentEntry.name);
}
}
currentEntry.leftSource = new SvnDiffSource(this.revision);
result.reset();
callback.fileOpened(result, new File(currentEntry.path), currentEntry.leftSource, currentEntry.rightSource, null, false, null);
currentEntry.skip = result.skip;
}
public void openFile(String path, long revision) throws SVNException {
Entry pb = currentEntry;
currentEntry = new Entry(true, path, currentEntry, false, SVNDepth.UNKNOWN, getLocalAbspath(path));
if (pb.skipChildren) {
currentEntry.skip = true;
} else if (pb.reposOnly) {
currentEntry.reposOnly = true;
} else {
pb.ensureLocalInfo();
ISVNWCDb.SVNWCDbInfo info = pb.localInfo.get(currentEntry.name);
if (info == null || info.kind != ISVNWCDb.SVNWCDbKind.File || info.status.isNotPresent()) {
currentEntry.reposOnly = true;
}
if (!currentEntry.reposOnly) {
switch (info.status) {
case Normal:
break;
case Deleted:
currentEntry.reposOnly = true;
if (!info.haveMoreWork) {
if (pb.compared == null) {
pb.compared = new HashSet<String>();
}
pb.compared.add(currentEntry.name);
}
break;
case Added:
if (ignoreAncestry) {
currentEntry.ignoringAncestry = true;
} else {
currentEntry.reposOnly = true;
}
break;
default:
SVNErrorManager.assertionFailure(false, null, SVNLogType.WC);
break;
}
}
if (!currentEntry.reposOnly) {
currentEntry.rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
if (pb.compared == null) {
pb.compared = new HashSet<String>();
}
pb.compared.add(currentEntry.name);
}
}
currentEntry.leftSource = new SvnDiffSource(this.revision);
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(currentEntry.localAbspath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.checksum, ISVNWCDb.WCDbBaseInfo.BaseInfoField.props);
currentEntry.baseChecksum = baseInfo.checksum;
currentEntry.baseProps = baseInfo.props;
result.reset();
callback.fileOpened(result, new File(currentEntry.path), currentEntry.leftSource, currentEntry.rightSource, null, false, null);
currentEntry.skip = result.skip;
}
public void applyTextDelta(String path, String baseChecksum) throws SVNException {
if (currentEntry.skip) {
return;
}
InputStream sourceStream;
if (baseChecksum != null && currentEntry.baseChecksum != null) {
SvnChecksum baseMd5 = db.getPristineMD5(anchorAbspath, currentEntry.baseChecksum);
if (baseMd5 != null && !baseMd5.getDigest().equals(baseChecksum)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH, "Checksum mismatch for ''{0}''", currentEntry.localAbspath);
SVNErrorManager.error(err, SVNLogType.WC);
}
sourceStream = db.readPristine(currentEntry.localAbspath, currentEntry.baseChecksum);
} else if (currentEntry.baseChecksum != null) {
sourceStream = db.readPristine(currentEntry.localAbspath, currentEntry.baseChecksum);
} else {
sourceStream = SVNFileUtil.DUMMY_IN;
}
currentEntry.tempFile = createTempFile(db.getWCRootTempDir(currentEntry.localAbspath));
deltaProcessor.applyTextDelta(sourceStream, currentEntry.tempFile, true);
}
public void closeFile(String path, String textChecksum) throws SVNException {
try {
Entry pb = currentEntry.parent;
if (!currentEntry.skip && textChecksum != null) {
SvnChecksum resultChecksum;
if (currentEntry.tempFile != null) {
resultChecksum = currentEntry.resultChecksum;
} else {
resultChecksum = currentEntry.baseChecksum;
}
if (resultChecksum.getKind() != SvnChecksum.Kind.md5) {
resultChecksum = db.getPristineMD5(currentEntry.localAbspath, resultChecksum);
}
if (!textChecksum.equals(resultChecksum.getDigest())) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH, "Checksum mismatch for ''{0}''", currentEntry.localAbspath);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
}
if (localBeforeRemote && !currentEntry.reposOnly && !currentEntry.ignoringAncestry) {
handleLocalOnly(pb, currentEntry.name);
}
SVNProperties propBase;
if (currentEntry.added) {
propBase = new SVNProperties();
} else {
propBase = currentEntry.baseProps;
}
SVNProperties reposProps = new SVNProperties(propBase);
reposProps.putAll(currentEntry.propChanges);
reposProps.removeNullValues();
File reposFile = currentEntry.tempFile;
if (reposFile == null) {
assert currentEntry.baseChecksum != null;
reposFile = SvnWcDbPristines.getPristinePath(getWcRoot(), currentEntry.baseChecksum);
}
if (currentEntry.skip) {
} else if (currentEntry.reposOnly) {
result.reset();
callback.fileDeleted(result, new File(currentEntry.path), currentEntry.leftSource, currentEntry.tempFile, reposProps);
} else {
File localFile;
SVNProperties localProps;
if (diffPristine) {
Structure<StructureFields.PristineInfo> pristineInfoStructure = db.readPristineInfo(currentEntry.localAbspath);
SvnChecksum checksum = pristineInfoStructure.get(StructureFields.PristineInfo.checksum);
localProps = pristineInfoStructure.get(StructureFields.PristineInfo.props);
assert checksum != null;
localFile = SvnWcDbPristines.getPristinePath(getWcRoot(), checksum);
} else {
localProps = db.readProperties(currentEntry.localAbspath);
localFile = context.getTranslatedFile(currentEntry.localAbspath, currentEntry.localAbspath, true, false, false, false, false); //TODO: cancellation?
}
SVNProperties propChanges = reposProps.compareTo(localProps);
result.reset();
callback.fileChanged(result, new File(currentEntry.path), currentEntry.leftSource, currentEntry.rightSource, reposFile, localFile, reposProps, localProps, true, propChanges);
}
if (!localBeforeRemote && !currentEntry.reposOnly && !currentEntry.ignoringAncestry) {
handleLocalOnly(pb, currentEntry.name);
}
} finally {
currentEntry = currentEntry.parent;
}
}
public void changeDirProperty(String name, SVNPropertyValue value) throws SVNException {
if (SVNProperty.isWorkingCopyProperty(name)) {
return;
} else if (SVNProperty.isRegularProperty(name)) {
currentEntry.hasPropChange = true;
if (currentEntry.propChanges == null) {
currentEntry.propChanges = new SVNProperties();
}
currentEntry.propChanges.put(name, value);
}
currentEntry.changePropertyCalled = true;
}
public void changeFileProperty(String path, String propertyName, SVNPropertyValue propertyValue) throws SVNException {
if (SVNProperty.isWorkingCopyProperty(propertyName)) {
return;
} else if (SVNProperty.isRegularProperty(propertyName)) {
currentEntry.hasPropChange = true;
if (currentEntry.propChanges == null) {
currentEntry.propChanges = new SVNProperties();
}
currentEntry.propChanges.put(propertyName, propertyValue);
}
}
public SVNCommitInfo closeEdit() throws SVNException {
if (!rootOpened) {
walkLocalNodesDiff(anchorAbspath, "", depth, null);
}
return null;
}
public void abortEdit() throws SVNException {
}
public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException {
return deltaProcessor.textDeltaChunk(diffWindow);
}
public void textDeltaEnd(String path) throws SVNException {
final String checksum = deltaProcessor.textDeltaEnd();
currentEntry.resultChecksum = new SvnChecksum(SvnChecksum.Kind.md5, checksum);
}
private File getLocalAbspath(String path) {
return new File(anchorAbspath, path);
}
private void checkCancelled() throws SVNCancelException {
canceller.checkCancelled();
}
//
// private SVNProperties applyPropsChanges(SVNProperties props, SVNProperties propChanges) {
// SVNProperties result = new SVNProperties(props);
// if (propChanges != null) {
// for(Iterator names = propChanges.nameSet().iterator(); names.hasNext();) {
// String name = (String) names.next();
// SVNPropertyValue value = propChanges.getSVNPropertyValue(name);
// if (value == null) {
// result.remove(name);
// } else {
// result.put(name, value);
// }
// }
// }
// return result;
// }
//
// private static void reversePropChanges(SVNProperties base, SVNProperties diff) {
// Collection<String> namesList = new ArrayList<String>(diff.nameSet());
// for (Iterator names = namesList.iterator(); names.hasNext();) {
// String name = (String) names.next();
// SVNPropertyValue newValue = diff.getSVNPropertyValue(name);
// SVNPropertyValue oldValue = base.getSVNPropertyValue(name);
// if (oldValue == null && newValue != null) {
// base.put(name, newValue);
// diff.put(name, (SVNPropertyValue) null);
// } else if (oldValue != null && newValue == null) {
// base.put(name, (SVNPropertyValue) null);
// diff.put(name, oldValue);
// } else if (oldValue != null && newValue != null) {
// base.put(name, newValue);
// diff.put(name, oldValue);
// }
// }
// }
private void walkLocalNodesDiff(File localAbspath, String path, SVNDepth depth, Set<String> compared) throws SVNException {
if (diffPristine) {
return;
}
boolean inAnchorNotTarget = path.length() == 0 && target.length() != 0;
ISVNWCDb.WCDbInfo wcDbInfo = db.readInfo(localAbspath, ISVNWCDb.WCDbInfo.InfoField.revision, ISVNWCDb.WCDbInfo.InfoField.propsMod);
long revision = wcDbInfo.revision;
boolean propsMod = wcDbInfo.propsMod;
SvnDiffSource leftSource = new SvnDiffSource(revision);
SvnDiffSource rightSource = new SvnDiffSource(SVNRepository.INVALID_REVISION);
boolean skip = false;
boolean skipChildren = false;
if (compared != null) {
//
skip = true;
} else if (!inAnchorNotTarget) {
result.reset();
callback.dirOpened(result, new File(path), leftSource, rightSource, null, null);
skip = result.skip;
skipChildren = result.skipChildren;
}
if (!skipChildren && depth != SVNDepth.EMPTY) {
SVNDepth depthBelowHere = depth;
if (depthBelowHere == SVNDepth.IMMEDIATES) {
depthBelowHere = SVNDepth.EMPTY;
}
boolean diffFiles = (depth == SVNDepth.UNKNOWN || depth.compareTo(SVNDepth.FILES) >= 0);
boolean diffDirectories = (depth == SVNDepth.UNKNOWN || depth.compareTo(SVNDepth.IMMEDIATES) >= 0);
Map<String, ISVNWCDb.SVNWCDbInfo> nodes = new HashMap<String, ISVNWCDb.SVNWCDbInfo>();
db.readChildren(localAbspath, nodes, new HashSet<String>());
List<String> children = new ArrayList<String>(nodes.keySet());
Collections.sort(children);
for (String name : children) {
ISVNWCDb.SVNWCDbInfo info = nodes.get(name);
if (inAnchorNotTarget && !target.equals(name)) {
continue;
}
if (compared != null && compared.contains(name)) {
continue;
}
if (info.status.isNotPresent()) {
continue;
}
assert info.status == ISVNWCDb.SVNWCDbStatus.Normal ||
info.status == ISVNWCDb.SVNWCDbStatus.Added ||
info.status == ISVNWCDb.SVNWCDbStatus.Deleted;
File childAbsPath = SVNFileUtil.createFilePath(localAbspath, name);
File childRelPath = SVNFileUtil.createFilePath(path, name);
boolean reposOnly = false;
boolean localOnly = false;
ISVNWCDb.SVNWCDbKind baseKind = ISVNWCDb.SVNWCDbKind.Unknown;
if (!info.haveBase) {
localOnly = true;
} else if (info.status == ISVNWCDb.SVNWCDbStatus.Normal) {
baseKind = info.kind;
} else if (info.status == ISVNWCDb.SVNWCDbStatus.Deleted && (!diffPristine || !info.haveMoreWork)) {
reposOnly = true;
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(childAbsPath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.status, ISVNWCDb.WCDbBaseInfo.BaseInfoField.kind);
baseKind = baseInfo.kind;
if (baseInfo.status.isNotPresent()) {
continue;
}
} else {
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(childAbsPath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.status, ISVNWCDb.WCDbBaseInfo.BaseInfoField.kind);
baseKind = baseInfo.kind;
if (baseInfo.status.isNotPresent()) {
localOnly = true;
} else if (baseKind != info.kind || !ignoreAncestry) {
reposOnly = true;
localOnly = true;
}
}
if (localBeforeRemote && localOnly) {
if (info.kind == ISVNWCDb.SVNWCDbKind.File && diffFiles) {
SvnNgDiffUtil.diffLocalOnlyFile(childAbsPath, childRelPath, changelists, this.diffPristine, this.context, this.callback);
} else if (info.kind == ISVNWCDb.SVNWCDbKind.Dir && diffDirectories) {
SvnNgDiffUtil.diffLocalOnlyDirectory(childAbsPath, childRelPath, depthBelowHere, changelists, this.diffPristine, this.context, this.callback);
}
}
if (reposOnly) {
if (baseKind == ISVNWCDb.SVNWCDbKind.File && diffFiles) {
SvnNgDiffUtil.diffBaseOnlyFile(childAbsPath, childRelPath, this.revision, this.db, this.callback);
} else if (baseKind == ISVNWCDb.SVNWCDbKind.Dir && diffDirectories) {
SvnNgDiffUtil.diffBaseOnlyDirectory(childAbsPath, childRelPath, this.revision, depthBelowHere, this.db, this.callback);
}
} else if (!localOnly) {
if (info.kind == ISVNWCDb.SVNWCDbKind.File && diffFiles) {
if (info.status != ISVNWCDb.SVNWCDbStatus.Normal || !diffPristine) {
SvnNgDiffUtil.diffBaseWorkingDiff(childAbsPath, childRelPath, this.revision, changelists, this.diffPristine, this.context, this.callback);
}
} else if (info.kind == ISVNWCDb.SVNWCDbKind.Dir && diffDirectories) {
walkLocalNodesDiff(childAbsPath, SVNFileUtil.getFilePath(childRelPath), depthBelowHere, null);
}
}
if (!localBeforeRemote && localOnly) {
if (info.kind == ISVNWCDb.SVNWCDbKind.File && diffFiles) {
SvnNgDiffUtil.diffLocalOnlyFile(childAbsPath, childRelPath, changelists, this.diffPristine, this.context, this.callback);
} else if (info.kind == ISVNWCDb.SVNWCDbKind.Dir && diffDirectories) {
SvnNgDiffUtil.diffLocalOnlyDirectory(childAbsPath, childRelPath, depthBelowHere, changelists, this.diffPristine, this.context, this.callback);
}
}
}
}
if (compared != null) {
return;
}
if (!skip && changelists == null && !inAnchorNotTarget && propsMod) {
SVNWCContext.PropDiffs propDiffs = context.getPropDiffs(localAbspath);
SVNProperties propChanges = propDiffs.propChanges;
SVNProperties leftProps = propDiffs.originalProps;
SVNProperties rightProps = new SVNProperties(leftProps);
rightProps.putAll(propChanges);
rightProps.removeNullValues();
result.reset();
callback.dirChanged(result, new File(path), leftSource, rightSource, leftProps, rightProps, propChanges, null);
} else if (!skip) {
result.reset();
callback.dirClosed(result, new File(path), leftSource, rightSource, null);
}
}
private File createTempFile(File tempDir) throws SVNException {
final File tempFile = SVNFileUtil.createUniqueFile(tempDir, "diff.", ".tmp", true);
tempFiles.add(tempFile);
return tempFile;
}
private void addToCompared(Entry entry, String path) {
if (entry.compared == null) {
currentEntry.compared = new HashSet<String>();
}
currentEntry.compared.add(path);
}
public void cleanup() {
for (File tempFile : tempFiles) {
try {
SVNFileUtil.deleteFile(tempFile);
} catch (SVNException ignore) {
}
}
}
public long getTargetRevision() {
return revision;
}
public static SVNProperties computePropDiff(SVNProperties props1, SVNProperties props2) {
SVNProperties propsDiff = new SVNProperties();
if (props2 != null) {
for (Iterator names = props2.nameSet().iterator(); names.hasNext();) {
String newPropName = (String) names.next();
if (props1.containsName(newPropName)) {
// changed.
SVNPropertyValue oldValue = props2.getSVNPropertyValue(newPropName);
SVNPropertyValue value = props1.getSVNPropertyValue(newPropName);
if (oldValue != null && !oldValue.equals(value)) {
propsDiff.put(newPropName, oldValue);
} else if (oldValue == null && value != null) {
propsDiff.put(newPropName, oldValue);
}
} else {
// added.
propsDiff.put(newPropName, props2.getSVNPropertyValue(newPropName));
}
}
}
if (props1 != null) {
for (Iterator names = props1.nameSet().iterator(); names.hasNext();) {
String oldPropName = (String) names.next();
if (!props2.containsName(oldPropName)) {
// deleted
propsDiff.put(oldPropName, (String) null);
}
}
}
return propsDiff;
}
private SVNWCDbRoot getWcRoot() throws SVNException {
if (wcRoot == null) {
SVNWCDb.DirParsedInfo parsed = ((SVNWCDb) db).parseDir(anchorAbspath, SVNSqlJetDb.Mode.ReadOnly);
wcRoot = parsed.wcDbDir.getWCRoot();
}
return wcRoot;
}
private void handleLocalOnly(Entry pb, String name) throws SVNException {
boolean reposDelete = (pb.deletes != null && pb.deletes.contains(name));
assert !name.contains("/");
assert !pb.added || ignoreAncestry;
if (pb.skipChildren) {
return;
}
pb.ensureLocalInfo();
ISVNWCDb.SVNWCDbInfo info = pb.localInfo.get(name);
if (info == null || info.status.isNotPresent()) {
return;
}
switch (info.status) {
case Incomplete:
return;
case Normal:
if (!reposDelete) {
return;
}
pb.deletes.remove(name);
break;
case Deleted:
if (!(diffPristine && reposDelete)) {
return;
}
break;
case Added:
default:
break;
}
if (info.kind == ISVNWCDb.SVNWCDbKind.Dir) {
if (pb.depth == SVNDepth.INFINITY || pb.depth == SVNDepth.UNKNOWN) {
depth = pb.depth;
} else {
depth = SVNDepth.EMPTY;
}
SvnNgDiffUtil.diffLocalOnlyDirectory(SVNFileUtil.createFilePath(pb.localAbspath, name), SVNFileUtil.createFilePath(pb.path, name), reposDelete ? SVNDepth.INFINITY : depth, changelists, this.diffPristine, this.context, this.callback);
} else {
SvnNgDiffUtil.diffLocalOnlyFile(SVNFileUtil.createFilePath(pb.localAbspath, name), SVNFileUtil.createFilePath(pb.path, name), changelists, this.diffPristine, this.context, this.callback);
}
}
private class Entry {
private boolean isFile;
private Entry parent;
private String path;
private String name;
private boolean added;
private SVNDepth depth;
private SVNProperties propChanges;
private File localAbspath;
private SvnChecksum baseChecksum;
private SvnChecksum resultChecksum;
// private File file;
private Set<String> compared;
private SvnDiffSource leftSource;
private SvnDiffSource rightSource;
private boolean skip;
private boolean skipChildren;
private Set<String> deletes;
private boolean reposOnly;
private boolean ignoringAncestry;
private File tempFile;
private Map<String, ISVNWCDb.SVNWCDbInfo> localInfo;
private SVNProperties baseProps;
private boolean hasPropChange;
private boolean changePropertyCalled;
public Entry(boolean file, String path, Entry parent, boolean added, SVNDepth depth, File localAbspath) {
this.isFile = file;
this.path = path;
this.name = SVNPathUtil.tail(path);
this.parent = parent;
this.added = added;
this.depth = depth;
this.localAbspath = localAbspath;
this.propChanges = new SVNProperties();
this.compared = new HashSet<String>();
}
public void ensureLocalInfo() throws SVNException {
if (localInfo != null) {
return;
}
localInfo = new HashMap<String, ISVNWCDb.SVNWCDbInfo>();
db.readChildren(localAbspath, localInfo, new HashSet<String>());
}
}
}