Initial work at refactoring the CVS sync trees to be API
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/trees/CoreSynchronizerRemoteHandleTree.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/trees/CoreSynchronizerRemoteHandleTree.java
new file mode 100644
index 0000000..2cac8f5
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/trees/CoreSynchronizerRemoteHandleTree.java
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.core.subscribers.trees;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ISynchronizer;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.sync.IRemoteResource;
+import org.eclipse.team.internal.core.Assert;
+import org.eclipse.team.internal.core.Policy;
+
+public abstract class CoreSynchronizerRemoteHandleTree extends RemoteHandleTree {
+
+	private static final byte[] NO_REMOTE = new byte[0];
+	private static final String SYNC_KEY_QUALIFIER = "org.eclipse.team.core.remote_handle_tree"; //$NON-NLS-1$
+	private QualifiedName syncName;
+	private Set changedResources = new HashSet();
+	
+	public CoreSynchronizerRemoteHandleTree(String id) {
+		this.syncName = new QualifiedName(SYNC_KEY_QUALIFIER, id);
+		getSynchronizer().add(syncName);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.subscribers.trees.RemoteHandleTree#dispose()
+	 */
+	public void dispose() {
+		getSynchronizer().remove(getSyncName());
+	}
+	
+	protected ISynchronizer getSynchronizer() {
+		return ResourcesPlugin.getWorkspace().getSynchronizer();
+	}
+
+	protected QualifiedName getSyncName() {
+		return syncName;
+	}
+
+	protected void setSyncBytes(IResource resource, byte[] bytes) throws TeamException {
+		byte[] oldBytes = getSyncBytes(resource);
+		if (oldBytes != null && bytesEqual(oldBytes, bytes)) return;
+		try {
+			getSynchronizer().setSyncInfo(getSyncName(), resource, bytes);
+		} catch (CoreException e) {
+			throw TeamException.asTeamException(e);
+		}
+		changedResources.add(resource);
+	}
+
+	protected void removeSyncBytes(IResource resource, int depth, boolean silent) throws TeamException {
+		if (resource.exists() || resource.isPhantom()) {
+			try {
+				getSynchronizer().flushSyncInfo(getSyncName(), resource, depth);
+			} catch (CoreException e) {
+				throw TeamException.asTeamException(e);
+			}
+			if(silent == false) {
+				changedResources.add(resource);
+			}
+		}
+	}
+
+	private boolean bytesEqual(byte[] syncBytes, byte[] oldBytes) {
+		if (syncBytes.length != oldBytes.length) return false;
+		for (int i = 0; i < oldBytes.length; i++) {
+			if (oldBytes[i] != syncBytes[i]) return false;
+		}
+		return true;
+	}
+	
+	protected void collectChanges(IResource local, IRemoteResource remote, int depth, IProgressMonitor monitor) throws TeamException {
+		byte[] remoteBytes;
+		if (remote != null) {
+			remoteBytes = toBytes(remote);
+		} else {
+			remoteBytes = NO_REMOTE;
+		}
+		setSyncBytes(local, remoteBytes);
+		if (depth == IResource.DEPTH_ZERO) return;
+		Map children = mergedMembers(local, remote, monitor);	
+		for (Iterator it = children.keySet().iterator(); it.hasNext();) {
+			IResource localChild = (IResource) it.next();
+			IRemoteResource remoteChild = (IRemoteResource)children.get(localChild);
+			collectChanges(localChild, remoteChild, 
+				depth == IResource.DEPTH_INFINITE ? IResource.DEPTH_INFINITE : IResource.DEPTH_ZERO, 
+				monitor);
+		}
+	}
+
+	protected Map mergedMembers(IResource local, IRemoteResource remote, IProgressMonitor progress) throws TeamException {
+
+		// {IResource -> IRemoteResource}
+		Map mergedResources = new HashMap();
+	
+		IRemoteResource[] remoteChildren = getRemoteChildren(remote, progress);
+	
+		IResource[] localChildren = getLocalChildren(local);		
+
+		if (remoteChildren.length > 0 || localChildren.length > 0) {
+			List syncChildren = new ArrayList(10);
+			Set allSet = new HashSet(20);
+			Map localSet = null;
+			Map remoteSet = null;
+
+			if (localChildren.length > 0) {
+				localSet = new HashMap(10);
+				for (int i = 0; i < localChildren.length; i++) {
+					IResource localChild = localChildren[i];
+					String name = localChild.getName();
+					localSet.put(name, localChild);
+					allSet.add(name);
+				}
+			}
+
+			if (remoteChildren.length > 0) {
+				remoteSet = new HashMap(10);
+				for (int i = 0; i < remoteChildren.length; i++) {
+					IRemoteResource remoteChild = remoteChildren[i];
+					String name = remoteChild.getName();
+					remoteSet.put(name, remoteChild);
+					allSet.add(name);
+				}
+			}
+	
+			Iterator e = allSet.iterator();
+			while (e.hasNext()) {
+				String keyChildName = (String) e.next();
+
+				if (progress != null) {
+					if (progress.isCanceled()) {
+						throw new OperationCanceledException();
+					}
+					// XXX show some progress?
+				}
+
+				IResource localChild =
+					localSet != null ? (IResource) localSet.get(keyChildName) : null;
+
+				IRemoteResource remoteChild =
+					remoteSet != null ? (IRemoteResource) remoteSet.get(keyChildName) : null;
+			
+				if (localChild == null) {
+					// there has to be a remote resource available if we got this far
+					Assert.isTrue(remoteChild != null);
+					boolean isContainer = remoteChild.isContainer();				
+					localChild = getResourceChild(local /* parent */, keyChildName, isContainer);
+				}
+				mergedResources.put(localChild, remoteChild);				
+			}
+		}
+		return mergedResources;
+	}
+
+	private IRemoteResource[] getRemoteChildren(IRemoteResource remote, IProgressMonitor progress) throws TeamException {
+		return remote != null ? remote.members(progress) : new IRemoteResource[0];
+	}
+
+	protected abstract IResource[] getLocalChildren(IResource local) throws TeamException;
+
+	/*
+	 * Returns a handle to a non-existing resource.
+	 */
+	private IResource getResourceChild(IResource parent, String childName, boolean isContainer) {
+		if (parent.getType() == IResource.FILE) {
+			return null;
+		}
+		if (isContainer) {
+			return ((IContainer) parent).getFolder(new Path(childName));
+		} else {
+			return ((IContainer) parent).getFile(new Path(childName));
+		}
+	}
+
+	/**
+	 * Return true if remote bytes for the resource have been fetched during
+	 * a refresh. This will also return true for remote resources that do not exist
+	 * (i.e. they have no sync bytes but did not exist remotely at the time of the
+	 * last refresh.
+	 * 
+	 * @param resource
+	 * @return
+	 */
+	protected boolean hasRemoteBytesFor(IResource resource) throws CVSException {
+		return super.getSyncBytes(resource) != null;
+	}
+
+	/**
+	 * This method will return null in both cases when the remote has never been fetched
+	 * or when the remote does not exist. Use <code>hasRemoteBytesFor</code> to
+	 * differentiate these cases.  
+	 */
+	public byte[] getSyncBytes(IResource resource) throws CVSException {
+		byte[] syncBytes = super.getSyncBytes(resource);
+		if (syncBytes != null && Util.equals(syncBytes, NO_REMOTE)) {
+			// If it is known that there is no remote, return null
+			return null;
+		}
+		return syncBytes;
+	}
+
+	public IResource[] getChangedResources() {
+		return (IResource[]) changedResources.toArray(new IResource[changedResources.size()]);
+	}
+
+	public void resetChanges() {
+		changedResources.clear();
+	}
+
+	/**
+	 * Refreshes the contents of the remote synchronizer and returns the list
+	 * of resources whose remote sync state changed.
+	 * 
+	 * @param resources
+	 * @param depth
+	 * @param monitor
+	 * @return
+	 * @throws TeamException
+	 */
+	public IResource[] refresh(IResource[] resources, int depth, IProgressMonitor monitor) throws TeamException {
+		int work = 100 * resources.length;
+		monitor.beginTask(null, work);
+		resetChanges();
+		try {
+			for (int i = 0; i < resources.length; i++) {
+				IResource resource = resources[i];	
+			
+				// build the remote tree only if an initial tree hasn't been provided
+				ICVSRemoteResource	tree = buildRemoteTree(resource, depth, Policy.subMonitorFor(monitor, 70));
+			
+				// update the known remote handles 
+				IProgressMonitor sub = Policy.infiniteSubMonitorFor(monitor, 30);
+				try {
+					sub.beginTask(null, 512);
+					//removeSyncBytes(resource, IResource.DEPTH_INFINITE);
+					collectChanges(resource, tree, depth, sub);
+				} finally {
+					sub.done();	 
+				}
+			}
+		} finally {
+			monitor.done();
+		}
+		IResource[] changes = getChangedResources();
+		resetChanges();
+		return changes;
+	}
+
+	/**
+	 * Build a remote tree for the given parameters.
+	 */
+	protected ICVSRemoteResource buildRemoteTree(IResource resource, int depth, IProgressMonitor monitor) throws TeamException {
+		// TODO: we are currently ignoring the depth parameter because the build remote tree is
+		// by default deep!
+		return CVSWorkspaceRoot.getRemoteTree(resource, tag, monitor);
+	}
+	
+	protected abstract byte[] toBytes(IRemoteResource remoteResource);
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/trees/RemoteHandleTree.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/trees/RemoteHandleTree.java
new file mode 100644
index 0000000..32cdf44
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/trees/RemoteHandleTree.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.core.subscribers.trees;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.sync.IRemoteResource;
+
+/**
+ * A RemoteHandleTree stores remote handles for local resources. 
+ */
+public abstract class RemoteHandleTree {
+	/**
+	 * Returns the remote handle stored in this tree for the given local resource.
+	 * @param resource the resource for which to find a remote
+	 * @return a remote handle or <code>null</code> if the tree doesn't contain
+	 * a handle for the resource.
+	 */
+	public abstract IRemoteResource getRemoteHandle(IResource resource);
+	
+	/** 
+	 * Refreshes the resource hierarchy from the given resources and their children (to the specified depth) 
+	 * from the corresponding resources in the remote location.
+	 * @param resources
+	 * @param depth
+	 * @param monitor
+	 * @return IResource[]
+	 */
+	public abstract IResource[] refresh(IResource[] resources, int depth, IProgressMonitor monitor);
+	
+	/**
+	 * Forget about the given handle.
+	 * @param resource
+	 */
+	public abstract void remove(IResource resource);
+	
+	/**
+	 * Disposes of this tree. It is the client's responsibility to ensure that this method is called when the
+	 * remote tree is no longer needed.	 
+	 */
+	public abstract void dispose();
+}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/trees/TreeTeamSubscriber.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/trees/TreeTeamSubscriber.java
new file mode 100644
index 0000000..d767db2
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/trees/TreeTeamSubscriber.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.core.subscribers.trees;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.subscribers.ComparisonCriteria;
+import org.eclipse.team.core.subscribers.TeamDelta;
+import org.eclipse.team.core.subscribers.TeamSubscriber;
+import org.eclipse.team.internal.core.Policy;
+
+public abstract class TreeTeamSubscriber extends TeamSubscriber {
+	
+	private QualifiedName id;
+	private String name;
+	private String description;
+	
+	// options this subscriber supports for determining the sync state of resources
+	private Map comparisonCriterias = new HashMap();
+	private String defaultCriteria;
+	
+	public TreeTeamSubscriber(QualifiedName id, String name, String description) {
+		this.id = id;
+		this.name = name;
+		this.description = description;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getId()
+	 */
+	public QualifiedName getId() {
+		return id;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getName()
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getDescription()
+	 */
+	public String getDescription() {
+		return description;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getCurrentComparisonCriteria()
+	 */
+	public ComparisonCriteria getCurrentComparisonCriteria() {		
+		return (ComparisonCriteria)comparisonCriterias.get(defaultCriteria);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#setCurrentComparisonCriteria(java.lang.String)
+	 */
+	public void setCurrentComparisonCriteria(String id) throws TeamException {
+		if(! comparisonCriterias.containsKey(id)) {
+			throw new TeamException(id + " is not a valid comparison criteria");
+		}
+		this.defaultCriteria = id;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getComparisonCriterias()
+	 */
+	public ComparisonCriteria[] getComparisonCriterias() {
+		return (ComparisonCriteria[]) comparisonCriterias.values().toArray(new ComparisonCriteria[comparisonCriterias.size()]);
+	}
+
+	protected void addComparisonCriteria(ComparisonCriteria comparator) {
+		comparisonCriterias.put(comparator.getId(), comparator);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.TeamSubscriber#members(org.eclipse.core.resources.IResource)
+	 */
+	public IResource[] members(IResource resource) throws TeamException {
+		if(resource.getType() == IResource.FILE) {
+			return new IResource[0];
+		}	
+		try {
+			IResource[] members = ((IContainer)resource).members(true /* include phantoms */);
+			List filteredMembers = new ArrayList(members.length);
+			for (int i = 0; i < members.length; i++) {
+				IResource member = members[i];
+				
+				if(member.isPhantom() && getRemoteTree().getRemoteHandle(member) == null) {
+					continue;
+				}
+				
+				if (isSupervised(resource)) {
+					filteredMembers.add(member);
+				}
+			}
+			return (IResource[]) filteredMembers.toArray(new IResource[filteredMembers.size()]);
+		} catch (CoreException e) {
+			throw TeamException.asTeamException(e);
+		}
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#refresh(org.eclipse.core.resources.IResource[], int, org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public void refresh(IResource[] resources, int depth, IProgressMonitor monitor) throws TeamException {
+		monitor = Policy.monitorFor(monitor);
+		try {
+			monitor.beginTask(null, 100);
+			IResource[] remoteChanges = getRemoteTree().refresh(resources, depth, Policy.subMonitorFor(monitor, 60));
+			IResource[] baseChanges = getBaseTree().refresh(resources, depth, Policy.subMonitorFor(monitor, 40));
+		
+			Set allChanges = new HashSet();
+			allChanges.addAll(Arrays.asList(remoteChanges));
+			allChanges.addAll(Arrays.asList(baseChanges));
+			IResource[] changedResources = (IResource[]) allChanges.toArray(new IResource[allChanges.size()]);
+			fireTeamResourceChange(TeamDelta.asSyncChangedDeltas(this, changedResources));
+		} finally {
+			monitor.done();
+		} 
+	}
+	
+	/**
+	 * Return the synchronizer that provides the remote resources
+	 */
+	protected abstract RemoteHandleTree getRemoteTree();
+	
+	/**
+	 * Return the synchronizer that provides the base resources
+	 */
+	protected abstract RemoteHandleTree getBaseTree();
+}
diff --git a/bundles/org.eclipse.team.cvs.core/plugin.xml b/bundles/org.eclipse.team.cvs.core/plugin.xml
index 9ff5a3c..3e83cb1 100644
--- a/bundles/org.eclipse.team.cvs.core/plugin.xml
+++ b/bundles/org.eclipse.team.cvs.core/plugin.xml
@@ -63,7 +63,7 @@
    <extension
          point="org.eclipse.team.core.subscriber">
       <subscriber
-            class="org.eclipse.team.internal.ccvs.core.CVSSubscriberFactory">
+            class="org.eclipse.team.internal.ccvs.core.subscribers.CVSSubscriberFactory">
       </subscriber>
    </extension>
 <!-- *************** Resource patterns ignored by CVS **************** -->
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java
index 3d8551e..633e63f 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java
@@ -55,6 +55,7 @@
 import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
 import org.eclipse.team.internal.ccvs.core.resources.FileModificationManager;
+import org.eclipse.team.internal.ccvs.core.subscribers.*;
 import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
 import org.eclipse.team.internal.ccvs.core.util.AddDeleteMoveListener;
 import org.eclipse.team.internal.ccvs.core.util.ProjectDescriptionManager;
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncTreeSubscriber.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncTreeSubscriber.java
deleted file mode 100644
index ab00088..0000000
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncTreeSubscriber.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials 
- * are made available under the terms of the Common Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- * 
- * Contributors:
- *     IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.team.internal.ccvs.core;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.core.resources.IContainer;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.QualifiedName;
-import org.eclipse.team.core.RepositoryProvider;
-import org.eclipse.team.core.TeamException;
-import org.eclipse.team.core.subscribers.ComparisonCriteria;
-import org.eclipse.team.core.subscribers.ContentComparisonCriteria;
-import org.eclipse.team.core.subscribers.SyncInfo;
-import org.eclipse.team.core.subscribers.TeamSubscriber;
-import org.eclipse.team.core.subscribers.TeamDelta;
-import org.eclipse.team.core.sync.IRemoteResource;
-import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
-import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSynchronizer;
-
-/**
- * This class provides common funtionality for three way sychronizing
- * for CVS.
- */
-public abstract class CVSSyncTreeSubscriber extends TeamSubscriber {
-	
-	private QualifiedName id;
-	private String name;
-	private String description;
-	
-	// options this subscriber supports for determining the sync state of resources
-	private Map comparisonCriterias = new HashMap();
-	private String defaultCriteria;
-	
-	CVSSyncTreeSubscriber(QualifiedName id, String name, String description) {
-		this.id = id;
-		this.name = name;
-		this.description = description;
-		initializeComparisonCriteria();
-	}
-
-	/**
-	 * Method invoked from the constructor to initialize the comparison criteria
-	 * and the default criteria.
-	 * This method can be overriden by subclasses.
-	 */
-	protected void initializeComparisonCriteria() {				
-		// setup comparison criteria
-		ComparisonCriteria revisionNumberComparator = new CVSRevisionNumberCompareCriteria();
-		ComparisonCriteria contentsComparator = new ContentComparisonCriteria(new ComparisonCriteria[] {revisionNumberComparator}, false /*consider whitespace */);
-		ComparisonCriteria contentsComparatorIgnoreWhitespace = new ContentComparisonCriteria(new ComparisonCriteria[] {revisionNumberComparator}, true /* ignore whitespace */);
-		
-		addComparisonCriteria(revisionNumberComparator);
-		addComparisonCriteria(contentsComparator);
-		addComparisonCriteria(contentsComparatorIgnoreWhitespace);
-		
-		// default
-		defaultCriteria = revisionNumberComparator.getId();
-	}
-	
-	/**
-	 * Add the comparison criteria to the subscriber
-	 * 
-	 * @param comparator
-	 */
-	protected void addComparisonCriteria(ComparisonCriteria comparator) {
-		comparisonCriterias.put(comparator.getId(), comparator);
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getId()
-	 */
-	public QualifiedName getId() {
-		return id;
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getName()
-	 */
-	public String getName() {
-		return name;
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getDescription()
-	 */
-	public String getDescription() {
-		return description;
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.TeamSubscriber#members(org.eclipse.core.resources.IResource)
-	 */
-	public IResource[] members(IResource resource) throws TeamException {
-		if(resource.getType() == IResource.FILE) {
-			return new IResource[0];
-		}	
-		try {
-			// Filter and return only phantoms associated with the remote synchronizer.
-			IResource[] members = ((IContainer)resource).members(true /* include phantoms */);
-			List filteredMembers = new ArrayList(members.length);
-			for (int i = 0; i < members.length; i++) {
-				IResource member = members[i];
-				
-				// TODO: consider that there may be several sync states on this resource. There
-				// should instead be a method to check for the existance of a set of sync types on
-				// a resource.
-				if(member.isPhantom() && getRemoteSynchronizer().getSyncBytes(member) == null) {
-					continue;
-				}
-				
-				// TODO: Is this a valid use of isSupervised
-				if (isSupervised(resource)) {
-					filteredMembers.add(member);
-				}
-			}
-			return (IResource[]) filteredMembers.toArray(new IResource[filteredMembers.size()]);
-		} catch (CoreException e) {
-			throw CVSException.wrapException(e);
-		}
-
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.TeamSubscriber#roots()
-	 */
-	public IResource[] roots() {
-		return null;
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getRemoteResource(org.eclipse.core.resources.IResource)
-	 */
-	public IRemoteResource getRemoteResource(IResource resource) throws TeamException {
-		return getRemoteSynchronizer().getRemoteResource(resource);
-	}
-
-	public IRemoteResource getBaseResource(IResource resource) throws TeamException {
-		return getBaseSynchronizer().getRemoteResource(resource);
-	}
-
-	/**
-	 * Return the synchronizer that provides the remote resources
-	 */
-	protected abstract ResourceSynchronizer getRemoteSynchronizer();
-	/**
-	 * Return the synchronizer that provides the base resources
-	 */
-	protected abstract ResourceSynchronizer getBaseSynchronizer();
-	
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getSyncInfo(org.eclipse.core.resources.IResource)
-	 */
-	public SyncInfo getSyncInfo(IResource resource, IProgressMonitor monitor) throws TeamException {
-		if (!isSupervised(resource)) return null;
-		IRemoteResource remoteResource = getRemoteResource(resource);
-		if(resource.getType() == IResource.FILE) {
-			IRemoteResource baseResource = getBaseResource(resource);
-			return getSyncInfo(resource, baseResource, remoteResource, monitor);
-		} else {
-			// In CVS, folders do not have a base. Hence, the remote is used as the base.
-			return getSyncInfo(resource, remoteResource, remoteResource, monitor);
-		}
-	}
-
-	/**
-	 * Method that creates an instance of SyncInfo for the provider local, base and remote.
-	 * Can be overiden by subclasses.
-	 * @param local
-	 * @param base
-	 * @param remote
-	 * @param monitor
-	 * @return
-	 */
-	protected SyncInfo getSyncInfo(IResource local, IRemoteResource base, IRemoteResource remote, IProgressMonitor monitor) throws TeamException {
-		try {
-			monitor = Policy.monitorFor(monitor);
-			monitor.beginTask(null, 100);
-			CVSSyncInfo info = new CVSSyncInfo(local, base, remote, this, Policy.subMonitorFor(monitor, 100));
-			
-			// if it's out of sync, then cache the contents
-			//if(info.getKind() != SyncInfo.IN_SYNC && remote != null) {
-			//	remote.getContents(Policy.subMonitorFor(monitor, 30));
-			//}
-			return info;
-		} finally {
-			monitor.done();
-		}
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#refresh(org.eclipse.core.resources.IResource[], int, org.eclipse.core.runtime.IProgressMonitor)
-	 */
-	public void refresh(IResource[] resources, int depth, IProgressMonitor monitor) throws TeamException {
-		monitor = Policy.monitorFor(monitor);
-		try {
-			monitor.beginTask(null, 100);
-			IResource[] remoteChanges = refreshRemote(resources, depth, Policy.subMonitorFor(monitor, 60));
-			IResource[] baseChanges = getBaseSynchronizer().refresh(resources, depth, Policy.subMonitorFor(monitor, 40));
-		
-			Set allChanges = new HashSet();
-			allChanges.addAll(Arrays.asList(remoteChanges));
-			allChanges.addAll(Arrays.asList(baseChanges));
-			IResource[] changedResources = (IResource[]) allChanges.toArray(new IResource[allChanges.size()]);
-			fireTeamResourceChange(TeamDelta.asSyncChangedDeltas(this, changedResources));
-		} finally {
-			monitor.done();
-		} 
-	}
-
-	protected IResource[] refreshRemote(IResource[] resources, int depth, IProgressMonitor monitor) throws TeamException {
-		return getRemoteSynchronizer().refresh(resources, depth, monitor);
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getCurrentComparisonCriteria()
-	 */
-	public ComparisonCriteria getCurrentComparisonCriteria() {		
-		return (ComparisonCriteria)comparisonCriterias.get(defaultCriteria);
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#setCurrentComparisonCriteria(java.lang.String)
-	 */
-	public void setCurrentComparisonCriteria(String id) throws TeamException {
-		if(! comparisonCriterias.containsKey(id)) {
-			throw new CVSException(id + " is not a valid comparison criteria");
-		}
-		this.defaultCriteria = id;
-	}
-	
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getComparisonCriterias()
-	 */
-	public ComparisonCriteria[] getComparisonCriterias() {
-		return (ComparisonCriteria[]) comparisonCriterias.values().toArray(new ComparisonCriteria[comparisonCriterias.size()]);
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#isSupervised(org.eclipse.core.resources.IResource)
-	 */
-	public boolean isSupervised(IResource resource) throws TeamException {
-		RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject(), CVSProviderPlugin.getTypeId());
-		if (provider == null) return false;
-		// TODO: what happens for resources that don't exist?
-		// TODO: is it proper to use ignored here?
-		ICVSResource cvsThing = CVSWorkspaceRoot.getCVSResourceFor(resource);
-		if (cvsThing.isIgnored()) {
-			// An ignored resource could have an incoming addition (conflict)
-			return getRemoteSynchronizer().getSyncBytes(resource) != null;
-		}
-		return true;
-	}
-	
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.TeamSubscriber#isThreeWay()
-	 */
-	public boolean isThreeWay() {
-		return true;
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.TeamSubscriber#isCancellable()
-	 */
-	public boolean isCancellable() {
-		return false;
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.team.core.sync.TeamSubscriber#cancel()
-	 */
-	public void cancel() {
-		// noop
-	}
-}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSMergeSubscriber.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSMergeSubscriber.java
similarity index 97%
rename from bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSMergeSubscriber.java
rename to bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSMergeSubscriber.java
index 78074e0..87a607c 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSMergeSubscriber.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSMergeSubscriber.java
@@ -8,7 +8,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
-package org.eclipse.team.internal.ccvs.core;
+package org.eclipse.team.internal.ccvs.core.subscribers;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -34,6 +34,10 @@
 import org.eclipse.team.core.subscribers.TeamDelta;
 import org.eclipse.team.core.subscribers.TeamProvider;
 import org.eclipse.team.core.sync.IRemoteResource;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.internal.ccvs.core.CVSStatus;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
 import org.eclipse.team.internal.ccvs.core.syncinfo.RemoteSynchronizer;
 import org.eclipse.team.internal.ccvs.core.syncinfo.RemoteTagSynchronizer;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSynchronizer;
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSMergeSyncInfo.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSMergeSyncInfo.java
similarity index 97%
rename from bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSMergeSyncInfo.java
rename to bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSMergeSyncInfo.java
index d992055..049b544 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSMergeSyncInfo.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSMergeSyncInfo.java
@@ -4,7 +4,7 @@
  * To change the template for this generated file go to
  * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
  */
-package org.eclipse.team.internal.ccvs.core;
+package org.eclipse.team.internal.ccvs.core.subscribers;
 
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.IProgressMonitor;
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSRevisionNumberCompareCriteria.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSRevisionNumberCompareCriteria.java
similarity index 90%
rename from bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSRevisionNumberCompareCriteria.java
rename to bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSRevisionNumberCompareCriteria.java
index 63fbfb6..4c6d621 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSRevisionNumberCompareCriteria.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSRevisionNumberCompareCriteria.java
@@ -8,13 +8,17 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
-package org.eclipse.team.internal.ccvs.core;
+package org.eclipse.team.internal.ccvs.core.subscribers;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.team.core.subscribers.ComparisonCriteria;
 import org.eclipse.team.core.sync.IRemoteResource;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.internal.ccvs.core.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.ICVSRemoteFile;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
 
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSubscriberFactory.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSSubscriberFactory.java
similarity index 97%
rename from bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSubscriberFactory.java
rename to bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSSubscriberFactory.java
index 5c71d06..ed45620 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSubscriberFactory.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSSubscriberFactory.java
@@ -8,7 +8,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
-package org.eclipse.team.internal.ccvs.core;
+package org.eclipse.team.internal.ccvs.core.subscribers;
 
 import org.eclipse.core.runtime.QualifiedName;
 import org.eclipse.team.core.TeamException;
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncInfo.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSSyncInfo.java
similarity index 95%
rename from bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncInfo.java
rename to bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSSyncInfo.java
index fbbb503..c1c8132 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncInfo.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSSyncInfo.java
@@ -8,7 +8,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
-package org.eclipse.team.internal.ccvs.core;
+package org.eclipse.team.internal.ccvs.core.subscribers;
 
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IResource;
@@ -17,6 +17,14 @@
 import org.eclipse.team.core.subscribers.SyncInfo;
 import org.eclipse.team.core.subscribers.TeamSubscriber;
 import org.eclipse.team.core.sync.IRemoteResource;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.internal.ccvs.core.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder;
+import org.eclipse.team.internal.ccvs.core.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.ICVSResourceVisitor;
+import org.eclipse.team.internal.ccvs.core.Policy;
 import org.eclipse.team.internal.ccvs.core.client.Update;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
 import org.eclipse.team.internal.ccvs.core.resources.RemoteFolder;
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSSyncTreeSubscriber.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSSyncTreeSubscriber.java
new file mode 100644
index 0000000..2414bae
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSSyncTreeSubscriber.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.core.subscribers;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.subscribers.ComparisonCriteria;
+import org.eclipse.team.core.subscribers.ContentComparisonCriteria;
+import org.eclipse.team.core.subscribers.SyncInfo;
+import org.eclipse.team.core.subscribers.trees.TreeTeamSubscriber;
+import org.eclipse.team.core.sync.IRemoteResource;
+import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.internal.ccvs.core.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+import org.eclipse.team.internal.core.TeamPlugin;
+
+/**
+ * This class provides common funtionality for three way sychronizing
+ * for CVS.
+ */
+public abstract class CVSSyncTreeSubscriber extends TreeTeamSubscriber {
+	
+	CVSSyncTreeSubscriber(QualifiedName id, String name, String description) {
+		super(id, name, description);
+		initializeComparisonCriteria();
+	}
+
+	public IRemoteResource getRemoteResource(IResource resource) throws TeamException {
+		return getRemoteTree().getRemoteHandle(resource);
+	}
+
+	public IRemoteResource getBaseResource(IResource resource) throws TeamException {
+		return getBaseTree().getRemoteHandle(resource);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#getSyncInfo(org.eclipse.core.resources.IResource)
+	 */
+	public SyncInfo getSyncInfo(IResource resource, IProgressMonitor monitor) throws TeamException {
+		if (!isSupervised(resource)) return null;
+		IRemoteResource remoteResource = getRemoteResource(resource);
+		if(resource.getType() == IResource.FILE) {
+			IRemoteResource baseResource = getBaseResource(resource);
+			return getSyncInfo(resource, baseResource, remoteResource, monitor);
+		} else {
+			// In CVS, folders do not have a base. Hence, the remote is used as the base.
+			return getSyncInfo(resource, remoteResource, remoteResource, monitor);
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.ISyncTreeSubscriber#isSupervised(org.eclipse.core.resources.IResource)
+	 */
+	public boolean isSupervised(IResource resource) throws TeamException {
+		RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject(), CVSProviderPlugin.getTypeId());
+		if (provider == null) return false;
+		// TODO: what happens for resources that don't exist?
+		// TODO: is it proper to use ignored here?
+		ICVSResource cvsThing = CVSWorkspaceRoot.getCVSResourceFor(resource);
+		if (cvsThing.isIgnored()) {
+			// An ignored resource could have an incoming addition (conflict)
+			return getRemoteResource(resource) != null;
+		}
+		return true;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.TeamSubscriber#isThreeWay()
+	 */
+	public boolean isThreeWay() {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.core.sync.TeamSubscriber#isCancellable()
+	 */
+	public boolean isCancellable() {
+		return false;
+	}
+	
+	/**
+	 * Method invoked from the constructor to initialize the comparison criteria
+	 * and the default criteria.
+	 * This method can be overriden by subclasses.
+	 */
+	protected void initializeComparisonCriteria() {				
+		// setup comparison criteria
+		ComparisonCriteria revisionNumberComparator = new CVSRevisionNumberCompareCriteria();
+		ComparisonCriteria contentsComparator = new ContentComparisonCriteria(new ComparisonCriteria[] {revisionNumberComparator}, false /*consider whitespace */);
+		ComparisonCriteria contentsComparatorIgnoreWhitespace = new ContentComparisonCriteria(new ComparisonCriteria[] {revisionNumberComparator}, true /* ignore whitespace */);
+		
+		addComparisonCriteria(revisionNumberComparator);
+		addComparisonCriteria(contentsComparator);
+		addComparisonCriteria(contentsComparatorIgnoreWhitespace);
+		
+		try {
+			// default
+			setCurrentComparisonCriteria(revisionNumberComparator.getId());
+		} catch (TeamException e) {
+			TeamPlugin.log(e);			
+		}
+	}
+	
+	protected SyncInfo getSyncInfo(IResource local, IRemoteResource base, IRemoteResource remote, IProgressMonitor monitor) throws TeamException {
+		try {
+			monitor = Policy.monitorFor(monitor);
+			monitor.beginTask(null, 100);
+			return new CVSSyncInfo(local, base, remote, this, Policy.subMonitorFor(monitor, 100));
+		} finally {
+			monitor.done();
+		}
+	}
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSWorkspaceSubscriber.java
similarity index 93%
rename from bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java
rename to bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSWorkspaceSubscriber.java
index f7a88ac..dffbf9c 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/subscribers/CVSWorkspaceSubscriber.java
@@ -8,7 +8,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
-package org.eclipse.team.internal.ccvs.core;
+package org.eclipse.team.internal.ccvs.core.subscribers;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -24,6 +24,13 @@
 import org.eclipse.team.core.TeamException;
 import org.eclipse.team.core.subscribers.SyncInfo;
 import org.eclipse.team.core.subscribers.TeamDelta;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.internal.ccvs.core.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener;
+import org.eclipse.team.internal.ccvs.core.Policy;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
 import org.eclipse.team.internal.ccvs.core.syncinfo.OptimizedRemoteSynchronizer;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSynchronizer;
@@ -38,7 +45,7 @@
 	// qualified name for remote sync info
 	private static final String REMOTE_RESOURCE_KEY = "remote-resource-key"; //$NON-NLS-1$
 
-	CVSWorkspaceSubscriber(QualifiedName id, String name, String description) {
+	public CVSWorkspaceSubscriber(QualifiedName id, String name, String description) {
 		super(id, name, description);
 		
 		// install sync info participant
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteResourceFactory.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteResourceFactory.java
deleted file mode 100644
index 21518f8..0000000
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteResourceFactory.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials 
- * are made available under the terms of the Common Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- * 
- * Contributors:
- *     IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.team.internal.ccvs.core.syncinfo;
-
-/**
- * @author Administrator
- *
- * To change the template for this generated type comment go to
- * Window>Preferences>Java>Code Generation>Code and Comments
- */
-public class RemoteResourceFactory {
-
-}
diff --git a/bundles/org.eclipse.team.cvs.ui/plugin.xml b/bundles/org.eclipse.team.cvs.ui/plugin.xml
index 21856ef..ba89e15 100644
--- a/bundles/org.eclipse.team.cvs.ui/plugin.xml
+++ b/bundles/org.eclipse.team.cvs.ui/plugin.xml
@@ -561,7 +561,7 @@
    <extension
          point="org.eclipse.team.ui.subscriberMenus">
       <subscriberContribution
-            subscriberClass="org.eclipse.team.internal.ccvs.core.CVSWorkspaceSubscriber"
+            subscriberClass="org.eclipse.team.internal.ccvs.core.subscribers.CVSWorkspaceSubscriber"
             id="org.eclipse.team.ccvs.ui.CVSWorkspaceSubscriberContributions">
           <action
                label="%CVSWorkspaceSubscriber.update.label"
@@ -586,7 +586,7 @@
          </action>
       </subscriberContribution>
           <subscriberContribution
-            subscriberClass="org.eclipse.team.internal.ccvs.core.CVSMergeSubscriber"
+            subscriberClass="org.eclipse.team.internal.ccvs.core.subscribers.CVSMergeSubscriber"
             id="org.eclipse.team.ccvs.ui.CVSMergeSubscriberContributions">
           <action
                label="%CVSWorkspaceSubscriber.merge.label"
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/SyncAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/SyncAction.java
index 47ca97b..95151f9 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/SyncAction.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/SyncAction.java
@@ -16,8 +16,8 @@
 import org.eclipse.jface.action.IAction;
 import org.eclipse.team.internal.ccvs.core.CVSException;
 import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
-import org.eclipse.team.internal.ccvs.core.CVSWorkspaceSubscriber;
 import org.eclipse.team.internal.ccvs.core.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.subscribers.CVSWorkspaceSubscriber;
 import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
 import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants;
 import org.eclipse.team.internal.ccvs.ui.Policy;
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/merge/MergeWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/merge/MergeWizard.java
index aa8bc33..14eefaf 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/merge/MergeWizard.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/merge/MergeWizard.java
@@ -21,8 +21,8 @@
 import org.eclipse.jface.wizard.Wizard;
 import org.eclipse.team.core.TeamException;
 import org.eclipse.team.core.subscribers.TeamProvider;
-import org.eclipse.team.internal.ccvs.core.CVSMergeSubscriber;
 import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.core.subscribers.CVSMergeSubscriber;
 import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
 import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants;
 import org.eclipse.team.internal.ccvs.ui.Policy;
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSSubscriberAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSSubscriberAction.java
index 3bb8470..4189c74 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSSubscriberAction.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSSubscriberAction.java
@@ -25,11 +25,11 @@
 import org.eclipse.team.core.subscribers.SyncInfo;
 import org.eclipse.team.internal.ccvs.core.CVSException;
 import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
-import org.eclipse.team.internal.ccvs.core.CVSSyncInfo;
 import org.eclipse.team.internal.ccvs.core.ICVSResource;
 import org.eclipse.team.internal.ccvs.core.ICVSRunnable;
 import org.eclipse.team.internal.ccvs.core.client.PruneFolderVisitor;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+import org.eclipse.team.internal.ccvs.core.subscribers.CVSSyncInfo;
 import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
 import org.eclipse.team.internal.ccvs.ui.Policy;
 import org.eclipse.team.ui.sync.SubscriberAction;
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/MergeUpdateAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/MergeUpdateAction.java
index a6947c3..6abb677 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/MergeUpdateAction.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/MergeUpdateAction.java
@@ -23,13 +23,13 @@
 import org.eclipse.team.core.subscribers.TeamSubscriber;
 import org.eclipse.team.core.sync.IRemoteResource;
 import org.eclipse.team.internal.ccvs.core.CVSException;
-import org.eclipse.team.internal.ccvs.core.CVSMergeSubscriber;
-import org.eclipse.team.internal.ccvs.core.CVSSyncInfo;
 import org.eclipse.team.internal.ccvs.core.CVSTag;
 import org.eclipse.team.internal.ccvs.core.ICVSFolder;
 import org.eclipse.team.internal.ccvs.core.client.Command;
 import org.eclipse.team.internal.ccvs.core.client.Update;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+import org.eclipse.team.internal.ccvs.core.subscribers.CVSMergeSubscriber;
+import org.eclipse.team.internal.ccvs.core.subscribers.CVSSyncInfo;
 import org.eclipse.team.internal.ccvs.ui.Policy;
 import org.eclipse.team.ui.sync.OrSyncInfoFilter;
 import org.eclipse.team.ui.sync.SyncInfoDirectionFilter;
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/SubscriberConfirmMergedAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/SubscriberConfirmMergedAction.java
index 1f07239..6c2e534 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/SubscriberConfirmMergedAction.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/SubscriberConfirmMergedAction.java
@@ -17,7 +17,7 @@
 import org.eclipse.team.core.TeamException;
 import org.eclipse.team.core.subscribers.SyncInfo;
 import org.eclipse.team.internal.ccvs.core.CVSException;
-import org.eclipse.team.internal.ccvs.core.CVSSyncInfo;
+import org.eclipse.team.internal.ccvs.core.subscribers.CVSSyncInfo;
 import org.eclipse.team.internal.ccvs.ui.Policy;
 import org.eclipse.team.ui.sync.SyncInfoDirectionFilter;
 import org.eclipse.team.ui.sync.SyncInfoFilter;