Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean Michel-Lemieux2002-03-14 15:47:18 +0000
committerJean Michel-Lemieux2002-03-14 15:47:18 +0000
commitc4d1cf537641fbcc005227f24057407e3301b981 (patch)
tree35dfcea53ee948c8a2ee0908ac581836b96ea354
parent781539afec322e2a7b49577f0dcaedf2db509525 (diff)
downloadeclipse.platform.team-c4d1cf537641fbcc005227f24057407e3301b981.tar.gz
eclipse.platform.team-c4d1cf537641fbcc005227f24057407e3301b981.tar.xz
eclipse.platform.team-c4d1cf537641fbcc005227f24057407e3301b981.zip
Keyword substitution refactoring and support for programmatic configuration.
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java239
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/AbstractStructureVisitor.java3
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Command.java116
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Commit.java6
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/DiffStructureVisitor.java3
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/ImportStructureVisitor.java12
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java21
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/TagFileSender.java3
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/UpdatedHandler.java13
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/AdminKSubstListener.java94
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties23
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSRemoteSyncElement.java5
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ResourceSyncInfo.java1
13 files changed, 514 insertions, 25 deletions
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
index 056d43eb0..8ed13506d 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
@@ -5,9 +5,15 @@ package org.eclipse.team.ccvs.core;
* All Rights Reserved.
*/
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -41,15 +47,15 @@ import org.eclipse.team.internal.ccvs.core.client.ResponseHandler;
import org.eclipse.team.internal.ccvs.core.client.Session;
import org.eclipse.team.internal.ccvs.core.client.Tag;
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.client.Command.LocalOption;
+import org.eclipse.team.internal.ccvs.core.client.listeners.AdminKSubstListener;
import org.eclipse.team.internal.ccvs.core.client.listeners.DiffListener;
+import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener;
import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
import org.eclipse.team.internal.ccvs.core.resources.CVSRemoteSyncElement;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
-import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
-import org.eclipse.team.internal.ccvs.core.resources.RemoteFolder;
-import org.eclipse.team.internal.ccvs.core.resources.RemoteFolderTreeBuilder;
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;
@@ -84,6 +90,11 @@ import org.eclipse.team.internal.ccvs.core.util.Assert;
* have them appear in Eclipse. This may be changed in the future.
*/
public class CVSTeamProvider implements ITeamNature, ITeamProvider {
+ private static final int CR_BYTE = 0x0D;
+ private static final int LF_BYTE = 0x0A;
+ private static final boolean IS_CRLF_PLATFORM = Arrays.equals(
+ System.getProperty("line.separator").getBytes(),
+ new byte[] { CR_BYTE, LF_BYTE });
private CVSWorkspaceRoot workspaceRoot;
private IProject project;
@@ -968,4 +979,226 @@ public class CVSTeamProvider implements ITeamNature, ITeamProvider {
// and creating an array of IStatus!
return new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, getMessageFor(e), e);
}
+
+ /**
+ * Sets the keyword substitution mode for the specified resources.
+ * <p>
+ * Applies the following rules in order:<br>
+ * <ul>
+ * <li>If a file is not managed, skips it.</li>
+ * <li>If a file is not changing modes, skips it.</li>
+ * <li>If a file is being changed from binary to text, corrects line delimiters
+ * then commits it, then admins it.</li>
+ * <li>If a file is added, changes the resource sync information locally.</li>
+ * <li>Otherwise commits the file (with FORCE to create a new revision), then admins it.</li>
+ * </ul>
+ * All files that are admin'd are committed with FORCE to prevent other developers from
+ * casually trying to commit pending changes to the repository without first checking out
+ * a new copy. This is not a perfect solution, as they could just as easily do an UPDATE
+ * and not obtain the new keyword sync info.
+ * </p>
+ *
+ * @param resources the resources to set keyword substitution mode
+ * @param depth the recursion depth
+ * @param toKSubst the desired keyword substitution mode
+ * @param monitor the progress monitor
+ * @return a status code indicating success or failure of the operation
+ *
+ * @throws TeamException
+ */
+ public IStatus setKeywordSubstitution(final IResource[] resources, final int depth,
+ final KSubstOption toKSubst, IProgressMonitor monitor) throws TeamException {
+ final IStatus[] result = new IStatus[] { ICommandOutputListener.OK };
+ workspaceRoot.getLocalRoot().run(new ICVSRunnable() {
+ public void run(final IProgressMonitor monitor) throws CVSException {
+ final boolean toBinary = toKSubst.isBinary();
+ final List /* of String */ filesToAdmin = new ArrayList();
+ final List /* of String */ filesToCommit = new ArrayList();
+ final Collection /* of ICVSFile */ filesToCommitAsText = new HashSet(); // need fast lookup
+
+ final IProgressMonitor progress = Policy.monitorFor(monitor);
+ progress.beginTask(Policy.bind("CVSTeamProvider.preparingToSetKSubst"), 100);
+ try {
+ /*** get all possibly affected files (ensure no duplicates) ***/
+ final Set /* of IFile */ files = new HashSet();
+ for (int i = 0; i < resources.length; i++) {
+ final IResource currentResource = resources[i];
+ // throw an exception if the resource is not a child of the receiver
+ checkIsChild(currentResource);
+ try {
+ currentResource.accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) throws CoreException {
+ if (resource.getType() == IResource.FILE) {
+ files.add(resource);
+ }
+ // always return true and let the depth determine if children are visited
+ return true;
+ }
+ }, depth, false);
+ } catch (CoreException e) {
+ throw new CVSException(new Status(IStatus.ERROR, CVSProviderPlugin.ID,
+ TeamException.UNABLE, Policy.bind("CVSTeamProvider.visitError", //$NON-NLS-1$
+ new Object[] { currentResource.getFullPath() }), e));
+ }
+ }
+ progress.worked(5);
+
+ /*** determine the resources to be committed and those to be admin'd ***/
+ for (Iterator it = files.iterator(); it.hasNext();) {
+ IFile file = (IFile) it.next();
+ ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor(file);
+ // only set keyword substitution if resource is a managed file
+ if (mFile.isManaged()) {
+ ResourceSyncInfo info = mFile.getSyncInfo();
+ String fromMode = info.getKeywordMode();
+ KSubstOption fromKSubst = KSubstOption.fromMode(fromMode);
+
+ // make sure we commit all added or changed resources
+ String remotePath = mFile.getRelativePath(workspaceRoot.getLocalRoot());
+
+ // check if mode must be changed
+ if (! toKSubst.equals(fromKSubst)) {
+ if (info.isAdded()) {
+ // change resource sync info for outgoing addition
+ ResourceSyncInfo newInfo = new ResourceSyncInfo(
+ info.getName(), info.getRevision(), info.getTimeStamp(), toKSubst.toMode(),
+ info.getTag(), info.getPermissions());
+ mFile.setSyncInfo(newInfo);
+ } else if (info.isDeleted()) {
+ // ignore deletions
+ } else {
+ // file exists remotely
+ boolean fromBinary = fromKSubst.isBinary();
+ if (fromBinary && ! toBinary) {
+ // converting from binary to text
+ cleanLineDelimiters(file, IS_CRLF_PLATFORM, progress);
+ // remember to commit the cleaned resource as text before admin
+ filesToCommitAsText.add(mFile);
+ } else {
+ // force a commit to bump the revision number
+ makeDirty(file);
+ }
+ // remember to commit and admin the resource
+ filesToCommit.add(remotePath); // FORCE creation of a new revision
+ filesToAdmin.add(remotePath);
+ }
+ }
+ }
+ }
+ progress.worked(5);
+
+ /*** commit then admin the resources ***/
+ if (filesToAdmin.size() != 0 || filesToCommit.size() != 0) {
+ Session s = new Session(workspaceRoot.getRemoteLocation(),
+ workspaceRoot.getLocalRoot(), false);
+ IProgressMonitor sessionProgress = Policy.subMonitorFor(progress, 90);
+ sessionProgress.beginTask(Policy.bind("CVSTeamProvider.settingKSubst"), 5 +
+ filesToAdmin.size() + filesToCommit.size());
+ try {
+ s.open(Policy.subMonitorFor(sessionProgress, 5));
+
+ // commit files that changed from binary to text
+ // NOTE: The files are committed as text with conversions even if the
+ // resource sync info still says "binary".
+ if (filesToCommit.size() != 0) {
+ s.setTextTransferOverride(filesToCommitAsText);
+ result[0] = Command.COMMIT.execute(s, Command.NO_GLOBAL_OPTIONS,
+ new LocalOption[] { Commit.DO_NOT_RECURSE, Commit.FORCE,
+ Commit.makeArgumentOption(Command.MESSAGE_OPTION, comment) },
+ (String[]) filesToCommit.toArray(new String[filesToCommit.size()]),
+ null, Policy.subMonitorFor(sessionProgress, filesToCommit.size()));
+ s.setTextTransferOverride(null);
+ // if errors were encountered, abort
+ if (! result[0].isOK()) return;
+ }
+
+ // admin files that changed keyword substitution mode
+ // NOTE: As confirmation of the completion of a command, the server replies
+ // with the RCS command output if a change took place. Rather than
+ // assume that the command succeeded, we listen for these lines
+ // and update the local ResourceSyncInfo for the particular files that
+ // were actually changed remotely.
+ result[0] = Command.ADMIN.execute(s, Command.NO_GLOBAL_OPTIONS,
+ new LocalOption[] { toKSubst },
+ (String[]) filesToAdmin.toArray(new String[filesToAdmin.size()]),
+ new AdminKSubstListener(toKSubst.toMode()),
+ Policy.subMonitorFor(sessionProgress, filesToAdmin.size()));
+ } finally {
+ s.close();
+ sessionProgress.done();
+ }
+ }
+ } finally {
+ progress.done();
+ }
+ }
+ }, monitor);
+ return result[0];
+ }
+
+ /**
+ * Fixes the line delimiters in the local file to reflect the platform's
+ * native encoding. Performs CR/LF -> LF or LF -> CR/LF conversion
+ * depending on the platform but does not affect delimiters that are
+ * already correctly encoded.
+ */
+ public static void cleanLineDelimiters(IFile file, boolean useCRLF, IProgressMonitor progress)
+ throws CVSException {
+ try {
+ // convert delimiters in memory
+ boolean changed = false;
+ InputStream is = null;
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try {
+ is = new BufferedInputStream(file.getContents());
+ boolean seenCR = false;
+ int c;
+ while ((c = is.read()) != -1) {
+ if (c == LF_BYTE) {
+ if (useCRLF) {
+ bos.write(CR_BYTE);
+ if (! seenCR) changed = true; // added CR
+ } else {
+ if (seenCR) changed = true; // stripped CR
+ }
+ bos.write(LF_BYTE);
+ seenCR = false;
+ } else {
+ if (seenCR) {
+ bos.write(CR_BYTE); // preserve orphaned carriage returns
+ seenCR = false;
+ }
+ if (c == CR_BYTE) {
+ seenCR = true;
+ } else {
+ bos.write(c);
+ }
+ }
+ }
+ if (seenCR) {
+ bos.write(CR_BYTE); // preserve orphaned carriage returns
+ }
+ } finally {
+ if (is != null) is.close();
+ bos.close();
+ }
+ if (changed) {
+ // write file back to disk with corrected delimiters if changes were made
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ file.setContents(bis, false /*force*/, true /*keepHistory*/, progress);
+ } else {
+ // otherwise make the file dirty
+ makeDirty(file);
+ }
+ } catch (CoreException e) {
+ throw CVSException.wrapException(file, Policy.bind("CVSTeamProvider.cleanLineDelimitersException"), e);
+ } catch (IOException e) {
+ throw CVSException.wrapException(file, Policy.bind("CVSTeamProvider.cleanLineDelimitersException"), e);
+ }
+ }
+
+ private static void makeDirty(IFile file) throws CVSException {
+ ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor(file);
+ mFile.setTimeStamp(null);
+ }
} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/AbstractStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/AbstractStructureVisitor.java
index 58429dd3b..43c02c6d5 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/AbstractStructureVisitor.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/AbstractStructureVisitor.java
@@ -18,6 +18,7 @@ import org.eclipse.team.ccvs.core.ICVSResource;
import org.eclipse.team.ccvs.core.ICVSResourceVisitor;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
import org.eclipse.team.internal.ccvs.core.resources.CVSEntryLineTag;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
@@ -149,7 +150,7 @@ abstract class AbstractStructureVisitor implements ICVSResourceVisitor {
// If the file exists, send the appropriate indication to the server
if (mFile.exists()) {
if (mFile.isModified()) {
- boolean binary = (info != null) && ResourceSyncInfo.BINARY_TAG.equals(mFile.getSyncInfo().getKeywordMode());
+ boolean binary = info != null && KSubstOption.fromMode(info.getKeywordMode()).isBinary();
if (sendModifiedContents) {
session.sendModified(mFile, binary, monitor);
} else {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Command.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Command.java
index 2b2005fd2..5a23711dc 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Command.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Command.java
@@ -7,9 +7,11 @@ package org.eclipse.team.internal.ccvs.core.client;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Vector;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -22,6 +24,7 @@ import org.eclipse.team.ccvs.core.ICVSFolder;
import org.eclipse.team.ccvs.core.ICVSResource;
import org.eclipse.team.ccvs.core.ICVSRunnable;
import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSProvider;
import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener;
@@ -81,13 +84,21 @@ public abstract class Command {
public static final LocalOption[] NO_LOCAL_OPTIONS = new LocalOption[0];
// valid for: annotate checkout commit diff export log rdiff remove rtag status tag update
public static final LocalOption DO_NOT_RECURSE = new LocalOption("-l"); //$NON-NLS-1$
- // valid for: add checkout export import update
- public static final LocalOption KSUBST_BINARY = new LocalOption("-kb"); //$NON-NLS-1$
// valid for: checkout export update
public static final LocalOption PRUNE_EMPTY_DIRECTORIES = new LocalOption("-P"); //$NON-NLS-1$
// valid for: checkout export update
public static final LocalOption MESSAGE_OPTION = new LocalOption("-m"); //$NON-NLS-1$
+ /*** Local options: keyword substitution mode ***/
+ // valid for: add admin checkout export import update
+ private static final Map ksubstOptionMap = new HashMap();
+ public static final KSubstOption KSUBST_BINARY = new KSubstOption("-kb"); //$NON-NLS-1$
+ public static final KSubstOption KSUBST_TEXT = new KSubstOption("-ko"); //$NON-NLS-1$
+ public static final KSubstOption KSUBST_TEXT_EXPAND = new KSubstOption("-kkv"); //$NON-NLS-1$
+ public static final KSubstOption KSUBST_TEXT_EXPAND_LOCKER = new KSubstOption("-kkvl"); //$NON-NLS-1$
+ public static final KSubstOption KSUBST_TEXT_VALUES_ONLY = new KSubstOption("-kv"); //$NON-NLS-1$
+ public static final KSubstOption KSUBST_TEXT_KEYWORDS_ONLY = new KSubstOption("-kk"); //$NON-NLS-1$
+
/*** Response handler map ***/
private static final Hashtable responseHandlers = new Hashtable();
static {
@@ -541,14 +552,36 @@ public abstract class Command {
/**
* Returns the option part of the option
*/
- public String getOption() {
+ String getOption() {
return option;
}
/**
+ * Compares two options for equality.
+ * @param other the other option
+ */
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if (other instanceof Option) {
+ Option otherOption = (Option) other;
+ return option.equals(otherOption.option);
+ }
+ return false;
+ }
+ /**
* Sends the option to a CVS server
* @param session the CVS session
*/
public abstract void send(Session session) throws CVSException;
+ /*
+ * To make debugging a tad easier.
+ */
+ public String toString() {
+ if (argument != null && argument.length() != 0) {
+ return option + " " + argument;
+ } else {
+ return option;
+ }
+ }
}
/**
* Option subtype for global options that are common to all commands.
@@ -587,6 +620,83 @@ public abstract class Command {
if (argument != null) session.sendArgument(argument);
}
}
+ /**
+ * Options subtype for keyword substitution options.
+ */
+ public static class KSubstOption extends LocalOption {
+ private String shortDisplayText;
+ private String longDisplayText;
+
+ private KSubstOption(String option) {
+ this(option, Policy.bind("KSubstOption." + option + ".short"),
+ Policy.bind("KSubstOption." + option + ".long"));
+ }
+ private KSubstOption(String option, String shortDisplayText, String longDisplayText) {
+ super(option);
+ this.shortDisplayText = shortDisplayText;
+ this.longDisplayText = longDisplayText;
+ ksubstOptionMap.put(option, this);
+ }
+ /**
+ * Gets the KSubstOption instance for the specified mode.
+ *
+ * @param mode the mode, e.g. -kb
+ * @return an instance for that mode
+ */
+ public static KSubstOption fromMode(String mode) {
+ if (mode.length() == 0) mode = "-kkv"; // use default
+ KSubstOption option = (KSubstOption) ksubstOptionMap.get(mode);
+ if (option == null) {
+ option = new KSubstOption(mode,
+ Policy.bind("KSubstOption.unknown.short", mode),
+ Policy.bind("KSubstOption.unknown.long", mode));
+ ksubstOptionMap.put(mode, option);
+ }
+ return option;
+ }
+ /**
+ * Gets the KSubstOption instance for the specified file by pattern.
+ *
+ * @param filename the filename of interest
+ * @return an instance for that mode
+ */
+ public static KSubstOption fromPattern(String filename) {
+ if (CVSProvider.isText(filename)) return KSUBST_TEXT_EXPAND; // XXX should this be KSUBST_TEXT?
+ return KSUBST_BINARY;
+ }
+ /**
+ * Returns an array of all valid modes.
+ */
+ public static KSubstOption[] getAllKSubstOptions() {
+ return (KSubstOption[]) ksubstOptionMap.values().toArray(new KSubstOption[ksubstOptionMap.size()]);
+ }
+ /**
+ * Returns the entry line mode string for this instance.
+ */
+ public String toMode() {
+ if (KSUBST_TEXT_EXPAND.equals(this)) return "";
+ return getOption();
+ }
+ /**
+ * Returns true if the substitution mode requires no data translation
+ * during file transfer.
+ */
+ public boolean isBinary() {
+ return KSUBST_BINARY.equals(this);
+ }
+ /**
+ * Returns a short localized text string describing this mode.
+ */
+ public String getShortDisplayText() {
+ return shortDisplayText;
+ }
+ /**
+ * Returns a long localized text string describing this mode.
+ */
+ public String getLongDisplayText() {
+ return longDisplayText;
+ }
+ }
/**
* Makes a -m log message option.
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Commit.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Commit.java
index 6545fe591..e1e59dc44 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Commit.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Commit.java
@@ -14,6 +14,12 @@ import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
public class Commit extends Command {
/*** Local options: specific to commit ***/
+ // Forces a file to be committed even if it has not been modified; implies -l.
+ // NOTE: This option is not fully supported -- a file will not be sent
+ // unless it is dirty. The primary use is to resend a file that may
+ // or may not be changed (e.g. could depend on CR/LF translations, etc...)
+ // and force the server to create a new revision and reply Checked-in.
+ public static final LocalOption FORCE = new LocalOption("-f");
protected Commit() { }
protected String getCommandId() {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/DiffStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/DiffStructureVisitor.java
index 524f317e9..15a9a1251 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/DiffStructureVisitor.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/DiffStructureVisitor.java
@@ -6,6 +6,7 @@ package org.eclipse.team.internal.ccvs.core.client;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.team.ccvs.core.ICVSFile;
import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
/**
@@ -24,7 +25,7 @@ class DiffStructureVisitor extends FileStructureVisitor {
* Send unmanaged files as modified with a default entry line.
*/
protected void sendFile(ICVSFile mFile, String mode) throws CVSException {
- boolean binary = mode != null && mode.indexOf(ResourceSyncInfo.BINARY_TAG) != -1;
+ boolean binary = mode != null && KSubstOption.fromMode(mode).isBinary();
boolean newFile = false;
if (mFile.isManaged()) {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/ImportStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/ImportStructureVisitor.java
index 702dc336b..3aece0bdd 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/ImportStructureVisitor.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/ImportStructureVisitor.java
@@ -14,7 +14,7 @@ import org.eclipse.team.ccvs.core.ICVSFolder;
import org.eclipse.team.ccvs.core.ICVSResourceVisitor;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.Policy;
-import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
import org.eclipse.team.internal.ccvs.core.util.FileNameMatcher;
/**
@@ -105,14 +105,12 @@ class ImportStructureVisitor implements ICVSResourceVisitor {
return;
}
- String mode;
+ // XXX should we default to text or to binary?
+ boolean binary = false;
if (wrapMatcher != null) {
- mode = wrapMatcher.getMatch(mFile.getName());
- } else {
- mode = ""; //$NON-NLS-1$
+ String mode = wrapMatcher.getMatch(mFile.getName());
+ if (mode != null) binary = KSubstOption.fromMode(mode).isBinary();
}
- // XXX Is this condition right?
- boolean binary = mode != null && mode.indexOf(ResourceSyncInfo.BINARY_TAG) != -1;
session.sendModified(mFile, binary, monitor);
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java
index bdd85698a..b492a3cfd 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java
@@ -10,6 +10,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Date;
import java.util.List;
@@ -78,6 +79,7 @@ public class Session {
private Date modTime = null;
private boolean noLocalChanges = false;
private List expansions;
+ private Collection /* of ICVSFile */ textTransferOverrideSet = null;
// a shared buffer used for file transfers
private byte[] transferBuffer = null;
@@ -510,6 +512,10 @@ public class Session {
*/
public void sendFile(ICVSFile file, boolean isBinary, IProgressMonitor monitor)
throws CVSException {
+ // check overrides
+ if (textTransferOverrideSet != null &&
+ textTransferOverrideSet.contains(file)) isBinary = false;
+
// update progress monitor
String title = Policy.bind(getSendFileTitleKey(), new Object[]{ Util.toTruncatedPath(file, localRoot, 3) }); //$NON-NLS-1$
monitor.subTask(Policy.bind("Session.transferNoSize", title)); //$NON-NLS-1$
@@ -605,6 +611,10 @@ public class Session {
*/
public void receiveFile(ICVSFile file, boolean isBinary, int responseType, IProgressMonitor monitor)
throws CVSException {
+ // check overrides
+ if (textTransferOverrideSet != null &&
+ textTransferOverrideSet.contains(file)) isBinary = false;
+
// update progress monitor
String title = Policy.bind("Session.receiving", new Object[]{ Util.toTruncatedPath(file, localRoot, 3) }); //$NON-NLS-1$
monitor.subTask(Policy.bind("Session.transferNoSize", title)); //$NON-NLS-1$
@@ -780,5 +790,14 @@ public class Session {
public void setSendFileTitleKey(String sendFileTitleKey) {
this.sendFileTitleKey = sendFileTitleKey;
}
-
+
+ /**
+ * Remembers a set of files that must be transferred as 'text'
+ * regardless of what the isBinary parameter to sendFile() is.
+ *
+ * @param textTransferOverrideSet the set of ICVSFiles to override, or null if none
+ */
+ public void setTextTransferOverride(Collection textTransferOverrideSet) {
+ this.textTransferOverrideSet = textTransferOverrideSet;
+ }
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/TagFileSender.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/TagFileSender.java
index 97137fada..f6a131e52 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/TagFileSender.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/TagFileSender.java
@@ -8,6 +8,7 @@ package org.eclipse.team.internal.ccvs.core.client;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.team.ccvs.core.ICVSFile;
import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
/**
@@ -35,7 +36,7 @@ class TagFileSender extends FileStructureVisitor {
}
if (! info.isAdded()) {
session.sendEntry(info.getEntryLine(false, mFile.getTimeStamp()));
- boolean binary = (info != null) && ResourceSyncInfo.BINARY_TAG.equals(mFile.getSyncInfo().getKeywordMode());
+ boolean binary = info != null && KSubstOption.fromMode(info.getKeywordMode()).isBinary();
session.sendIsModified(mFile, binary, monitor);
}
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/UpdatedHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/UpdatedHandler.java
index ef4b4fb7c..320b18a50 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/UpdatedHandler.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/UpdatedHandler.java
@@ -11,6 +11,7 @@ import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.team.ccvs.core.ICVSFile;
import org.eclipse.team.ccvs.core.ICVSFolder;
import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
import org.eclipse.team.internal.ccvs.core.util.Assert;
import org.eclipse.team.internal.ccvs.core.util.EntryFileDateFormat;
@@ -71,6 +72,7 @@ class UpdatedHandler extends ResponseHandler {
String repositoryFile = session.readLine();
String entryLine = session.readLine();
String permissionsLine = session.readLine();
+ ResourceSyncInfo info = new ResourceSyncInfo(entryLine, permissionsLine, null);
// clear file update modifiers
Date modTime = session.getModTime();
@@ -83,8 +85,8 @@ class UpdatedHandler extends ResponseHandler {
Assert.isTrue(mParent.exists());
ICVSFile mFile = mParent.getFile(fileName);
- boolean binary = entryLine.indexOf("/" + ResourceSyncInfo.BINARY_TAG) != -1; //$NON-NLS-1$
- boolean readOnly = permissionsLine.indexOf(READ_ONLY_FLAG) == -1;
+ boolean binary = KSubstOption.fromMode(info.getKeywordMode()).isBinary();
+ boolean readOnly = info.getPermissions().indexOf(READ_ONLY_FLAG) == -1;
session.receiveFile(mFile, binary, handlerType, monitor);
if (readOnly) mFile.setReadOnly(true);
@@ -101,14 +103,15 @@ class UpdatedHandler extends ResponseHandler {
// having outgoing changes.
// The purpose for having the two different timestamp options for merges is to
// dissallow commit of files that have conflicts until they have been manually edited.
- if(entryLine.indexOf(ResourceSyncInfo.MERGE_UNMODIFIED) != -1) {
+ if(info.getTimeStamp().indexOf(ResourceSyncInfo.MERGE_UNMODIFIED) != -1) {
timestamp = ResourceSyncInfo.RESULT_OF_MERGE_CONFLICT + mFile.getTimeStamp();
} else {
timestamp = ResourceSyncInfo.RESULT_OF_MERGE;
}
} else {
timestamp = mFile.getTimeStamp();
- }
- mFile.setSyncInfo(new ResourceSyncInfo(entryLine, permissionsLine, timestamp));
+ }
+ mFile.setSyncInfo(new ResourceSyncInfo(info.getName(), info.getRevision(),
+ timestamp, info.getKeywordMode(), info.getTag(), info.getPermissions()));
}
} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/AdminKSubstListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/AdminKSubstListener.java
new file mode 100644
index 000000000..e69847c80
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/AdminKSubstListener.java
@@ -0,0 +1,94 @@
+package org.eclipse.team.internal.ccvs.core.client.listeners;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.team.ccvs.core.CVSStatus;
+import org.eclipse.team.ccvs.core.ICVSFile;
+import org.eclipse.team.ccvs.core.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
+import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
+
+/**
+ * Used with 'admin -ksubst' to capture lines of text that are issued
+ * as confirmation that the remote keyword substitution mode has been
+ * changed. When encountered, updates the local ResourceSyncInfo for
+ * the file in question to reflect
+ *
+ * e.g.
+ * RCS file: path/filename,v
+ * done
+ */
+public class AdminKSubstListener implements ICommandOutputListener {
+ private String ksubstMode;
+
+ public AdminKSubstListener(String ksubstMode) {
+ this.ksubstMode = ksubstMode;
+ }
+
+ public IStatus messageLine(String line, ICVSFolder commandRoot,
+ IProgressMonitor monitor) {
+ if (line.startsWith("RCS file:")) { // $NON-NLS-1$
+ String rcsFile = line.substring(10).trim();
+ if (! rcsFile.endsWith(",v")) {
+ return new CVSStatus(CVSStatus.ERROR,
+ Policy.bind("AdminKSubstListener.expectedRCSFile", rcsFile));
+ }
+ String remoteRootLocation = null;
+ try {
+ FolderSyncInfo info = commandRoot.getFolderSyncInfo();
+ remoteRootLocation = info.getRemoteLocation();
+ } catch (CVSException e) {
+ // XXX bad eating of exception
+ }
+ if (remoteRootLocation == null) {
+ return new CVSStatus(CVSStatus.ERROR,
+ Policy.bind("AdminKSubstListener.commandRootNotManaged"));
+ }
+ IPath rcsFilePath = new Path(rcsFile.substring(0, rcsFile.length() - 2));
+ IPath remoteRootPath = new Path(remoteRootLocation);
+ if (! remoteRootPath.isPrefixOf(rcsFilePath)) {
+ return new CVSStatus(CVSStatus.ERROR,
+ Policy.bind("AdminKSubstListener.expectedChildOfCommandRoot",
+ rcsFilePath.toString(), remoteRootPath.toString()));
+ }
+ rcsFilePath = rcsFilePath.removeFirstSegments(remoteRootPath.segmentCount());
+ try {
+ ICVSFile file = commandRoot.getFile(rcsFilePath.toString());
+ ResourceSyncInfo info = file.getSyncInfo();
+ if (info != null) {
+ // only update sync info if we have it locally
+ ResourceSyncInfo newInfo = new ResourceSyncInfo(
+ info.getName(), info.getRevision(), info.getTimeStamp(),
+ ksubstMode, info.getTag(), info.getPermissions());
+ file.setSyncInfo(newInfo);
+ }
+ } catch (CVSException e) {
+ return new CVSStatus(CVSStatus.ERROR,
+ Policy.bind("AdminKSubstListener.couldNotSetResourceSyncInfo",
+ rcsFilePath.toString(), e.toString()));
+ }
+ }
+ return OK;
+ }
+
+ public IStatus errorLine(String line, ICVSFolder commandRoot,
+ IProgressMonitor monitor) {
+ // we don't expect to see anything on stderr if the command succeeds
+ // possible errors include:
+ // cvs server: cannot open /repo/a.txt,v: Permission denied
+ // cvs server: failed to create lock directory for `/repo/folder' (/repo/folder/#cvs.lock): Permission denied
+ // cvs server: failed to remove lock /repo/folder/#cvs.wfl.fiji.4442: Permission denied
+ // cvs server: lock failed - giving up
+ // cvs [server aborted]: lock failed - giving up
+ return new CVSStatus(CVSStatus.ERROR, CVSStatus.ERROR_LINE, line);
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
index 124a71b91..d5b23d352 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
@@ -68,6 +68,9 @@ CVSTeamProvider.updatingFolder=Updating {0}
CVSTeamProvider.scrubbingResource=Scrubbing {0}
CVSTeamProvider.updatingFile=Updating {0}
CVSTeamProvider.makeBranch=Creating branch
+CVSTeamProvider.preparingToSetKSubst=Preparing to set keyword substitution mode
+CVSTeamProvider.settingKSubst=Setting keyword substitution mode
+CVSTeamProvider.cleanLineDelimitersException=Exception occurred while cleaning line delimiters
ProjectDescriptionManager.unableToSetDescription=An error occured setting the project description
ProjectDescriptionManager.unableToReadDescription=An error occured reading the project description
@@ -138,6 +141,26 @@ Updated.numberFormat=Server did not send length of the file
UnsupportedHandler.message=Unsupported response received from server
RemovedHandler.invalid=Invalid removed response received from CVS server for {0}
+KSubstOption.-kb.short=Binary
+KSubstOption.-kb.long=Binary (-kb)
+KSubstOption.-ko.short=Text
+KSubstOption.-ko.long=Text (-ko)
+KSubstOption.-kkv.short=Text -kkv
+KSubstOption.-kkv.long=Text with keyword expansion (-kkv)
+KSubstOption.-kkvl.short=Text -kkvl
+KSubstOption.-kkvl.long=Text with keyword expansion and locker (-kkvl)
+KSubstOption.-kv.short=Text -kv
+KSubstOption.-kv.long=Text with keyword replacement (-kv)
+KSubstOption.-kk.short=Text -kk
+KSubstOption.-kk.long=Text with keyword compression (-kk)
+KSubstOption.unknown.short=Unknown {0}
+KSubstOption.unknown.long=Unknown ({0})
+
+AdminKSubstListener.expectedRCSFile=Expected RCS file {0} to end in ',v'
+AdminKSubstListener.commandRootNotManaged=Local root for this command is not managed
+AdminKSubstListener.expectedChildOfCommandRoot=Expected RCS file {0} to be a child of remote root for this command {1}
+AdminKSubstListener.couldNotSetResourceSyncInfo=Could not set resource sync info for {0}: {1}
+
CVSRepositoryLocation.nullLocation=Location must not be null
CVSRepositoryLocation.emptyLocation=Location must not be empty
CVSRepositoryLocation.endWhitespace=Location must not end with whitespace
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSRemoteSyncElement.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSRemoteSyncElement.java
index 8708710fd..26f1d17a1 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSRemoteSyncElement.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSRemoteSyncElement.java
@@ -18,9 +18,9 @@ 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.CVSProvider;
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;
@@ -159,8 +159,9 @@ public class CVSRemoteSyncElement extends RemoteSyncElement {
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,
- CVSProvider.isText(local.getName()) ? ResourceSyncInfo.USE_SERVER_MODE:ResourceSyncInfo.BINARY_TAG, local.getParent().getFolderSyncInfo().getTag(), null);
+ mode, local.getParent().getFolderSyncInfo().getTag(), null);
revision = info.getRevision();
} else {
info = remote.getSyncInfo();
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ResourceSyncInfo.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ResourceSyncInfo.java
index 133330b9e..02a758c57 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ResourceSyncInfo.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ResourceSyncInfo.java
@@ -31,7 +31,6 @@ public class ResourceSyncInfo {
// utility constants
private static final String DIRECTORY_PREFIX = "D/"; //$NON-NLS-1$
- public static final String BINARY_TAG = "-kb"; //$NON-NLS-1$
public static final String USE_SERVER_MODE = ""; //$NON-NLS-1$
private static final String SEPERATOR = "/"; //$NON-NLS-1$

Back to the top