blob: 26f1d17a19410a0743edecc1cc44695f75500f93 [file] [log] [blame]
package org.eclipse.team.internal.ccvs.core.resources;
/*
* (c) Copyright IBM Corp. 2000, 2002.
* All Rights Reserved.
*/
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.team.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.ccvs.core.ICVSFolder;
import org.eclipse.team.ccvs.core.ICVSRemoteResource;
import org.eclipse.team.ccvs.core.ICVSResource;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.sync.ILocalSyncElement;
import org.eclipse.team.core.sync.IRemoteResource;
import org.eclipse.team.core.sync.IRemoteSyncElement;
import org.eclipse.team.core.sync.RemoteSyncElement;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.client.Update;
import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
import org.eclipse.team.internal.ccvs.core.util.Assert;
public class CVSRemoteSyncElement extends RemoteSyncElement {
CVSLocalSyncElement localSync;
IRemoteResource remote;
boolean isThreeWay = true;
public CVSRemoteSyncElement(boolean isThreeWay, IResource local, IRemoteResource base, IRemoteResource remote) {
localSync = new CVSLocalSyncElement(local, base);
this.remote = remote;
this.isThreeWay = isThreeWay;
}
/*
* @see RemoteSyncElement#create(IResource, IRemoteResource, IRemoteResource)
*/
public IRemoteSyncElement create(boolean isThreeWay, IResource local, IRemoteResource base, IRemoteResource remote, Object data) {
return new CVSRemoteSyncElement(isThreeWay, local, base, remote);
}
/*
* @see IRemoteSyncElement#getRemote()
*/
public IRemoteResource getRemote() {
return remote;
}
/*
* @see IRemoteSyncElement#isOutOfDate()
*/
public boolean isOutOfDate() {
IRemoteResource base = getBase();
if(base!=null && remote!=null) {
ICVSRemoteResource remoteCvs = (ICVSRemoteResource)remote;
ICVSRemoteResource baseCvs = (ICVSRemoteResource)base;
return ! remoteCvs.equals(baseCvs);
} else if(base!=null && remote==null) {
return true;
} else {
return false;
}
}
/*
* @see LocalSyncElement#getData()
*/
protected Object getData() {
return localSync.getData();
}
/*
* @see ILocalSyncElement#getLocal()
*/
public IResource getLocal() {
return localSync.getLocal();
}
/*
* @see ILocalSyncElement#getBase()
*/
public IRemoteResource getBase() {
return localSync.getBase();
}
/*
* @see ILocalSyncElement#isDirty()
*/
public boolean isDirty() {
return localSync.isDirty();
}
/*
* @see ILocalSyncElement#isCheckedOut()
*/
public boolean isCheckedOut() {
return localSync.isCheckedOut();
}
/*
* Local helper to indicate if the corresponding local resource has a base
*
* XXX Should this be part of the interface?
*/
public boolean hasBase() {
return getBase() != null;
}
/*
* @see ILocalSyncElement#hasRemote()
*/
public boolean hasRemote() {
return remote != null;
}
/*
* @see LocalSyncElement#create(IResource, IRemoteResource, Object)
*/
public ILocalSyncElement create(IResource local, IRemoteResource base, Object data) {
return localSync.create(local, base, data);
}
/*
* @see LocalSyncElement#isIgnored(IResource)
*/
protected boolean isIgnored(IResource resource) {
return localSync.isIgnored(resource);
}
/*
* @see IRemoteSyncElement#ignoreBaseTree()
*/
public boolean isThreeWay() {
return isThreeWay;
}
/*
* Update the sync info of the local resource in such a way that the local changes can be committed.
*/
public void makeOutgoing(IProgressMonitor monitor) throws TeamException {
int syncKind = getSyncKind(GRANULARITY_TIMESTAMP, monitor);
boolean incoming = (syncKind & DIRECTION_MASK) == INCOMING;
boolean outgoing = (syncKind & DIRECTION_MASK) == OUTGOING;
ICVSResource local = localSync.getCVSResource();
RemoteResource remote = (RemoteResource)getRemote();
ResourceSyncInfo info = local.getSyncInfo();
String revision = null;
if (outgoing) {
// The sync info is alright, it's already outgoing!
return;
} else if (incoming) {
// We have an incoming change, addition, or deletion that we want to ignore
if (local.exists()) {
// We could have an incoming change or deletion
if (remote == null) {
String mode = KSubstOption.fromPattern(local.getName()).toMode();
info = new ResourceSyncInfo(local.getName(), ResourceSyncInfo.ADDED_REVISION, ResourceSyncInfo.DUMMY_TIMESTAMP,
mode, local.getParent().getFolderSyncInfo().getTag(), null);
revision = info.getRevision();
} else {
info = remote.getSyncInfo();
// Otherwise change the revision to the remote revision
revision = info.getRevision();
// Use the local sync info for the other info
info = local.getSyncInfo();
}
} else {
// We have an incoming add, turn it around as an outgoing delete
info = remote.getSyncInfo();
revision = ResourceSyncInfo.DELETED_PREFIX + info.getRevision();
}
} else if (local.exists()) {
// We have a conflict and a local resource!
if (hasRemote()) {
if (hasBase()) {
// We have a conflicting change, Update the local revision
revision = remote.getSyncInfo().getRevision();
} else {
// We have conflictin additions.
// We need to fetch the contents of the remote to get all the relevant information (timestamp, permissions)
remote.getContents(Policy.monitorFor(monitor));
info = remote.getSyncInfo();
revision = info.getRevision();
}
} else if (hasBase()) {
// We have a remote deletion. Make the local an addition
revision = ResourceSyncInfo.ADDED_REVISION;
} else {
// There's a local, no base and no remote. We can't possible have a conflict!
Assert.isTrue(false);
}
} else {
// We have a conflict and there is no local!
if (hasRemote()) {
// We have a local deletion that conflicts with remote changes.
revision = ResourceSyncInfo.DELETED_PREFIX + remote.getSyncInfo().getRevision();
} else {
// We have conflicting deletions. Clear the sync info
local.setSyncInfo(null);
return;
}
}
info = new ResourceSyncInfo(info.getName(), revision, ResourceSyncInfo.DUMMY_TIMESTAMP, info.getKeywordMode(), local.getParent().getFolderSyncInfo().getTag(), info.getPermissions());
local.setSyncInfo(info);
}
/*
* Update the sync info of the local resource in such a way that the remote resource can be loaded
* ignore any local changes.
*/
public void makeIncoming(IProgressMonitor monitor) throws TeamException {
// To make outgoing deletions incoming, the local will not exist but
// it is still important to unmanage (e.g. delete all meta info) for the
// deletion.
CVSWorkspaceRoot.getCVSResourceFor(getLocal()).unmanage();
}
/*
* Load the resource and folder sync info into the local from the remote
*
* This method can be used on incoming folder additions to set the folder sync info properly
* without hitting the server again. It also applies to conflicts that involves unmanaged
* local resources.
*/
public void makeInSync(IProgressMonitor monitor) throws TeamException {
// Only work on folders
if (! isContainer()) return;
int syncKind = getSyncKind(GRANULARITY_TIMESTAMP, monitor);
boolean outgoing = (syncKind & DIRECTION_MASK) == OUTGOING;
if (outgoing) return;
ICVSFolder local = (ICVSFolder)localSync.getCVSResource();
RemoteFolder remote = (RemoteFolder)getRemote();
// The parent must be managed
if (! local.getParent().isCVSFolder())
return;
if (! local.exists()) {
local.mkdir();
} else {
// If the folder already has CVS info, check that the remote and local match
if(local.isManaged() && local.isCVSFolder() && ! remote.getFolderSyncInfo().equals(local.getFolderSyncInfo())) {
throw new CVSException(IStatus.ERROR, 0, Policy.bind("CVSRemoteSyncElement.alreadyManaged"));//$NON-NLS-1$
}
}
// Since the parent is managed, this will also set the resource sync info. It is
// impossible for an incoming folder addition to map to another location in the
// repo, so we assume that using the parent's folder sync as a basis is safe.
FolderSyncInfo remoteInfo = remote.getFolderSyncInfo();
FolderSyncInfo localInfo = local.getParent().getFolderSyncInfo();
local.setFolderSyncInfo(new FolderSyncInfo(remoteInfo.getRepository(), remoteInfo.getRoot(), localInfo.getTag(), localInfo.getIsStatic()));
}
/*
* @see ILocalSyncElement#getSyncKind(int, IProgressMonitor)
*/
public int getSyncKind(int granularity, IProgressMonitor progress) {
// 1. Run the generic sync calculation algorithm, then handle CVS specific
// sync cases.
int kind = super.getSyncKind(granularity, progress);
// 2. Set the CVS specific sync type based on the workspace sync state provided
// by the CVS server.
if(remote!=null && (kind & IRemoteSyncElement.PSEUDO_CONFLICT) == 0) {
int type = ((RemoteResource)remote).getWorkspaceSyncState();
switch(type) {
// the server compared both text files and decided that it cannot merge
// them without line conflicts.
case Update.STATE_CONFLICT:
return kind | ILocalSyncElement.MANUAL_CONFLICT;
// the server compared both text files and decided that it can safely merge
// them without line conflicts.
case Update.STATE_MERGEABLE_CONFLICT:
return kind | ILocalSyncElement.AUTOMERGE_CONFLICT;
}
}
// 3. unmanage delete/delete conflicts and return that they are in sync
kind = handleDeletionConflicts(kind);
return kind;
}
/*
* If the resource has a delete/delete conflict then ensure that the local is unmanaged so that the
* sync info can be properly flushed.
*/
private int handleDeletionConflicts(int kind) {
if(kind == (IRemoteSyncElement.CONFLICTING | IRemoteSyncElement.DELETION | IRemoteSyncElement.PSEUDO_CONFLICT)) {
try {
ICVSResource cvsResource = localSync.getCVSResource();
if(!isContainer() && cvsResource.isManaged()) {
cvsResource.unmanage();
}
return IRemoteSyncElement.IN_SYNC;
} catch(CVSException e) {
CVSProviderPlugin.log(e.getStatus());
return IRemoteSyncElement.CONFLICTING | IRemoteSyncElement.DELETION;
}
}
return kind;
}
}