package org.tmatesoft.svn.core.internal.wc17.db; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.reset; import java.io.*; import java.util.*; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.db.SVNSqlJetDb; import org.tmatesoft.svn.core.internal.db.SVNSqlJetStatement; import org.tmatesoft.svn.core.internal.util.SVNSkel; import org.tmatesoft.svn.core.internal.wc.*; import org.tmatesoft.svn.core.internal.wc17.SVNWCConflictDescription17; import org.tmatesoft.svn.core.internal.wc17.SVNWCContext; import org.tmatesoft.svn.core.internal.wc17.SVNWCUtils; import org.tmatesoft.svn.core.internal.wc17.db.SVNWCDb.DirParsedInfo; import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema; import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema.ACTUAL_NODE__Fields; import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbStatements; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.util.SVNLogType; public class SvnWcDbConflicts extends SvnWcDbShared { private static final String CONFLICT_OP_UPDATE = "update"; private static final String CONFLICT_OP_SWITCH = "switch"; private static final String CONFLICT_OP_MERGE = "merge"; public static SVNSkel convertToConflictSkel(String conflictOld, String conflictWorking, String conflictNew, String propReject, byte[] treeConflictData) throws SVNException { SVNSkel conflictData = null; if (conflictOld != null || conflictNew != null || conflictWorking != null) { conflictData = createConflictSkel(); addTextConflict(conflictData, conflictWorking, conflictOld, conflictNew); } if (propReject != null) { if (conflictData == null) { conflictData = createConflictSkel(); } addPropConflict(conflictData, propReject); } if (treeConflictData != null) { if (conflictData == null) { conflictData = createConflictSkel(); } final SVNSkel tcSkel = SVNSkel.parse(treeConflictData); final File fakePath = SVNFileUtil.createFilePath(""); final SVNTreeConflictDescription tcDesc = SVNTreeConflictUtil.readSingleTreeConflict(tcSkel, fakePath); addTreeConflict(conflictData, tcDesc.getConflictReason(), tcDesc.getConflictAction()); if (tcDesc.getOperation() != null && tcDesc.getOperation() != SVNOperation.NONE) { setConflictOperation(conflictData, tcDesc.getOperation(), tcDesc.getSourceLeftVersion(), tcDesc.getSourceRightVersion()); } } else if (conflictData != null) { setConflictOperation(conflictData, SVNOperation.UPDATE, null, null); } return conflictData; } public static SVNSkel convertToConflictSkel(File wcRootAbsPath, SVNWCDb db, String localRelpath, String conflictOld, String conflictWorking, String conflictNew, String propReject, byte[] treeConflictData) throws SVNException { SVNSkel conflictData = null; if (conflictOld != null || conflictNew != null || conflictWorking != null) { conflictData = createConflictSkel(); File oldAbsPath = null; File newAbsPath = null; File wrkAbsPath = null; if (conflictOld != null) { oldAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, conflictOld); } if (conflictNew != null) { newAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, conflictNew); } if (conflictWorking != null) { wrkAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, conflictWorking); } addTextConflict(conflictData, db, wcRootAbsPath, wrkAbsPath, oldAbsPath, newAbsPath); } if (propReject != null) { if (conflictData == null) { conflictData = createConflictSkel(); } File prejAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, propReject); addPropConflict(conflictData, db, wcRootAbsPath, prejAbsPath, null, null, null, Collections.<String>emptySet()); } if (treeConflictData != null) { if (conflictData == null) { conflictData = createConflictSkel(); } final SVNSkel tcSkel = SVNSkel.parse(treeConflictData); final File localAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, localRelpath); final SVNTreeConflictDescription tcDesc = SVNTreeConflictUtil.readSingleTreeConflict(tcSkel, localAbsPath); addTreeConflict(conflictData, db, wcRootAbsPath, tcDesc.getConflictReason(), tcDesc.getConflictAction(), null); if (tcDesc.getOperation() != null && tcDesc.getOperation() != SVNOperation.NONE) { setConflictOperation(conflictData, tcDesc.getOperation(), tcDesc.getSourceLeftVersion(), tcDesc.getSourceRightVersion()); } } else if (conflictData != null) { setConflictOperation(conflictData, SVNOperation.UPDATE, null, null); } return conflictData; } public static List<SVNWCConflictDescription17> convertFromSkel(SVNWCDb db, File localAbsPath, boolean createTempFiles, SVNSkel conflictSkel) throws SVNException { final List<SVNWCConflictDescription17> conflicts = new ArrayList<SVNWCConflictDescription17>(); if (conflictSkel == null) { return conflicts; } final Structure<ConflictInfo> conflictInfo = readConflictInfo(conflictSkel); final List<SVNConflictVersion> locations = conflictInfo.get(ConflictInfo.locations); SVNConflictVersion leftVersion = null; SVNConflictVersion rightVersion = null; if (locations != null && locations.size() > 0) { leftVersion = locations.get(0); } if (locations != null && locations.size() > 1) { rightVersion = locations.get(1); } if (conflictInfo.is(ConflictInfo.propConflicted)) { readPropertyConflicts(conflicts, db, localAbsPath, conflictSkel, createTempFiles, (SVNOperation) conflictInfo.get(ConflictInfo.conflictOperation), leftVersion, rightVersion); } if (conflictInfo.is(ConflictInfo.textConflicted)) { final Structure<TextConflictInfo> textConflictInfo = readTextConflict(db, localAbsPath, conflictSkel); final SVNWCConflictDescription17 description = SVNWCConflictDescription17.createText(localAbsPath); description.setOperation(conflictInfo.<SVNOperation>get(ConflictInfo.conflictOperation)); description.setSrcLeftVersion(leftVersion); description.setSrcRightVersion(rightVersion); description.setTheirFile(textConflictInfo.<File>get(TextConflictInfo.theirAbsPath)); description.setBaseFile(textConflictInfo.<File>get(TextConflictInfo.theirOldAbsPath)); description.setMyFile(textConflictInfo.<File>get(TextConflictInfo.mineAbsPath)); description.setMergedFile(localAbsPath); conflicts.add(description); } if (conflictInfo.is(ConflictInfo.treeConflicted)) { final Structure<TreeConflictInfo> treeConflictInfo = readTreeConflict(db, localAbsPath, conflictSkel); final SVNNodeKind tcKind; if (leftVersion != null) { tcKind = leftVersion.getKind(); } else if (rightVersion != null) { tcKind = rightVersion.getKind(); } else { tcKind = SVNNodeKind.FILE; } final SVNWCConflictDescription17 description = SVNWCConflictDescription17.createTree(localAbsPath, tcKind, conflictInfo.<SVNOperation>get(ConflictInfo.conflictOperation), leftVersion, rightVersion); description.setReason(treeConflictInfo.<SVNConflictReason>get(TreeConflictInfo.localChange)); description.setAction(treeConflictInfo.<SVNConflictAction>get(TreeConflictInfo.incomingChange)); conflicts.add(description); } return conflicts; } public enum ConflictInfo { conflictOperation, locations, textConflicted, propConflicted, treeConflicted, } public enum ConflictKind { text, prop, tree, reject, obstructed; } public enum PropertyConflictInfo { markerAbspath, mineProps, theirOldProps, theirProps, conflictedPropNames, } public enum TextConflictInfo { mineAbsPath, theirOldAbsPath, theirAbsPath, } public enum TreeConflictInfo { localChange, incomingChange, moveSrcOpRootAbsPath, } public enum ConflictStatus { conflicted, conflictIgnored, textConflicted, propConflicted, treeConflicted } public static SVNSkel createConflictSkel() throws SVNException { final SVNSkel skel = SVNSkel.createEmptyList(); skel.prepend(SVNSkel.createEmptyList()); skel.prepend(SVNSkel.createEmptyList()); return skel; } public static boolean isConflictSkelComplete(SVNSkel skel) throws SVNException { if (skel == null || skel.getListSize() < 2) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.INCOMPLETE_DATA, "Not a conflict skel"); SVNErrorManager.error(err, SVNLogType.WC); } if (skel.first().getListSize() < 2) { return false; } else if (skel.first().next().getListSize() == 0) { return false; } return true; } public static void prependLocation(SVNSkel skel, SVNConflictVersion location) throws SVNException { SVNSkel loc = SVNSkel.createEmptyList(); if (location == null) { skel.prepend(loc); return; } loc.prepend(SVNSkel.createAtom(location.getKind().toString())); loc.prepend(SVNSkel.createAtom(Long.toString(location.getPegRevision()))); loc.prepend(SVNSkel.createAtom(location.getPath())); // TODO UUID loc.prepend(SVNSkel.createEmptyList()); loc.prepend(SVNSkel.createAtom(location.getRepositoryRoot().toString())); loc.prepend(SVNSkel.createAtom("subversion")); skel.prepend(loc); } public static void setConflictOperation(SVNSkel skel, SVNOperation operation, SVNConflictVersion original, SVNConflictVersion target) throws SVNException { final SVNSkel why = skel.first(); final SVNSkel origins = SVNSkel.createEmptyList(); prependLocation(origins, target); prependLocation(origins, original); why.prepend(origins); why.prepend(SVNSkel.createAtom(operation.getName())); } public static void addTextConflict(SVNSkel skel, ISVNWCDb db, File wriAbsPath, File mineAbsPath, File theirOldAbsPath, File theirAbsPath) throws SVNException { final SVNSkel textConflict = SVNSkel.createEmptyList(); final SVNSkel markers = SVNSkel.createEmptyList(); if (theirAbsPath != null) { final File theirRelPath = db.toRelPath(theirAbsPath); markers.prepend(SVNSkel.createAtom(SVNFileUtil.getFilePath(theirRelPath))); } else { markers.prepend(SVNSkel.createEmptyList()); } if (mineAbsPath != null) { final File mineRelPath = db.toRelPath(mineAbsPath); markers.prepend(SVNSkel.createAtom(SVNFileUtil.getFilePath(mineRelPath))); } else { markers.prepend(SVNSkel.createEmptyList()); } if (theirOldAbsPath != null) { final File theirOldRelPath = db.toRelPath(theirOldAbsPath); markers.prepend(SVNSkel.createAtom(SVNFileUtil.getFilePath(theirOldRelPath))); } else { markers.prepend(SVNSkel.createEmptyList()); } textConflict.prepend(markers); textConflict.prepend(SVNSkel.createAtom(ConflictKind.text.toString())); skel.first().next().prepend(textConflict); } public static void addPropConflict(SVNSkel skel, ISVNWCDb db, File wriAbsPath, File markerAbsPath, SVNProperties mineProps, SVNProperties theirOldProps, SVNProperties theirProps, Collection<String> conflictedPropNames) throws SVNException { final SVNSkel propConflict = SVNSkel.createEmptyList(); if (theirProps != null) { propConflict.prepend(SVNSkel.createPropList(theirProps.asMap())); } else { propConflict.prepend(SVNSkel.createAtom("")); } if (mineProps != null) { propConflict.prepend(SVNSkel.createPropList(mineProps.asMap())); } else { propConflict.prepend(SVNSkel.createAtom("")); } if (theirOldProps != null) { propConflict.prepend(SVNSkel.createPropList(theirOldProps.asMap())); } else { propConflict.prepend(SVNSkel.createAtom("")); } final SVNSkel conflictNames = SVNSkel.createEmptyList(); for (String propertyName : conflictedPropNames) { conflictNames.prepend(SVNSkel.createAtom(propertyName)); } propConflict.prepend(conflictNames); final SVNSkel markers = SVNSkel.createEmptyList(); if (markerAbsPath != null) { final File markerRelPath = db.toRelPath(markerAbsPath); markers.prepend(SVNSkel.createAtom(SVNFileUtil.getFilePath(markerRelPath))); } propConflict.prepend(markers); propConflict.prepend(SVNSkel.createAtom(ConflictKind.prop.toString())); skel.first().next().prepend(propConflict); } public static void addTreeConflict(SVNSkel skel, ISVNWCDb db, File wriAbsPath, SVNConflictReason localChange, SVNConflictAction incomingChange, File moveSrcOpRootAbsPath) throws SVNException { final SVNSkel treeConflict = SVNSkel.createEmptyList(); if (localChange == SVNConflictReason.MOVED_AWAY && moveSrcOpRootAbsPath != null) { final File moveSrcOpRootRelPath = db.toRelPath(moveSrcOpRootAbsPath); treeConflict.prepend(SVNSkel.createAtom(SVNFileUtil.getFilePath(moveSrcOpRootRelPath))); } treeConflict.prepend(SVNSkel.createAtom(incomingChange.getName())); treeConflict.prepend(SVNSkel.createAtom(localChange.getName())); final SVNSkel markers = SVNSkel.createEmptyList(); treeConflict.prepend(markers); treeConflict.prepend(SVNSkel.createAtom(ConflictKind.tree.toString())); skel.first().next().prepend(treeConflict); } public static SVNSkel readConflict(SVNWCDb db, File localAbspath) throws SVNException { final DirParsedInfo dirInfo = db.obtainWcRoot(localAbspath); final SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); final long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); File localRelPath = dirInfo.localRelPath; return readConflictInternal(dirInfo.wcDbDir.getWCRoot(), localRelPath); } public static SVNSkel readConflictInternal(SVNWCDbRoot wcRoot, File localRelPath) throws SVNException { long wcId = wcRoot.getWcId(); SVNSqlJetDb sdb = wcRoot.getSDb(); final String localRelPathStr = localRelPath.getPath().replace(File.separatorChar, '/'); final SVNSqlJetStatement stmt = sdb.getStatement(SVNWCDbStatements.SELECT_ACTUAL_NODE); try { stmt.bindf("is", wcId, localRelPathStr); if (!stmt.next()) { final SVNSqlJetStatement stmtNode = sdb.getStatement(SVNWCDbStatements.SELECT_NODE_INFO); try { stmtNode.bindf("is", wcId, localRelPathStr); if (stmtNode.next()) { return null; } } finally { reset(stmtNode); } File localAbspath = SVNFileUtil.createFilePath(wcRoot.getAbsPath(), localRelPath); final SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_PATH_NOT_FOUND, "The node ''{0}'' was not found.", localAbspath); SVNErrorManager.error(err, SVNLogType.WC); } final byte[] conflictData = stmt.getColumnBlob(ACTUAL_NODE__Fields.conflict_data); if (conflictData != null) { return SVNSkel.parse(conflictData); } } finally { reset(stmt); } return null; } public static Structure<ConflictInfo> readConflictInfo(SVNSkel conflictSkel) throws SVNException { final Structure<ConflictInfo> result = Structure.obtain(ConflictInfo.class); SVNSkel c; final SVNSkel operation = SvnWcDbConflicts.readConflictOperation(conflictSkel); if (operation == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.INCOMPLETE_DATA, "Not a completed conflict skel"); SVNErrorManager.error(err, SVNLogType.WC); } // operation c = operation.first(); result.set(ConflictInfo.conflictOperation, SVNOperation.fromString(c.getValue())); // location c = c.next(); final Collection<SVNConflictVersion> locations = new ArrayList<SVNConflictVersion>(); result.set(ConflictInfo.locations, locations); for(int i = 0; i < c.getListSize(); i++) { final SVNConflictVersion location = SvnWcDbConflicts.readConflictLocation(c.getChild(i)); locations.add(location); } result.set(ConflictInfo.textConflicted, SvnWcDbConflicts.hasConflictKind(conflictSkel, ConflictKind.text)); result.set(ConflictInfo.propConflicted, SvnWcDbConflicts.hasConflictKind(conflictSkel, ConflictKind.prop)); result.set(ConflictInfo.treeConflicted, SvnWcDbConflicts.hasConflictKind(conflictSkel, ConflictKind.tree)); return result; } public static void readPropertyConflicts(List<SVNWCConflictDescription17> target, SVNWCDb db, File localAbsPath, SVNSkel conflictSkel, boolean createTempFiles, SVNOperation operation, SVNConflictVersion leftVersion, SVNConflictVersion rightVersion) throws SVNException { final Structure<PropertyConflictInfo> propertyConflictInfo = readPropertyConflict(db, localAbsPath, conflictSkel); final Set<String> conflictedProps = propertyConflictInfo.get(PropertyConflictInfo.conflictedPropNames); if (!createTempFiles || conflictedProps.isEmpty()) { final SVNWCConflictDescription17 description = SVNWCConflictDescription17.createProp(localAbsPath, SVNNodeKind.UNKNOWN, ""); description.setTheirFile((File) propertyConflictInfo.get(PropertyConflictInfo.markerAbspath)); description.setOperation(operation); description.setSrcLeftVersion(leftVersion); description.setSrcRightVersion(rightVersion); target.add(description); return; } // final File tmpFileRoot = db.getWCRootTempDir(localAbsPath); for(String propertyName : conflictedProps) { final SVNWCConflictDescription17 description = SVNWCConflictDescription17.createProp(localAbsPath, SVNNodeKind.UNKNOWN, propertyName); description.setOperation(operation); description.setSrcLeftVersion(leftVersion); description.setSrcRightVersion(rightVersion); description.setPropertyName(propertyName); final Map<String, byte[]> mineProps = propertyConflictInfo.get(PropertyConflictInfo.mineProps); final Map<String, byte[]> theirProps = propertyConflictInfo.get(PropertyConflictInfo.theirProps); final Map<String, byte[]> oldProps = propertyConflictInfo.get(PropertyConflictInfo.theirOldProps); final byte[] mineValue = mineProps.get(propertyName); final byte[] theirValue = theirProps.get(propertyName); final byte[] oldValue = oldProps.get(propertyName); if (theirValue == null) { description.setAction(SVNConflictAction.DELETE); } else if (mineValue == null) { description.setAction(SVNConflictAction.ADD); } else { description.setAction(SVNConflictAction.EDIT); } if (mineValue == null) { description.setReason(SVNConflictReason.DELETED); } else if (theirValue == null) { description.setReason(SVNConflictReason.ADDED); } else { description.setReason(SVNConflictReason.EDITED); } description.setTheirFile((File) propertyConflictInfo.get(PropertyConflictInfo.markerAbspath)); if (mineValue != null) { final File tempFile = SVNFileUtil.createUniqueFile(tmpFileRoot, "svn.", ".prop.tmp", false); description.setMyFile(tempFile); SVNFileUtil.writeToFile(tempFile, mineValue); } if (theirValue != null) { final File tempFile = SVNFileUtil.createUniqueFile(tmpFileRoot, "svn.", ".prop.tmp", false); description.setMergedFile(tempFile); SVNFileUtil.writeToFile(tempFile, theirValue); } if (oldValue != null) { final File tempFile = SVNFileUtil.createUniqueFile(tmpFileRoot, "svn.", ".prop.tmp", false); description.setBaseFile(tempFile); SVNFileUtil.writeToFile(tempFile, oldValue); } target.add(description); } } public static Structure<PropertyConflictInfo> readPropertyConflict(ISVNWCDb db, File wriAbsPath, SVNSkel conflictSkel) throws SVNException { final SVNSkel propConflict = SvnWcDbConflicts.getConflict(conflictSkel, ConflictKind.prop); if (propConflict == null) { final SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_MISSING, "Conflict not set"); SVNErrorManager.error(err, SVNLogType.WC); } SVNSkel c; final Structure<PropertyConflictInfo> result = Structure.obtain(PropertyConflictInfo.class); c = propConflict.first().next(); if (c.first() != null && c.first().isAtom()) { File markerRelpath = SVNFileUtil.createFilePath(c.first().getValue()); result.set(PropertyConflictInfo.markerAbspath, db.fromRelPath(wriAbsPath, markerRelpath)); } c = c.next(); final Set<String> conflictedPropertyNames = new HashSet<String>(); for(int i = 0; i < c.getListSize(); i++) { conflictedPropertyNames.add(c.getChild(i).getValue()); } result.set(PropertyConflictInfo.conflictedPropNames, conflictedPropertyNames); c = c.next(); if (c.isValidPropList()) { result.set(PropertyConflictInfo.theirOldProps, c.parsePropList()); } else { result.set(PropertyConflictInfo.theirOldProps, new HashMap<String, byte[]>()); } c = c.next(); if (c.isValidPropList()) { result.set(PropertyConflictInfo.mineProps, c.parsePropList()); } else { result.set(PropertyConflictInfo.mineProps, new HashMap<String, byte[]>()); } c = c.next(); if (c.isValidPropList()) { result.set(PropertyConflictInfo.theirProps, c.parsePropList()); } else { result.set(PropertyConflictInfo.theirProps, new HashMap<String, byte[]>()); } return result; } public static Structure<TextConflictInfo> readTextConflict(ISVNWCDb db, File wriAbsPath, SVNSkel conflictSkel) throws SVNException { final SVNSkel textConflict = SvnWcDbConflicts.getConflict(conflictSkel, ConflictKind.text); if (textConflict == null) { final SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_MISSING, "Conflict not set"); SVNErrorManager.error(err, SVNLogType.WC); } final Structure<TextConflictInfo> result = Structure.obtain(TextConflictInfo.class); SVNSkel m = textConflict.first().next().first(); if (m.isAtom()) { final File path = db.fromRelPath(db.getWCRoot(wriAbsPath), new File(m.getValue())); result.set(TextConflictInfo.theirOldAbsPath, path); } m = m.next(); if (m.isAtom()) { final File path = db.fromRelPath(db.getWCRoot(wriAbsPath), new File(m.getValue())); result.set(TextConflictInfo.mineAbsPath, path); } m = m.next(); if (m.isAtom()) { final File path = db.fromRelPath(db.getWCRoot(wriAbsPath), new File(m.getValue())); result.set(TextConflictInfo.theirAbsPath, path); } return result; } public static Structure<TreeConflictInfo> readTreeConflict(ISVNWCDb db, File wriAbsPath, SVNSkel conflictSkel) throws SVNException { final SVNSkel treeConflict = SvnWcDbConflicts.getConflict(conflictSkel, ConflictKind.tree); if (treeConflict == null) { final SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_MISSING, "Conflict not set"); SVNErrorManager.error(err, SVNLogType.WC); } final Structure<TreeConflictInfo> result = Structure.obtain(TreeConflictInfo.class); SVNSkel c = treeConflict.first().next().next(); SVNConflictReason reason = SVNConflictReason.fromString(c.getValue()); if (reason == null) { reason = SVNConflictReason.EDITED; } boolean movedAway = reason == SVNConflictReason.MOVED_AWAY; result.set(TreeConflictInfo.localChange, reason); c = c.next(); SVNConflictAction incoming = SVNConflictAction.fromString(c.getValue()); if (incoming == null) { incoming = SVNConflictAction.EDIT; } result.set(TreeConflictInfo.incomingChange, incoming); c = c.next(); if (c != null && movedAway) { result.set(TreeConflictInfo.moveSrcOpRootAbsPath, db.fromRelPath(db.getWCRoot(wriAbsPath), new File(c.getValue()))); } return result; } public static Structure<ConflictStatus> getConflictStatusForUpdate(SVNWCDb db, File localAbsPath, boolean treeConflictOnly) throws SVNException { final Structure<ConflictStatus> result = getConflictStatus(db, localAbsPath); if (treeConflictOnly) { result.set(ConflictStatus.conflicted, result.is(ConflictStatus.treeConflicted)); } else { result.set(ConflictStatus.conflicted, result.is(ConflictStatus.treeConflicted) || result.is(ConflictStatus.textConflicted) || result.is(ConflictStatus.propConflicted)); } return result; } private static Structure<ConflictStatus> getConflictStatus(SVNWCDb db, File localAbsPath) throws SVNException { final Structure<ConflictStatus> result = Structure.obtain(ConflictStatus.class); final SVNSkel conflicts = readConflict(db, localAbsPath); if (conflicts == null) { return result; } boolean resolvedText = false; boolean resolvedProps = false; final Structure<ConflictInfo> conflictsInfo = readConflictInfo(conflicts); if (conflictsInfo.is(ConflictInfo.textConflicted)) { final Structure<TextConflictInfo> tc = readTextConflict(db, localAbsPath, conflicts); final File mineAbsPath = tc.get(TextConflictInfo.mineAbsPath); final File theirAbsPath = tc.get(TextConflictInfo.theirAbsPath); final File theirOldAbsPath = tc.get(TextConflictInfo.theirOldAbsPath); boolean done = false; if (mineAbsPath != null) { result.set(ConflictStatus.textConflicted, SVNFileType.getType(mineAbsPath) == SVNFileType.FILE); done = result.is(ConflictStatus.textConflicted); } if (!done && theirAbsPath != null) { result.set(ConflictStatus.textConflicted, SVNFileType.getType(theirAbsPath) == SVNFileType.FILE); done = result.is(ConflictStatus.textConflicted); } if (!done && theirOldAbsPath != null) { result.set(ConflictStatus.textConflicted, SVNFileType.getType(theirOldAbsPath) == SVNFileType.FILE); done = result.is(ConflictStatus.textConflicted); } if (!done && (mineAbsPath != null || theirAbsPath != null || theirOldAbsPath != null)) { resolvedText = false; } } if (conflictsInfo.is(ConflictInfo.propConflicted)) { final Structure<PropertyConflictInfo> pc = readPropertyConflict(db, localAbsPath, conflicts); final File propRejectPath = pc.get(PropertyConflictInfo.markerAbspath); if (propRejectPath != null) { result.set(ConflictStatus.propConflicted, SVNFileType.getType(propRejectPath) == SVNFileType.FILE); } if (!result.is(ConflictStatus.propConflicted)) { resolvedProps = true; } } if (conflictsInfo.is(ConflictInfo.treeConflicted)) { final Structure<TreeConflictInfo> pc = readTreeConflict(db, localAbsPath, conflicts); final SVNConflictReason reason = pc.get(TreeConflictInfo.incomingChange); final SVNConflictAction action = pc.get(TreeConflictInfo.localChange); if (reason == SVNConflictReason.MOVED_AWAY && action == SVNConflictAction.EDIT) { result.set(ConflictStatus.treeConflicted, false); result.set(ConflictStatus.conflictIgnored, true); } } if (resolvedProps || resolvedText) { if (db.isWCLockOwns(localAbsPath, false)) { db.opMarkResolved(localAbsPath, resolvedText, resolvedProps, false, null); } } return result; } private static SVNSkel readConflictOperation(SVNSkel conflictSkel) { return conflictSkel.first(); } private static boolean hasConflictKind(SVNSkel conflictSkel, ConflictKind kind) { return SvnWcDbConflicts.getConflict(conflictSkel, kind) != null; } public static SVNSkel getConflict(SVNSkel conflictSkel, ConflictKind kind) { for (SVNSkel c = conflictSkel.first().next().first(); c != null; c = c.next()) { if (kind.name().equalsIgnoreCase(c.first().getValue())) { return c; } } return null; } private static SVNConflictVersion readConflictLocation(SVNSkel locationSkel) throws SVNException { SVNSkel c = locationSkel.first(); if (c == null || !c.contentEquals("subversion")) { return null; } c = c.next(); final SVNURL repositoryRootURL = SVNURL.parseURIEncoded(c.getValue()); c = c.next(); // TODO UUID c = c.next(); final String reposRelPath = c.getValue(); c = c.next(); final long revision = Long.parseLong(c.getValue()); c = c.next(); final SVNNodeKind nodeKind = SVNNodeKind.parseKind(c.getValue()); return new SVNConflictVersion(repositoryRootURL, reposRelPath, revision, nodeKind); } public static void conflictSkelOpUpdate(SVNSkel conflictSkel, SVNConflictVersion original, SVNConflictVersion target) throws SVNException { assert conflictSkel != null && conflictSkel.first() != null && conflictSkel.first().next() != null && !conflictSkel.first().next().isAtom(); SVNSkel why = getOperation(conflictSkel); assert why == null; why = conflictSkel.getChild(0); SVNSkel origins = SVNSkel.createEmptyList(); prependLocation(origins, target); prependLocation(origins, original); why.prepend(origins); why.prependString(CONFLICT_OP_UPDATE); } public static void conflictSkelOpSwitch(SVNSkel conflictSkel, SVNConflictVersion original, SVNConflictVersion target) throws SVNException { assert conflictSkel != null && conflictSkel.first() != null && conflictSkel.first().next() != null && !conflictSkel.first().next().isAtom(); SVNSkel why = getOperation(conflictSkel); assert why == null; why = conflictSkel.getChild(0); SVNSkel origins = SVNSkel.createEmptyList(); prependLocation(origins, target); prependLocation(origins, original); why.prepend(origins); why.prependString(CONFLICT_OP_SWITCH); } public static void conflictSkelOpMerge(SVNSkel conflictSkel, SVNConflictVersion original, SVNConflictVersion target) throws SVNException { assert conflictSkel != null && conflictSkel.first() != null && conflictSkel.first().next() != null && !conflictSkel.first().next().isAtom(); SVNSkel why = getOperation(conflictSkel); assert why == null; why = conflictSkel.getChild(0); SVNSkel origins = SVNSkel.createEmptyList(); prependLocation(origins, target); prependLocation(origins, original); why.prepend(origins); why.prependString(CONFLICT_OP_MERGE); } public static SVNSkel createConflictMarkers(ISVNWCDb db, File localAbsPath, SVNSkel conflictSkel) throws SVNException { Structure<ConflictInfo> conflictInfoStructure = readConflictInfo(conflictSkel); SVNOperation conflictOperation = conflictInfoStructure.get(ConflictInfo.conflictOperation); boolean propConflicted = conflictInfoStructure.is(ConflictInfo.propConflicted); if (propConflicted) { File markerDir; String markerName; SVNFileType type = SVNFileType.getType(localAbsPath); if (type == SVNFileType.DIRECTORY) { markerDir = localAbsPath; markerName = "dir_conflicts"; } else { markerDir = SVNFileUtil.getParentFile(localAbsPath); markerName = SVNFileUtil.getFileName(localAbsPath); } File markerAbsPath = SVNFileUtil.createUniqueFile(markerDir, markerName, ".prej", false); File markerRelPath = db.toRelPath(markerAbsPath); SVNSkel propConflict = getConflict(conflictSkel, ConflictKind.prop); propConflict.first().next().prependPath(markerRelPath); Structure<PropertyConflictInfo> propertyConflictInfoStructure = readPropertyConflict(db, localAbsPath, conflictSkel); SVNProperties mineProps = SVNProperties.wrap((Map)propertyConflictInfoStructure.get(PropertyConflictInfo.mineProps)); SVNProperties theirOriginalProps = SVNProperties.wrap((Map)propertyConflictInfoStructure.get(PropertyConflictInfo.theirOldProps)); SVNProperties theirProps = SVNProperties.wrap((Map)propertyConflictInfoStructure.get(PropertyConflictInfo.theirProps)); Collection<String> conflictedPropNames = propertyConflictInfoStructure.get(PropertyConflictInfo.conflictedPropNames); SVNProperties oldProps; if (conflictOperation == SVNOperation.MERGE) { oldProps = db.readPristineProperties(localAbsPath); } else { oldProps = theirOriginalProps; } SVNSkel propData = SVNSkel.createEmptyList(); propData.prepend(SVNSkel.createEmptyList()); for (String propName : conflictedPropNames) { addPropConflict(propData, propName, oldProps != null ? oldProps.getSVNPropertyValue(propName) : null, mineProps != null ? mineProps.getSVNPropertyValue(propName) : null, theirProps != null ? theirProps.getSVNPropertyValue(propName) : null, theirOriginalProps != null ? theirOriginalProps.getSVNPropertyValue(propName) : null); } return SVNWCContext.wqBuildPrejInstall(db, localAbsPath, propData); } return null; } private static SVNSkel getOperation(SVNSkel conflictSkel) throws SVNException { assert conflictSkel != null && conflictSkel.first() != null && conflictSkel.first().next() != null && !conflictSkel.first().next().isAtom(); SVNSkel why = conflictSkel.first(); if (why.first() == null) { why = null; } return why; } public static void addPropConflict(SVNSkel skel, String propName, SVNPropertyValue originalVal, SVNPropertyValue mineVal, SVNPropertyValue incomingVal, SVNPropertyValue incomingBaseVal) throws SVNException { SVNSkel propSkel = SVNSkel.createEmptyList(); prependPropValue(incomingBaseVal, propSkel); prependPropValue(incomingVal, propSkel); prependPropValue(mineVal, propSkel); prependPropValue(originalVal, propSkel); propSkel.prependString(propName); propSkel.prependString(ConflictKind.prop.toString()); skel.appendChild(propSkel); } public static void prependPropValue(SVNPropertyValue fromVal, SVNSkel skel) throws SVNException { SVNSkel valueSkel = SVNSkel.createEmptyList(); if (fromVal != null && (fromVal.getBytes() != null || fromVal.getString() != null)) { valueSkel.prependPropertyValue(fromVal); } skel.prepend(valueSkel); } public static SVNSkel treeConflictDescriptionToSkel(ISVNWCDb db, File wriAbsPath, SVNTreeConflictDescription conflictDescription) throws SVNException { SVNSkel skel = createConflictSkel(); addTreeConflict(skel, db, wriAbsPath, conflictDescription.getConflictReason(), conflictDescription.getConflictAction(), null); if (conflictDescription.getOperation() != null) { if (conflictDescription.getOperation() == SVNOperation.UPDATE) { SvnWcDbConflicts.conflictSkelOpUpdate(skel, conflictDescription.getSourceLeftVersion(), conflictDescription.getSourceRightVersion()); } else if (conflictDescription.getOperation() == SVNOperation.SWITCH) { SvnWcDbConflicts.conflictSkelOpSwitch(skel, conflictDescription.getSourceLeftVersion(), conflictDescription.getSourceRightVersion()); } else if (conflictDescription.getOperation() == SVNOperation.MERGE) { SvnWcDbConflicts.conflictSkelOpMerge(skel, conflictDescription.getSourceLeftVersion(), conflictDescription.getSourceRightVersion()); } } return skel; } public static boolean conflictSkelResolve(SVNSkel conflictSkel, ISVNWCDb db, File wriAbsPAth, boolean resolveText, String resolveProp, boolean resolveTree) throws SVNException { SVNSkel operation = getOperation(conflictSkel); if (operation == null) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.INCOMPLETE_DATA, "Not a completed conflict skel"); SVNErrorManager.error(errorMessage, SVNLogType.WC); } SVNSkel conflicts = conflictSkel.first().next(); Set<SVNSkel> conflictsToRemove = new HashSet<SVNSkel>(); SVNSkel pconflict = conflicts.first(); while (pconflict != null) { SVNSkel c = pconflict.first(); if (resolveText && c.contentEquals(ConflictKind.text.name())) { conflictsToRemove.add(pconflict); pconflict = pconflict.next(); continue; } else if (resolveProp != null && c.contentEquals(ConflictKind.prop.name())) { SVNSkel props = c.next().next(); SVNSkel propnames = props.first(); if (resolveProp.length() == 0) { props.removeAllChildren(); propnames = null; } else { Set<SVNSkel> propsToRemove = new HashSet<SVNSkel>(); while (propnames != null) { if (propnames.contentEquals(resolveProp)) { propsToRemove.add(propnames); propnames = propnames.next(); break; } propnames = propnames.next(); } props.removeChildren(propsToRemove); } if (c.next().next().first() == null) { conflictsToRemove.add(pconflict); pconflict = pconflict.next(); continue; } } else if (resolveTree && c.contentEquals(ConflictKind.tree.name())) { conflictsToRemove.add(pconflict); pconflict = pconflict.next(); continue; } pconflict = pconflict.next(); } conflicts.removeChildren(conflictsToRemove); return !isConflictSkelComplete(conflictSkel); } public static List<File> readConflictMarkers(SVNWCDb db, File wriAbsPath, SVNSkel conflictSkel) throws SVNException { assert conflictSkel != null; List<File> list = null; for (SVNSkel conflict = conflictSkel.first().next().first(); conflict != null; conflict = conflict.next()) { for (SVNSkel marker = conflict.first().next().first(); marker != null; marker = marker.next()) { if (!marker.isAtom()) { continue; } if (list == null) { list = new ArrayList(4); } File path = db.fromRelPath(wriAbsPath, SVNFileUtil.createFilePath(marker.getValue())); list.add(path); } } return list; } public static SVNSkel upgradeConflictSkelFromRaw(ISVNWCDb db, File wriAbsPath, File localRelPath, String conflictOld, String conflictWork, String conflictNew, File prejFile, SVNSkel oldTreeConflictData) throws SVNException { File wcRootAbsPath = db.getWCRoot(wriAbsPath); SVNSkel conflictData = null; if (conflictOld != null || conflictNew != null || conflictWork != null) { conflictData = SvnWcDbConflicts.createConflictSkel(); File oldAbsPath = null; if (conflictOld != null) { oldAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, conflictOld); } File newAbsPath = null; if (conflictNew != null) { newAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, conflictNew); } File workAbsPath = null; if (conflictWork != null) { workAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, conflictWork); } SvnWcDbConflicts.addTextConflict(conflictData, db, wriAbsPath, workAbsPath, oldAbsPath, newAbsPath); } if (prejFile != null) { if (conflictData == null) { conflictData = SvnWcDbConflicts.createConflictSkel(); } addPropConflict(conflictData, db, wriAbsPath, prejFile, null, null, null, new HashSet<String>()); } if (oldTreeConflictData != null) { if (conflictData == null) { conflictData = SvnWcDbConflicts.createConflictSkel(); } File localAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, localRelPath); SVNTreeConflictDescription treeConflictDescription = SVNTreeConflictUtil.readSingleTreeConflict(oldTreeConflictData, SVNFileUtil.getFileDir(localAbsPath)); addTreeConflict(conflictData, db, wriAbsPath, treeConflictDescription.getConflictReason(), treeConflictDescription.getConflictAction(), null); final SVNOperation operation = treeConflictDescription.getOperation(); if (operation == SVNOperation.UPDATE) { conflictSkelOpUpdate(conflictData, treeConflictDescription.getSourceLeftVersion(), treeConflictDescription.getSourceRightVersion()); } else if (operation == SVNOperation.SWITCH) { conflictSkelOpSwitch(conflictData, treeConflictDescription.getSourceLeftVersion(), treeConflictDescription.getSourceRightVersion()); } else if (operation == SVNOperation.MERGE) { conflictSkelOpMerge(conflictData, treeConflictDescription.getSourceLeftVersion(), treeConflictDescription.getSourceRightVersion()); } else { conflictSkelOpUpdate(conflictData, treeConflictDescription.getSourceLeftVersion(), treeConflictDescription.getSourceRightVersion()); } } else if (conflictData != null) { conflictSkelOpUpdate(conflictData, null, null); } return conflictData; } private static void addTextConflict(SVNSkel skel, String mineRelPath, String theirOldRelPath, String theirRelPath) throws SVNException { final SVNSkel textConflict = SVNSkel.createEmptyList(); final SVNSkel markers = SVNSkel.createEmptyList(); if (theirRelPath != null) { markers.prepend(SVNSkel.createAtom(theirRelPath)); } else { markers.prepend(SVNSkel.createEmptyList()); } if (mineRelPath != null) { markers.prepend(SVNSkel.createAtom(mineRelPath)); } else { markers.prepend(SVNSkel.createEmptyList()); } if (theirOldRelPath != null) { markers.prepend(SVNSkel.createAtom(theirOldRelPath)); } else { markers.prepend(SVNSkel.createEmptyList()); } textConflict.prepend(markers); textConflict.prepend(SVNSkel.createAtom(ConflictKind.text.toString())); skel.first().next().prepend(textConflict); } private static void addPropConflict(SVNSkel skel, String propReject) throws SVNException { final SVNSkel propConflict = SVNSkel.createEmptyList(); final SVNSkel conflictNames = SVNSkel.createEmptyList(); propConflict.prepend(conflictNames); final SVNSkel markers = SVNSkel.createEmptyList(); if (propReject != null) { markers.prepend(SVNSkel.createAtom(propReject)); } propConflict.prepend(markers); propConflict.prepend(SVNSkel.createAtom(ConflictKind.prop.toString())); skel.first().next().prepend(propConflict); } private static void addTreeConflict(SVNSkel skel, SVNConflictReason localChange, SVNConflictAction incomingChange) throws SVNException { final SVNSkel treeConflict = SVNSkel.createEmptyList(); treeConflict.prepend(SVNSkel.createAtom(incomingChange.getName())); treeConflict.prepend(SVNSkel.createAtom(localChange.getName())); final SVNSkel markers = SVNSkel.createEmptyList(); treeConflict.prepend(markers); treeConflict.prepend(SVNSkel.createAtom(ConflictKind.tree.toString())); skel.first().next().prepend(treeConflict); } }