blob: 20df86e70ee8f50e1888db17f12d31e5527631d1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.core.mapping;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.team.core.diff.IDiff;
import org.eclipse.team.core.diff.IDiffChangeListener;
import org.eclipse.team.core.diff.IDiffTree;
import org.eclipse.team.core.diff.IThreeWayDiff;
import org.eclipse.team.core.diff.ITwoWayDiff;
import org.eclipse.team.core.mapping.provider.MergeContext;
/**
* Provides the context for an <code>IResourceMappingMerger</code> or a model
* specific synchronization view that supports merging.
* <p>
* The diff tree associated with this context may be updated asynchronously in
* response to calls to any method of this context (e.g. merge and markAsMerged
* methods) that may result in changes in the synchronization state of
* resources. It may also get updated as a result of changes triggered from
* other sources. Hence, the callback from the diff tree to report changes may
* occur in the same thread as the method call or asynchronously in a separate
* thread, regardless of who triggered the refresh. Clients of this method (and
* any other asynchronous method on this context) may determine if all changes
* have been collected using {@link IJobManager#find(Object)} using this context
* as the <code>family</code> argument in order to determine if there are any
* jobs running that are populating the diff tree. Clients may also call
* {@link IJobManager#join(Object, IProgressMonitor)} if they wish to wait until
* all background handlers related to this context are finished.
* </p>
*
* @see IResourceMappingMerger
* @see MergeContext
* @since 3.2
* @noimplement This interface is not intended to be implemented by clients.
* Clients should instead subclass {@link MergeContext}.
*/
public interface IMergeContext extends ISynchronizationContext {
/**
* Return the type of merge that will be performed when using this
* context (either {@link ISynchronizationContext#TWO_WAY} or
* {@link ISynchronizationContext#THREE_WAY}). In most cases,
* this type which match that returned by {@link ISynchronizationContext#getType()}.
* However, for some THREE_WAY synchronizations, the merge type may
* be TWO_WAY which indicates that clients of the context should
* ignore local changes when performing merges. This capability is
* provided to support replace operations that support three-way
* preview but ignore local changes when replacing.
* @return the type of merge that will be performed when using this
* context.
*/
public int getMergeType();
/**
* Method that allows the model merger to signal that the file associated
* with the given diff node has been completely merged. Model mergers can
* call this method if they have transfered all changes from a remote file
* to a local file and wish to signal that the merge is done. This will
* allow repository providers to update the synchronization state of the
* file to reflect that the file is up-to-date with the repository.
* <p>
* For two-way merging, this method can be used to reject any change. For
* three-way merging, this method should only be used when remote content in
* being merged with local content (i.e. the file exists both locally and
* remotely) with the exception that it can also be used to keep local
* changes to a file that has been deleted. See the
* {@link #merge(IDiff[], boolean, IProgressMonitor) } method for more
* details. For other cases in which either the local file or remote file
* does not exist, one of the <code>merge</code> methods should be used.
* This is done to accommodate repositories that have special handling for
* file additions, removals and moves. Invoking this method with a diff node
* associated with a folder will have no effect.
* <p>
* The <code>inSyncHint</code> allows a client to indicate to the context
* that the model persisted in the file is in-sync. If the hint is
* <code>true</code>, the context should compare the local and remote
* file at the content level and make the local file in-sync with the remote
* if the contents are the same.
* </p>
*
* @param node the diff node whose file has been merged
* @param inSyncHint a hint to the context that the model persisted in the
* file is in-sync.
* @param monitor a progress monitor
* @throws CoreException if errors occur
*/
public void markAsMerged(IDiff node, boolean inSyncHint,
IProgressMonitor monitor) throws CoreException;
/**
* Mark the files associated with the given diff nodes as being merged.
* This method is equivalent to calling {@link #markAsMerged(IDiff, boolean, IProgressMonitor) }
* for each diff but gives the context the opportunity to optimize the
* operation for multiple files.
* <p>
* This method will batch change notification by using the
* {@link #run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor) }
* method. The rule for he method will be obtained using
* {@link #getMergeRule(IDiff)} and the flags will be
* <code>IResource.NONE</code> meaning that intermittent change events may
* occur. Clients may wrap the call in an outer run that either uses a
* broader scheduling rule or the <code>IWorkspace.AVOID_UPDATES</code>
* flag.
*
* @param nodes the nodes to be marked as merged
* @param inSyncHint a hint to the context that the model persisted in the
* file is in-sync.
* @param monitor a progress monitor
* @throws CoreException if errors occur
*/
public void markAsMerged(IDiff[] nodes, boolean inSyncHint,
IProgressMonitor monitor) throws CoreException;
/**
* Method that can be called by the model merger to attempt a file-system
* level merge. This is useful for cases where the model merger does not
* need to do any special processing to perform the merge. By default, this
* method attempts to use an appropriate {@link IStorageMerger} to merge the
* files covered by the provided traversals. If a storage merger cannot be
* found, the text merger is used. If this behavior is not desired,
* sub-classes of {@link MergeContext} may override this method.
* <p>
* This method does a best-effort attempt to merge of the file associated
* with the given diff. A file that could not be merged will be indicated in
* the returned status. If the status returned has the code
* <code>MergeStatus.CONFLICTS</code>, the list of failed files can be
* obtained by calling the <code>MergeStatus#getConflictingFiles()</code>
* method.
* <p>
* It is not expected that clients of this API will associate special
* meaning with the existence of a folder other than the fact that it
* contains files. The sync delta tree should still include folder changes
* so that clients that have a one-to-one correspondence between their model
* objects and folders can decorate these elements appropriately. However,
* clients of this API will only be expected to perform operations on file
* deltas and will expect folders to be created as needed to contain the
* files (i.e. implementations of this method should ignore any folder
* deltas in the provided deltas). Clients will also expect local folders
* that have incoming folder deletions to be removed once all the folder's
* children have been removed using merge.
* <p>
* There are two special cases where merge is meaningful for folders. First,
* a merge on a local added empty folder with force set should delete the
* folder. However, the folder should not be deleted if it has any local
* children unless merge is called for those resources first and they end up
* being deleted as a result. Second, a merge on an incoming folder addition
* should create the empty folder locally.
* <p>
* It is not expected that clients of this API will be capable of dealing
* with namespace conflicts. Implementors should ensure that any namespace
* conflicts are dealt with before the merger is invoked.
* <p>
* The deltas provided to this method should be those obtained from the tree ({@link ISynchronizationContext#getDiffTree()})
* of this context. Any resource changes triggered by this merge will be
* reported through the resource delta mechanism and the change notification
* mechanisms of the delta tree associated with this context.
* <p>
* For two-way merging, as indicated by either the
* {@link ISynchronizationContext#getType()} or {@link #getMergeType()}
* methods, clients can either accept changes using the
* {@link #merge(IDiff[], boolean, IProgressMonitor) } method or reject them
* using {@link #markAsMerged(IDiff, boolean, IProgressMonitor) }.
* Three-way changes are a bit more complicated. The following list
* summarizes how particular remote file changes can be handled. The delta
* kind and flags mentioned in the descriptions are obtained the remote
* change (see {@link IThreeWayDiff#getRemoteChange()}), whereas conflicts
* are indicated by the three-way delta itself.
* <ul>
*
* <li> When the delta kind is {@link IDiff#ADD} and the delta is also a
* move (i.e. the {@link ITwoWayDiff#MOVE_FROM} is set). The merge can
* either use the {@link #merge(IDiff[], boolean, IProgressMonitor) } method
* to accept the rename or perform an
* {@link IFile#move(IPath, boolean, boolean, IProgressMonitor) } where the
* source file is obtained using {@link ITwoWayDiff#getFromPath()} and the
* destination is the path of the delta ({@link IDiff#getPath()}). This
* later approach is helpful in the case where the local file and remote
* file both contain content changes (i.e. the file can be moved by the
* model and then the contents can be merged by the model). </li>
*
* <li> When the delta kind is {@link IDiff#REMOVE} and the delta is also a
* move (i.e. the {@link ITwoWayDiff#MOVE_TO} is set). The merge can either
* use the {@link #merge(IDiff[], boolean, IProgressMonitor) } method to
* accept the rename or perform an
* {@link IFile#move(IPath, boolean, boolean, IProgressMonitor) } where the
* source file is obtained using {@link IDiff#getPath()} and the destination
* is obtained from {@link ITwoWayDiff#getToPath()}. This later approach is
* helpful in the case where the local file and remote file both contain
* content changes (i.e. the file can be moved by the model and then the
* contents can be merged by the model). </li>
*
* <li> When the delta kind is {@link IDiff#ADD} and it is not part of a
* move, the merger must use the
* {@link #merge(IDiff[], boolean, IProgressMonitor) } method to accept this
* change. If there is a conflicting addition, the force flag can be set to
* override the local change. If the model wishes to keep the local changes,
* they can overwrite the file after merging it. Models should consult the
* flags to see if the remote change is a rename ({@link ITwoWayDiff#MOVE_FROM}).
* </li>
*
* <li>When the delta kind is {@link IDiff#REMOVE} and it is not part of a
* move, the merger can use the
* {@link #merge(IDiff[], boolean, IProgressMonitor) } method but could also
* perform the delete manually using any of the {@link IFile} delete
* methods. In the case where there are local changes to the file being
* deleted, the model may either choose to merge using the force flag (thus
* removing the file and the local changes) or call
* {@link #markAsMerged(IDiff, boolean, IProgressMonitor) } on the file
* which will convert the incoming deletion to an outgoing addition.</li>
*
* <li>When the delta kind is {@link IDiff#CHANGE} and there is no
* conflict, the model is advised to use the
* {@link #merge(IDiff[], boolean, IProgressMonitor) } method to merge these
* changes as this is the most efficient means to do so. However, the model
* can choose to perform the merge themselves and then invoke
* {@link #markAsMerged(IDiff, boolean, IProgressMonitor) } with the
* <code>inSyncHint</code> set to <code>true</code> but this will be
* less efficient. </li>
*
* <li>When the delta kind is {@link IDiff#CHANGE} and there is a conflict,
* the model can use the {@link #merge(IDiff[], boolean, IProgressMonitor) }
* method to merge these changes. If the force flag is not set, an
* auto-merge is attempted using an appropriate {@link IStorageMerger}. If
* the force flag is set, the local changes are discarded. The model can
* choose to attempt the merge themselves and, if it is successful, invoke
* {@link #markAsMerged(IDiff, boolean, IProgressMonitor) } with the
* <code>inSyncHint</code> set to <code>false</code> which will make the
* file an outgoing change. </li>
* </ul>
*
* TODO: need to talk about ITwoWayDelta CONTENT and REPLACED
*
* @see IDiffTree#addDiffChangeListener(IDiffChangeListener)
* @see org.eclipse.core.resources.IWorkspace#addResourceChangeListener(IResourceChangeListener)
*
* @param diff
* the difference to be merged
* @param ignoreLocalChanges
* ignore any local changes when performing the merge.
* @param monitor
* a progress monitor
* @return a status indicating success or failure. A code of
* <code>MergeStatus.CONFLICTS</code> indicates that the file
* contain non-mergable conflicts and must be merged manually.
* @throws CoreException
* if an error occurs
*/
public IStatus merge(IDiff diff, boolean ignoreLocalChanges, IProgressMonitor monitor)
throws CoreException;
/**
* Attempt to merge any files associated with the given diffs. This method
* is equivalent to calling
* {@link #merge(IDiff, boolean, IProgressMonitor) } for each diff
* individually but gives the context a chance to perform a more optimal
* merge involving multiple resources.
* <p>
* This method will batch change notification by using the
* {@link #run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor) }
* method. The rule for he method will be obtained using
* {@link #getMergeRule(IDiff) } and the flags will be
* <code>IResource.NONE</code> meaning that intermittent change events may
* occur. Clients may wrap the call in an outer run that either uses a
* broader scheduling rule or the <code>IWorkspace.AVOID_UPDATES</code>
* flag.
*
* @param diffs the differences to be merged
* @param ignoreLocalChanges ignore any local changes when performing the merge.
* @param monitor a progress monitor
* @return a status indicating success or failure. A code of
* <code>MergeStatus.CONFLICTS</code> indicates that the file
* contain non-mergable conflicts and must be merged manually.
* @throws CoreException if an error occurs
*/
public IStatus merge(IDiff[] diffs, boolean ignoreLocalChanges,
IProgressMonitor monitor) throws CoreException;
/**
* Reject the change associated with the given diff. For a two-way
* merge, this should just remove the change from the set of changes in
* the diff tree. For a three-way merge, this should throw away the
* remote change and keep any local changes. So, for instance, if
* the diff is an incoming change or a conflict, the result of rejecting the
* change should be an outgoing change that will make the remote state
* match the local state.
* @param diff the diff
* @param monitor a progress monitor
* @throws CoreException if an error occurs
*/
public void reject(IDiff diff, IProgressMonitor monitor) throws CoreException;
/**
* Reject the changes associated with the given diffs. This method is
* equivalent to calling {@link #reject(IDiff, IProgressMonitor)} for
* each diff.
* @param diffs the diffs
* @param monitor a progress monitor
* @throws CoreException if an error occurs
*/
public void reject(IDiff[] diffs, IProgressMonitor monitor) throws CoreException;
/**
* Runs the given action as an atomic workspace operation. It has the same
* semantics as
* {@link IWorkspace#run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor)}
* with the added behavior that any synchronization state updates are
* batched or deferred until the end of the operation (depending on the
* {@link IWorkspace#AVOID_UPDATE } flag.
*
* @param runnable a workspace runnable
* @param rule a scheduling rule to be obtained while the runnable is run
* @param flags flags indicating when updates occur (either
* <code>IResource.NONE</code> or
* <code>IWorkspace.AVOID_UPDATE</code>.
* @param monitor a progress monitor
* @throws CoreException if an error occurs
*/
public void run(IWorkspaceRunnable runnable, ISchedulingRule rule,
int flags, IProgressMonitor monitor) throws CoreException;
/**
* Return the scheduling rule that is required to merge (or reject) the resource
* associated with the given diff. If a resource being merged is a folder or
* project, the returned rule will be sufficient to merge any files
* contained in the folder or project. The returned rule also applies to
* {@link #markAsMerged(IDiff, boolean, IProgressMonitor) }
* and {@link #reject(IDiff, IProgressMonitor)}.
*
* @param diff the diff to be merged
* @return the scheduling rule that is required to merge the resource of the
* given diff
*/
public ISchedulingRule getMergeRule(IDiff diff);
/**
* Return the scheduling rule that is required to merge (or reject) the resources
* associated with the given diffs. If a resource being merged is a folder
* or project, the returned rule will be sufficient to merge any files
* contained in the folder or project. The returned rule also applies to
* {@link #markAsMerged(IDiff[], boolean, IProgressMonitor) }
* and {@link #reject(IDiff[], IProgressMonitor)}.
*
* @param diffs the diffs being merged
* @return the scheduling rule that is required to merge the resources of
* the given diffs
*/
public ISchedulingRule getMergeRule(IDiff[] diffs);
}