/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.internal.wc; import java.io.File; import java.util.Iterator; import java.util.Map; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNProperties; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.internal.util.SVNHashMap; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.SVNMergeDriver.MergeSource; import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea; import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry; import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import org.tmatesoft.svn.core.wc.SVNDiffOptions; import org.tmatesoft.svn.core.wc.SVNEvent; import org.tmatesoft.svn.core.wc.SVNStatusType; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNMergeCallback15 extends SVNMergeCallback { public SVNMergeCallback15(SVNAdminArea adminArea, SVNURL url, boolean force, boolean dryRun, SVNDiffOptions options, Map conflictedPathsGetter, SVNMergeDriver mergeDriver) { super(adminArea, url, force, dryRun, options, conflictedPathsGetter, mergeDriver); } public SVNStatusType propertiesChanged(String path, SVNProperties originalProperties, SVNProperties diff, boolean[] isTreeConflicted) throws SVNException { setIsConflicted(isTreeConflicted, false); SVNProperties regularProps = new SVNProperties(); categorizeProperties(diff, regularProps, null, null); if (regularProps.isEmpty()) { return SVNStatusType.UNKNOWN; } try { File file = getFile(path); SVNWCAccess wcAccess = getWCAccess(); if (wcAccess.getAdminArea(file) == null) { wcAccess.probeTry(file, true, SVNWCAccess.INFINITE_DEPTH); } MergeSource mergeSource = myMergeDriver.getCurrentMergeSource(); if (mergeSource.getRevision1() < mergeSource.getRevision2() || !myMergeDriver.myAreSourcesAncestral) { SVNProperties filteredProps = myMergeDriver.filterSelfReferentialMergeInfo(regularProps, file); if (filteredProps != null) { regularProps = filteredProps; } } SVNStatusType status = SVNPropertiesManager.mergeProperties(getWCAccess(), file, originalProperties, regularProps, false, myIsDryRun); if (!myIsDryRun) { for (Iterator propsIter = regularProps.nameSet().iterator(); propsIter.hasNext();) { String propName = (String) propsIter.next(); SVNPropertyValue propValue = regularProps.getSVNPropertyValue(propName); if (SVNProperty.MERGE_INFO.equals(propName)) { SVNPropertyValue mergeInfoProp = originalProperties.getSVNPropertyValue(SVNProperty.MERGE_INFO); if (mergeInfoProp == null && propValue != null) { myMergeDriver.addPathWithNewMergeInfo(file); } else if (mergeInfoProp != null && propValue == null) { myMergeDriver.addPathWithDeletedMergeInfo(file); } } } } return status; } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() == SVNErrorCode.UNVERSIONED_RESOURCE || e.getErrorMessage().getErrorCode() == SVNErrorCode.ENTRY_NOT_FOUND) { return SVNStatusType.MISSING; } throw e; } } public SVNStatusType directoryAdded(String path, long revision, boolean[] isTreeConflicted) throws SVNException { setIsConflicted(isTreeConflicted, false); File mergedFile = getFile(path); SVNAdminArea dir = retrieve(mergedFile.getParentFile(), true); if (dir == null) { if (myIsDryRun && myAddedPath != null && SVNPathUtil.isAncestor(myAddedPath, path)) { return SVNStatusType.CHANGED; } return SVNStatusType.MISSING; } SVNURL copyFromURL = null; long copyFromRevision = SVNRepository.INVALID_REVISION; if (myMergeDriver.myIsSameRepository) { copyFromURL = myURL.appendPath(path, false); copyFromRevision = revision; } // TODO protocol SVNFileType fileType = SVNFileType.getType(mergedFile); if (fileType == SVNFileType.NONE) { SVNEntry entry = getWCAccess().getEntry(mergedFile, false); if (entry != null && !entry.isScheduledForDeletion()) { return SVNStatusType.OBSTRUCTED; } if (myIsDryRun) { myAddedPath = path; } else { if (!mergedFile.mkdirs()) { if (SVNFileType.getType(mergedFile) != SVNFileType.DIRECTORY) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot create directory ''{0}''", mergedFile); SVNErrorManager.error(err, SVNLogType.DEFAULT); } } ISVNEventHandler oldEventHandler = dir.getWCAccess().getEventHandler(); dir.getWCAccess().setEventHandler(null); SVNWCManager.add(mergedFile, dir, copyFromURL, copyFromRevision, null); dir.getWCAccess().setEventHandler(oldEventHandler); } return SVNStatusType.CHANGED; } else if (fileType == SVNFileType.DIRECTORY) { SVNEntry entry = getWCAccess().getEntry(mergedFile, false); if (entry == null || entry.isScheduledForDeletion()) { if (!myIsDryRun) { ISVNEventHandler oldEventHandler = dir.getWCAccess().getEventHandler(); dir.getWCAccess().setEventHandler(null); SVNWCManager.add(mergedFile, dir, copyFromURL, copyFromRevision, null); dir.getWCAccess().setEventHandler(oldEventHandler); } if (myIsDryRun) { myAddedPath = path; } return SVNStatusType.CHANGED; } else if (myIsDryRun && isPathDeleted(path)) { return SVNStatusType.CHANGED; } return SVNStatusType.OBSTRUCTED; } else if (fileType == SVNFileType.FILE || fileType == SVNFileType.SYMLINK) { if (myIsDryRun) { myAddedPath = null; } SVNEntry entry = getWCAccess().getEntry(mergedFile, false); if (entry != null && myIsDryRun && isPathDeleted(path)) { return SVNStatusType.CHANGED; } return SVNStatusType.OBSTRUCTED; } if (myIsDryRun) { myAddedPath = null; } return SVNStatusType.UNKNOWN; } public SVNStatusType directoryDeleted(final String path, boolean[] isTreeConflicted) throws SVNException { setIsConflicted(isTreeConflicted, false); final File mergedFile = getFile(path); final SVNAdminArea dir = retrieve(mergedFile.getParentFile(), true); if (dir == null) { return SVNStatusType.MISSING; } SVNEntry entry = getWCAccess().getEntry(mergedFile, true); SVNFileType fileType = SVNFileType.getType(mergedFile); if (fileType == SVNFileType.DIRECTORY) { if (entry != null && !entry.isScheduledForDeletion()) { final ISVNEventHandler oldEventHandler = getWCAccess().getEventHandler(); ISVNEventHandler handler = new ISVNEventHandler() { public void checkCancelled() throws SVNCancelException { oldEventHandler.checkCancelled(); } public void handleEvent(SVNEvent event, double progress) throws SVNException { } }; getWCAccess().setEventHandler(handler); try { delete(mergedFile, myIsForce, myIsDryRun); } catch (SVNException e) { return SVNStatusType.OBSTRUCTED; } finally { getWCAccess().setEventHandler(oldEventHandler); } return SVNStatusType.CHANGED; } } else if (fileType == SVNFileType.FILE || fileType == SVNFileType.SYMLINK) { return SVNStatusType.OBSTRUCTED; } else if (fileType == SVNFileType.NONE) { return SVNStatusType.MISSING; } return SVNStatusType.UNKNOWN; } public void directoryOpened(String path, long revision, boolean[] isTreeConflicted) throws SVNException { setIsConflicted(isTreeConflicted, false); } public SVNStatusType[] fileChanged(String path, File file1, File file2, long revision1, long revision2, String mimeType1, String mimeType2, SVNProperties originalProperties, SVNProperties diff, boolean[] isTreeConflicted) throws SVNException { setIsConflicted(isTreeConflicted, false); boolean needsMerge = true; File mergedFile = getFile(path); SVNAdminArea dir = retrieve(mergedFile.getParentFile(), myIsDryRun); if (dir == null) { return new SVNStatusType[] {SVNStatusType.MISSING, SVNStatusType.MISSING}; } SVNStatusType[] result = new SVNStatusType[] {SVNStatusType.UNCHANGED, SVNStatusType.UNCHANGED}; SVNEntry entry = getWCAccess().getEntry(mergedFile, false); SVNFileType fileType = null; if (entry != null) { fileType = SVNFileType.getType(mergedFile); } if (entry == null || (fileType != SVNFileType.FILE && fileType != SVNFileType.SYMLINK)) { return new SVNStatusType[] {SVNStatusType.MISSING, SVNStatusType.MISSING}; } if (diff != null && !diff.isEmpty()) { result[1] = propertiesChanged(path, originalProperties, diff, null); } String name = mergedFile.getName(); if (file1 != null) { boolean textModified = dir.hasTextModifications(name, false); if (!textModified && (SVNProperty.isBinaryMimeType(mimeType1) || SVNProperty.isBinaryMimeType(mimeType2))) { boolean same = SVNFileUtil.compareFiles(!myIsAddNecessitatedMerge ? file1 : file2, mergedFile, null); if (same) { if (!myIsDryRun && !myIsAddNecessitatedMerge) { SVNFileUtil.rename(file2, mergedFile); boolean executable = false; if (diff != null && diff.containsName(SVNProperty.EXECUTABLE)) { executable = diff.getStringValue(SVNProperty.EXECUTABLE) != null; } else { executable = originalProperties != null && originalProperties.getStringValue(SVNProperty.EXECUTABLE) != null; } SVNFileUtil.setExecutable(mergedFile, executable); } result[0] = SVNStatusType.CHANGED; needsMerge = false; } } if (needsMerge) { String localLabel = ".working"; String baseLabel = ".merge-left.r" + revision1; String latestLabel = ".merge-right.r" + revision2; SVNStatusType mergeResult = dir.mergeText(name, file1, file2, null, localLabel, baseLabel, latestLabel, diff, myIsDryRun, myDiffOptions, null); dir.runLogs(); if (mergeResult == SVNStatusType.CONFLICTED || mergeResult == SVNStatusType.CONFLICTED_UNRESOLVED) { result[0] = mergeResult; } else if (textModified && mergeResult != SVNStatusType.UNCHANGED) { result[0] = SVNStatusType.MERGED; } else if (mergeResult == SVNStatusType.MERGED) { result[0] = SVNStatusType.CHANGED; } else if (mergeResult != SVNStatusType.MISSING) { result[0] = SVNStatusType.UNCHANGED; } if (mergeResult == SVNStatusType.CONFLICTED) { if (myConflictedPaths == null) { myConflictedPaths = new SVNHashMap(); } myConflictedPaths.put(path, path); } } } return result; } public SVNStatusType[] fileAdded(String path, File file1, File file2, long revision1, long revision2, String mimeType1, String mimeType2, SVNProperties originalProperties, SVNProperties diff, boolean[] isTreeConflicted) throws SVNException { setIsConflicted(isTreeConflicted, false); SVNStatusType[] result = new SVNStatusType[] {null, SVNStatusType.UNKNOWN}; SVNProperties newProps = new SVNProperties(originalProperties); for (Iterator propChangesIter = diff.nameSet().iterator(); propChangesIter.hasNext();) { String propName = (String) propChangesIter.next(); if (SVNProperty.isWorkingCopyProperty(propName)) { continue; } if (!myMergeDriver.isSameRepository() && !SVNProperty.isRegularProperty(propName)) { continue; } if (!myMergeDriver.isSameRepository() && SVNProperty.MERGE_INFO.equals(propName)) { continue; } SVNPropertyValue propValue = diff.getSVNPropertyValue(propName); newProps.put(propName, propValue); } File mergedFile = getFile(path); SVNAdminArea dir = retrieve(mergedFile.getParentFile(), true); if (dir == null) { if (myIsDryRun && myAddedPath != null && SVNPathUtil.isAncestor(myAddedPath, path)) { result[0] = SVNStatusType.CHANGED; if (!newProps.isEmpty()) { result[1] = SVNStatusType.CHANGED; } } else { result[0] = SVNStatusType.MISSING; } return result; } SVNFileType fileType = SVNFileType.getType(mergedFile); if (fileType == SVNFileType.NONE) { SVNEntry entry = getWCAccess().getEntry(mergedFile, false); if (entry != null && !entry.isScheduledForDeletion()) { result[0] = SVNStatusType.OBSTRUCTED; return result; } if (!myIsDryRun) { String copyFromURL = null; long copyFromRevision = SVNRepository.INVALID_REVISION; if (myMergeDriver.myIsSameRepository) { String targePath = myMergeDriver.myTarget.getAbsolutePath(); String minePath = mergedFile.getAbsolutePath(); String relativePath = SVNPathUtil.getRelativePath(targePath, minePath); copyFromURL = myURL.appendPath(relativePath, false).toString(); copyFromRevision = revision2; } // TODO compare protocols with dir one. SVNWCManager.addRepositoryFile(dir, mergedFile.getName(), null, file2, newProps, null, copyFromURL, copyFromRevision); } result[0] = SVNStatusType.CHANGED; if (!newProps.isEmpty()) { result[1] = SVNStatusType.CHANGED; } } else if (fileType == SVNFileType.DIRECTORY) { if (myIsDryRun && isPathDeleted(path)){ result[0] = SVNStatusType.CHANGED; } else { result[0] = SVNStatusType.OBSTRUCTED; } } else if (fileType == SVNFileType.FILE || fileType == SVNFileType.SYMLINK) { SVNEntry entry = getWCAccess().getEntry(mergedFile, false); if (entry == null || entry.isScheduledForDeletion()) { result[0] = SVNStatusType.OBSTRUCTED; } else if (myIsDryRun && isPathDeleted(path)){ result[0] = SVNStatusType.CHANGED; } else { myIsAddNecessitatedMerge = true; result = fileChanged(path, file1, file2, revision1, revision2, mimeType1, mimeType2, originalProperties, diff, null); myIsAddNecessitatedMerge = false; } } return result; } public SVNStatusType fileDeleted(String path, File file1, File file2, String mimeType1, String mimeType2, SVNProperties originalProperties, boolean[] isTreeConflicted) throws SVNException { setIsConflicted(isTreeConflicted, false); File mergedFile = getFile(path); SVNAdminArea dir = retrieve(mergedFile.getParentFile(), true); if (dir == null) { return SVNStatusType.MISSING; } SVNFileType fileType = SVNFileType.getType(mergedFile); if (fileType == SVNFileType.FILE || fileType == SVNFileType.SYMLINK) { if (areFilesTheSame(file1, originalProperties, mergedFile, dir) || myMergeDriver.myIsForce || myMergeDriver.myIsRecordOnly) { ISVNEventHandler oldEventHandler = getWCAccess().getEventHandler(); getWCAccess().setEventHandler(null); try { delete(mergedFile, myIsForce, myIsDryRun); } catch (SVNException e) { return SVNStatusType.OBSTRUCTED; } finally { getWCAccess().setEventHandler(oldEventHandler); } return SVNStatusType.CHANGED; } return SVNStatusType.OBSTRUCTED; } else if (fileType == SVNFileType.DIRECTORY) { return SVNStatusType.OBSTRUCTED; } else if (fileType == SVNFileType.NONE) { return SVNStatusType.MISSING; } return SVNStatusType.UNKNOWN; } public SVNStatusType[] directoryClosed(String path, boolean[] isTreeConflicted) throws SVNException { setIsConflicted(isTreeConflicted, false); return new SVNStatusType[] { SVNStatusType.UNKNOWN, SVNStatusType.UNKNOWN }; } protected void delete(File path, boolean force, boolean dryRun) throws SVNException { if (!force) { SVNWCManager.canDelete(path, getWCAccess().getOptions(), getWCAccess()); } SVNAdminArea root = getWCAccess().retrieve(path.getParentFile()); if (!dryRun) { SVNWCManager.delete(getWCAccess(), root, path, true, false); } } }