Bug 426279 - Clean up sync flags and use them during synchronization
Change-Id: I90fa33a25a2317522a0b5538d0854f1e04fb2159
Signed-off-by: John Eblen <jeblen@acm.org>
diff --git a/rdt/org.eclipse.ptp.rdt.sync.cdt.core/src/org/eclipse/ptp/internal/rdt/sync/cdt/core/SyncGCCBuildCommandParser.java b/rdt/org.eclipse.ptp.rdt.sync.cdt.core/src/org/eclipse/ptp/internal/rdt/sync/cdt/core/SyncGCCBuildCommandParser.java
index a622644..bd7b07d 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.cdt.core/src/org/eclipse/ptp/internal/rdt/sync/cdt/core/SyncGCCBuildCommandParser.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.cdt.core/src/org/eclipse/ptp/internal/rdt/sync/cdt/core/SyncGCCBuildCommandParser.java
@@ -17,6 +17,7 @@
import org.eclipse.cdt.core.settings.model.CIncludePathEntry;
import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
+import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
diff --git a/rdt/org.eclipse.ptp.rdt.sync.cdt.core/src/org/eclipse/ptp/internal/rdt/sync/cdt/core/remotemake/SyncCommandLauncher.java b/rdt/org.eclipse.ptp.rdt.sync.cdt.core/src/org/eclipse/ptp/internal/rdt/sync/cdt/core/remotemake/SyncCommandLauncher.java
index f7dd928..7add4d4 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.cdt.core/src/org/eclipse/ptp/internal/rdt/sync/cdt/core/remotemake/SyncCommandLauncher.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.cdt.core/src/org/eclipse/ptp/internal/rdt/sync/cdt/core/remotemake/SyncCommandLauncher.java
@@ -386,15 +386,7 @@
private void syncOnPostBuild(IProgressMonitor monitor) throws CoreException {
SyncConfig config = SyncConfigManager.getActive(getProject());
if (shouldSyncAfterRun && SyncManager.getSyncAuto() && config.isSyncOnPostBuild()) {
- switch (SyncManager.getSyncMode(getProject())) {
- case ACTIVE:
- SyncManager.syncBlocking(null, getProject(), SyncFlag.FORCE, monitor, null);
- break;
-
- case ALL:
- SyncManager.syncAllBlocking(null, getProject(), SyncFlag.FORCE, null);
- break;
- }
+ SyncManager.syncBlocking(null, getProject(), SyncFlag.RL_ONLY, monitor, null);
}
}
@@ -403,11 +395,11 @@
if (shouldSyncBeforeRun && config.isSyncOnPreBuild()) {
switch (SyncManager.getSyncMode(getProject())) {
case ACTIVE:
- SyncManager.syncBlocking(null, getProject(), SyncFlag.FORCE, monitor, null);
+ SyncManager.syncBlocking(null, getProject(), SyncFlag.LR_ONLY, monitor, null);
break;
case ALL:
- SyncManager.syncAllBlocking(null, getProject(), SyncFlag.FORCE, null);
+ SyncManager.syncAllBlocking(null, getProject(), SyncFlag.LR_ONLY, null);
break;
}
}
diff --git a/rdt/org.eclipse.ptp.rdt.sync.cdt.ui/src/org/eclipse/ptp/internal/rdt/sync/cdt/ui/wizards/NewRemoteSyncProjectWizard.java b/rdt/org.eclipse.ptp.rdt.sync.cdt.ui/src/org/eclipse/ptp/internal/rdt/sync/cdt/ui/wizards/NewRemoteSyncProjectWizard.java
index 206960a..99c6754 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.cdt.ui/src/org/eclipse/ptp/internal/rdt/sync/cdt/ui/wizards/NewRemoteSyncProjectWizard.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.cdt.ui/src/org/eclipse/ptp/internal/rdt/sync/cdt/ui/wizards/NewRemoteSyncProjectWizard.java
@@ -121,7 +121,7 @@
// Force an initial sync
if (success && project != null) {
try {
- SyncManager.sync(null, project, SyncFlag.FORCE, new CommonSyncExceptionHandler(false, true));
+ SyncManager.sync(null, project, SyncFlag.BOTH, new CommonSyncExceptionHandler(false, true));
} catch (CoreException e) {
// This should never happen because only a blocking sync can throw a core exception.
Activator.log(Messages.NewRemoteSyncProjectWizard_0, e);
diff --git a/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/internal/rdt/sync/core/SynchronizedResource.java b/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/internal/rdt/sync/core/SynchronizedResource.java
index af65629..2f1756a 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/internal/rdt/sync/core/SynchronizedResource.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/internal/rdt/sync/core/SynchronizedResource.java
@@ -44,7 +44,7 @@
*/
@Override
public void refresh(IProgressMonitor monitor) throws CoreException {
- SyncManager.syncBlocking(null, fResource.getProject(), SyncFlag.FORCE, monitor, null);
+ SyncManager.syncBlocking(null, fResource.getProject(), SyncFlag.BOTH, monitor, null);
}
/*
diff --git a/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/SyncFlag.java b/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/SyncFlag.java
index 98621f5..209fd59 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/SyncFlag.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/SyncFlag.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011 Oak Ridge National Laboratory and others.
+ * Copyright (c) 2014 Oak Ridge National Laboratory and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -10,33 +10,43 @@
*******************************************************************************/
package org.eclipse.ptp.rdt.sync.core;
+import java.util.Collections;
import java.util.EnumSet;
+import java.util.Set;
/**
- * Flags to control the behavior of synchronization. Currently, the individual FORCE_SYNC_TO_LOCAL and FORCE_SYNC_TO_REMOTE flags
- * are not used. Instead, the EnumSets are used as defined below.
+ * Flags to control the behavior of synchronization.
*
- * Please note that the synchronization protocol is a work in progress, and the meanings of these flags are subject to change.
- *
- * @since 3.0
+ * @since 4.0
*/
public enum SyncFlag {
- DISABLE_SYNC, FORCE_SYNC_TO_LOCAL, FORCE_SYNC_TO_REMOTE;
+ /**
+ * Sync local to remote
+ * @since 4.0
+ */
+ SYNC_LR,
+
+ /**
+ * Sync remote to local
+ * @since 4.0
+ */
+ SYNC_RL;
/**
- * Do not actually transfer files. Just do any necessary bookkeeping. (This is used, for example, when files change on the
- * system, but the user has disabled sync'ing.)
+ * Convenience flag set for sync'ing both directions (from local to remote and from remote to local).
+ * @since 4.0
*/
- public static final EnumSet<SyncFlag> NO_SYNC = EnumSet.of(SyncFlag.DISABLE_SYNC);
+ public static final Set<SyncFlag> BOTH = Collections.unmodifiableSet(EnumSet.allOf(SyncFlag.class));
/**
- * Transfer files "if needed". For example, if a resource change has affected the local repository. (The meaning of "if needed"
- * is subject to change.
+ * Convenience flag set for sync'ing only from local to remote.
+ * @since 4.0
*/
- public static final EnumSet<SyncFlag> NO_FORCE = EnumSet.noneOf(SyncFlag.class);
+ public static final Set<SyncFlag> LR_ONLY = Collections.unmodifiableSet(EnumSet.of(SyncFlag.SYNC_LR));
/**
- * Force transferring of files. This could be necessary, for example, to download remote changes.
+ * Convenience flag set for sync'ing only from remote to local.
+ * @since 4.0
*/
- public static final EnumSet<SyncFlag> FORCE = EnumSet.of(SyncFlag.FORCE_SYNC_TO_LOCAL, SyncFlag.FORCE_SYNC_TO_REMOTE);
-}
+ public static final Set<SyncFlag> RL_ONLY = Collections.unmodifiableSet(EnumSet.of(SyncFlag.SYNC_RL));
+}
\ No newline at end of file
diff --git a/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/SyncManager.java b/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/SyncManager.java
index a4e891b..d2e9c30 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/SyncManager.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/SyncManager.java
@@ -53,11 +53,11 @@
private final SyncConfig fSyncConfig;
private final IResourceDelta fDelta;
private final ISynchronizeService fSyncService;
- private final EnumSet<SyncFlag> fSyncFlags;
+ private final Set<SyncFlag> fSyncFlags;
private final ISyncExceptionHandler fSyncExceptionHandler;
public SynchronizeJob(IProject project, SyncConfig syncConfig, IResourceDelta delta, ISynchronizeService syncService,
- EnumSet<SyncFlag> syncFlags, ISyncExceptionHandler seHandler) {
+ Set<SyncFlag> syncFlags, ISyncExceptionHandler seHandler) {
super(Messages.SyncManager_4);
fProject = project;
fSyncConfig = syncConfig;
@@ -343,7 +343,7 @@
}
// Note that the monitor is ignored for non-blocking jobs since SynchronizeJob creates its own monitor
- private static Job[] scheduleSyncJobs(IResourceDelta delta, IProject project, EnumSet<SyncFlag> syncFlags, boolean syncAll,
+ private static Job[] scheduleSyncJobs(IResourceDelta delta, IProject project, Set<SyncFlag> syncFlags, boolean syncAll,
boolean isBlocking, boolean useExceptionHandler, ISyncExceptionHandler seHandler, IProgressMonitor monitor)
throws CoreException {
int jobNum = 0;
@@ -486,7 +486,7 @@
SyncUtils.flushNode(node);
}
- private static Job sync(IResourceDelta delta, IProject project, EnumSet<SyncFlag> syncFlags, boolean isBlocking,
+ private static Job sync(IResourceDelta delta, IProject project, Set<SyncFlag> syncFlags, boolean isBlocking,
boolean useExceptionHandler, ISyncExceptionHandler seHandler, IProgressMonitor monitor) throws CoreException {
if (getSyncMode(project) == SyncMode.UNAVAILABLE) {
return null;
@@ -509,8 +509,9 @@
* logic to handle exceptions
* @return the scheduled sync job
* @throws CoreException
+ * @since 4.0
*/
- public static Job sync(IResourceDelta delta, IProject project, EnumSet<SyncFlag> syncFlags, ISyncExceptionHandler seHandler)
+ public static Job sync(IResourceDelta delta, IProject project, Set<SyncFlag> syncFlags, ISyncExceptionHandler seHandler)
throws CoreException {
return sync(delta, project, syncFlags, false, true, seHandler, null);
}
@@ -529,8 +530,9 @@
* @return array of sync jobs scheduled
* @throws CoreException
* on problems sync'ing
+ * @since 4.0
*/
- public static Job[] syncAll(IResourceDelta delta, IProject project, EnumSet<SyncFlag> syncFlags, ISyncExceptionHandler seHandler)
+ public static Job[] syncAll(IResourceDelta delta, IProject project, Set<SyncFlag> syncFlags, ISyncExceptionHandler seHandler)
throws CoreException {
if (getSyncMode(project) == SyncMode.UNAVAILABLE) {
return new Job[0];
@@ -554,8 +556,9 @@
* @return array of sync jobs scheduled
* @throws CoreException
* on problems sync'ing
+ * @since 4.0
*/
- public static Job[] syncAllBlocking(IResourceDelta delta, IProject project, EnumSet<SyncFlag> syncFlags,
+ public static Job[] syncAllBlocking(IResourceDelta delta, IProject project, Set<SyncFlag> syncFlags,
ISyncExceptionHandler seHandler) throws CoreException {
if (getSyncMode(project) == SyncMode.UNAVAILABLE) {
return new Job[0];
@@ -579,8 +582,9 @@
* @return the scheduled sync job
* @throws CoreException
* on problems sync'ing
+ * @since 4.0
*/
- public static Job syncBlocking(IResourceDelta delta, IProject project, EnumSet<SyncFlag> syncFlags, IProgressMonitor monitor)
+ public static Job syncBlocking(IResourceDelta delta, IProject project, Set<SyncFlag> syncFlags, IProgressMonitor monitor)
throws CoreException {
return sync(delta, project, syncFlags, true, false, null, monitor);
}
@@ -602,8 +606,9 @@
* @return the scheduled sync job
* @throws CoreException
* on problems sync'ing
+ * @since 4.0
*/
- public static Job syncBlocking(IResourceDelta delta, IProject project, EnumSet<SyncFlag> syncFlags, IProgressMonitor monitor,
+ public static Job syncBlocking(IResourceDelta delta, IProject project, Set<SyncFlag> syncFlags, IProgressMonitor monitor,
ISyncExceptionHandler seHandler) throws CoreException {
return sync(delta, project, syncFlags, true, true, seHandler, monitor);
}
diff --git a/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/services/ISynchronizeService.java b/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/services/ISynchronizeService.java
index 12855e4..6eab809 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/services/ISynchronizeService.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.core/src/org/eclipse/ptp/rdt/sync/core/services/ISynchronizeService.java
@@ -118,7 +118,7 @@
* @since 4.0
*/
public void synchronize(IProject project, RemoteLocation remoteLoc, IResourceDelta delta,
- IProgressMonitor monitor, EnumSet<SyncFlag> syncFlags) throws CoreException;
+ IProgressMonitor monitor, Set<SyncFlag> syncFlags) throws CoreException;
/**
* Get SyncFileFilter for given project
diff --git a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/GitRepo.java b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/GitRepo.java
index c01f378..2645c20 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/GitRepo.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/GitRepo.java
@@ -320,9 +320,13 @@
*/
public void merge(IProgressMonitor monitor) throws RemoteSyncException, MissingConnectionException {
CommandResults mergeResults;
- // ff-only prevents accidental corruption of the remote repository but is supported only in recent Git versions.
- // final String command = gitCommand + " merge --ff-only " + remotePushBranch; //$NON-NLS-1$
- final String command = gitCommand() + " merge " + GitSyncService.remotePushBranch; //$NON-NLS-1$
+ // ff-only was introduced in Git 1.6.6 and prevents accidental corruption of the remote repository.
+ String command;
+ if (remoteGitVersion >= 1060600) {
+ command = gitCommand() + " merge --ff-only " + GitSyncService.remotePushBranch; //$NON-NLS-1$
+ } else {
+ command = gitCommand() + " merge " + GitSyncService.remotePushBranch; //$NON-NLS-1$
+ }
try {
mergeResults = this.executeRemoteCommand(command, monitor);
@@ -367,6 +371,37 @@
}
/**
+ * Get the SHA-1 of the current head commit
+ *
+ * @param monitor
+ * @return SHA-1 hash code or null if no HEAD commit (can happen between initialization and first commit)
+ *
+ * @throws RemoteSyncException
+ * on problems executing the necessary remote commands.
+ * @throws MissingConnectionException
+ * if the connection is unresolved
+ */
+ public String getHead(IProgressMonitor monitor) throws RemoteSyncException, MissingConnectionException {
+ String command = gitCommand() + " rev-parse HEAD"; //$NON-NLS-1$
+ CommandResults headResults;
+ try {
+ headResults = this.executeRemoteCommand(command, monitor);
+ } catch (IOException e) {
+ throw new RemoteSyncException(e);
+ } catch (InterruptedException e) {
+ throw new RemoteSyncException(e);
+ } catch (RemoteConnectionException e) {
+ throw new RemoteSyncException(e);
+ }
+ // Assume failure occurs because there is no head.
+ if (headResults.getExitCode() > 0) {
+ return null;
+ } else {
+ return headResults.getStdout().trim();
+ }
+ }
+
+ /**
* Get the remote location of this repository
* @return remote location
*/
@@ -378,7 +413,7 @@
* Return the Git version used for this repository
*
* @param monitor
- * @return Git version as a single int in the format: MMMmmmrrr (Major, minor, and revision)
+ * @return Git version as a single int in the format: MMmmrrpp (Major, minor, revision, and patch)
*
* @throws RemoteSyncException
* @throws MissingConnectionException
diff --git a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/GitSyncService.java b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/GitSyncService.java
index f19e664..47d1adc 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/GitSyncService.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/GitSyncService.java
@@ -11,7 +11,6 @@
package org.eclipse.ptp.internal.rdt.sync.git.core;
import java.io.IOException;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -62,11 +61,15 @@
// Variables for managing sync threads
private static final ReentrantLock syncLock = new ReentrantLock();
- private static final ConcurrentMap<ProjectAndRemoteLocationPair, AtomicLong> syncThreadsWaiting =
+ private static final ConcurrentMap<ProjectAndRemoteLocationPair, AtomicLong> syncLRPending =
+ new ConcurrentHashMap<ProjectAndRemoteLocationPair, AtomicLong>();
+ private static final ConcurrentMap<ProjectAndRemoteLocationPair, AtomicLong> syncRLPending =
new ConcurrentHashMap<ProjectAndRemoteLocationPair, AtomicLong>();
// Entry indicates that the remote location has a clean (up-to-date) file filter for the project
private static final Set<LocalAndRemoteLocationPair> cleanFileFilterMap = new HashSet<LocalAndRemoteLocationPair>();
+ // Entry indicates that the remote location contains the most recently committed local changes
+ private static final Set<LocalAndRemoteLocationPair> localChangesPushed = new HashSet<LocalAndRemoteLocationPair>();
// Boilerplate class for IPath and RemoteLocation Pair
private class LocalAndRemoteLocationPair {
@@ -408,7 +411,7 @@
*/
@Override
public void synchronize(final IProject project, RemoteLocation rl, IResourceDelta delta, IProgressMonitor monitor,
- EnumSet<SyncFlag> syncFlags) throws CoreException {
+ Set<SyncFlag> syncFlags) throws CoreException {
if (project == null || rl == null) {
throw new NullPointerException();
}
@@ -417,7 +420,7 @@
try {
/*
- * A synchronize with SyncFlag.FORCE guarantees that both directories are in sync.
+ * A synchronize with SyncFlag.BOTH guarantees that both directories are in sync.
*
* More precise: it guarantees that all changes written to disk at the moment of the call are guaranteed to be
* synchronized between both directories. No guarantees are given for changes occurring during the synchronize call.
@@ -428,24 +431,41 @@
* Example: Why sync if current delta is empty? The RemoteMakeBuilder forces a sync before and after building. In some
* cases, we want to ensure repos are synchronized regardless of the passed delta, which can be set to null.
*/
- // TODO: We are not using the individual "sync to local" and "sync to remote" flags yet.
- if (syncFlags.contains(SyncFlag.DISABLE_SYNC)) {
- return;
+
+ ProjectAndRemoteLocationPair syncTarget = new ProjectAndRemoteLocationPair(project, remoteLoc);
+ Boolean syncLR = syncFlags.contains(SyncFlag.SYNC_LR);
+ Boolean syncRL = syncFlags.contains(SyncFlag.SYNC_RL);
+ Set<SyncFlag> modifiedSyncFlags = new HashSet<SyncFlag>(syncFlags);
+
+ // Do not sync LR (local-to-remote) if another thread is already waiting to do it.
+ if (syncLR) {
+ AtomicLong threadCount = syncLRPending.putIfAbsent(syncTarget, new AtomicLong(1));
+ if (threadCount != null) {
+ if (threadCount.get() > 0) {
+ syncLR = false;
+ modifiedSyncFlags.remove(SyncFlag.SYNC_LR);
+ } else {
+ threadCount.incrementAndGet();
+ }
+ }
}
- ProjectAndRemoteLocationPair syncTarget = new ProjectAndRemoteLocationPair(project, remoteLoc);
- AtomicLong threadCount = syncThreadsWaiting.get(syncTarget);
- if (threadCount != null && threadCount.get() > 0 && syncFlags == SyncFlag.NO_FORCE) {
- return; // the queued thread will do the work for us. And we don't have to wait because of NO_FORCE
- }
+ // Do not sync RL (remote-to-local) if another thread is already waiting to do it.
+ if (syncRL) {
+ AtomicLong threadCount = syncRLPending.putIfAbsent(syncTarget, new AtomicLong(1));
+ if (threadCount != null) {
+ if (threadCount.get() > 0) {
+ syncRL = false;
+ modifiedSyncFlags.remove(SyncFlag.SYNC_RL);
+ } else {
+ threadCount.incrementAndGet();
+ }
+ }
+ }
- // Increment the value, initializing it if necessary. See:
- // http://stackoverflow.com/questions/2539654/java-concurrency-many-writers-one-reader/2539761#2539761
- if (threadCount == null){
- threadCount = syncThreadsWaiting.putIfAbsent(syncTarget, new AtomicLong(1));
- }
- if(threadCount != null){
- threadCount.incrementAndGet();
+ // Return if we have nothing to do.
+ if (!(syncLR || syncRL)) {
+ return;
}
// lock syncLock. interruptible by progress monitor
@@ -458,14 +478,21 @@
} catch (InterruptedException e1) {
throw new CoreException(new Status(IStatus.CANCEL, Activator.PLUGIN_ID, Messages.GitSyncService_5));
} finally {
- threadCount = syncThreadsWaiting.get(syncTarget);
- assert(threadCount != null) : Messages.GitSyncService_19;
- threadCount.decrementAndGet();
+ if (syncLR) {
+ AtomicLong LRPending = syncLRPending.get(syncTarget);
+ assert(LRPending != null) : Messages.GitSyncService_20;
+ LRPending.decrementAndGet();
+ }
+ if (syncRL) {
+ AtomicLong RLPending = syncRLPending.get(syncTarget);
+ assert(RLPending != null) : Messages.GitSyncService_21;
+ RLPending.decrementAndGet();
+ }
}
try {
subMon.subTask(Messages.GitSyncService_9);
- doSync(project, remoteLoc, syncFlags, subMon.newChild(95));
+ doSync(project, remoteLoc, modifiedSyncFlags, subMon.newChild(95));
} catch (RemoteSyncMergeConflictException e) {
subMon.subTask(Messages.GitSyncService_10);
// Refresh after merge conflict since conflicted files are altered with markup.
@@ -490,12 +517,12 @@
}
/**
- * Synchronize the given project to the given remote. Currently both directions are always synchronized.
+ * Synchronize the given project to the given remote.
* The sync strategy follows these three high-level steps:
* 1) Commit local and remote changes. These are independent operations.
- * 2) Fetch remote changes and merge them locally. Thus, all merge conflicts should occur locally and thus easily managed.
- * 3) Push local changes to remote and merge them remotely. This final merge should never fail assuming files are
- * unchanged during the sync.
+ * 2) Fetch remote changes and merge them locally. Thus, all merge conflicts should occur locally and can be easily managed.
+ * 3) Push local changes to remote and merge them remotely. This final merge should never fail assuming files are unchanged
+ * during the sync.
*
* @param project
* the local project
@@ -508,7 +535,7 @@
* for various problems sync'ing. All exceptions are wrapped in a RemoteSyncException and thrown, so that clients
* can always detect when a sync fails and why.
*/
- private void doSync(IProject project, RemoteLocation remoteLoc, EnumSet<SyncFlag> syncFlags, IProgressMonitor monitor)
+ private void doSync(IProject project, RemoteLocation remoteLoc, Set<SyncFlag> syncFlags, IProgressMonitor monitor)
throws RemoteSyncException {
RecursiveSubMonitor subMon = RecursiveSubMonitor.convert(monitor, 100);
try {
@@ -519,60 +546,83 @@
throw new RemoteSyncMergeConflictException(Messages.GitSyncService_8);
}
+ LocalAndRemoteLocationPair lrpair = new LocalAndRemoteLocationPair(localRepo.getDirectory(), remoteLoc);
+
// Commit local changes
subMon.subTask(Messages.GitSyncService_12);
- boolean hasChanges = localRepo.commit(subMon.newChild(5));
- if ((!hasChanges) && (syncFlags == SyncFlag.NO_FORCE)) {
+ if (localRepo.commit(subMon.newChild(5))) {
+ // New changes - mark all local/remote pairs with current local as needing to be updated.
+ Iterator<LocalAndRemoteLocationPair> it = localChangesPushed.iterator();
+ while (it.hasNext()) {
+ LocalAndRemoteLocationPair lrp = it.next();
+ if (lrp.getLocal().equals(localRepo.getDirectory())) {
+ it.remove();
+ }
+ }
+ }
+
+ // Return early if local changes have already been pushed and remote-to-local sync was not requested.
+ if ((localChangesPushed.contains(lrpair)) && (!syncFlags.contains(SyncFlag.SYNC_RL))) {
return;
}
// Get remote repository. creating it if necessary
subMon.subTask(Messages.GitSyncService_7);
GitRepo remoteRepo = getGitRepo(remoteLoc, subMon.newChild(15));
+
// Unresolved connection - abort
if (remoteRepo == null) {
return;
}
// Update remote file filter
- LocalAndRemoteLocationPair lp = new LocalAndRemoteLocationPair(localRepo.getDirectory(), remoteRepo.getRemoteLocation());
- int commitWork = 20;
- if (!cleanFileFilterMap.contains(lp)) {
+ int commitWork = 15;
+ if (!cleanFileFilterMap.contains(lrpair)) {
commitWork -= 10;
subMon.subTask(Messages.GitSyncService_11);
remoteRepo.uploadFilter(localRepo, subMon.newChild(10));
- cleanFileFilterMap.add(lp);
+ cleanFileFilterMap.add(lrpair);
}
+ // Commit remote changes
subMon.subTask(Messages.GitSyncService_13);
remoteRepo.commitRemoteFiles(subMon.newChild(commitWork));
- try {
- // Fetch the remote repository
- subMon.subTask(Messages.GitSyncService_14);
- localRepo.fetch(remoteRepo.getRemoteLocation(), subMon.newChild(20));
+ // Get hash code of the head of the remote repository
+ subMon.subTask(Messages.GitSyncService_22);
+ String remoteHead = remoteRepo.getHead(subMon.newChild(5));
- // Merge it with local
- subMon.subTask(Messages.GitSyncService_15);
- org.eclipse.jgit.api.MergeResult mergeResult = localRepo.merge(subMon.newChild(5));
- if (mergeResult.getFailingPaths() != null) {
- String message = Messages.GitSyncService_16;
- for (String s : mergeResult.getFailingPaths().keySet()) {
- message += System.getProperty("line.separator") + s; //$NON-NLS-1$
+ // Sync remote-to-local if and only if the remote head commit is null or not in the local repository.
+ // Note that the sync flag settings are irrelevant here. If only an LR sync is requested, we still need to update the
+ // local with remote changes, and if RL sync is requested, it is still unnecessary if there are no remote changes.
+ try {
+ if ((remoteHead == null) || (!localRepo.commitExists(remoteHead))) {
+ // Fetch the remote repository
+ subMon.subTask(Messages.GitSyncService_14);
+ localRepo.fetch(remoteRepo.getRemoteLocation(), subMon.newChild(20));
+
+ // Merge it with local
+ subMon.subTask(Messages.GitSyncService_15);
+ org.eclipse.jgit.api.MergeResult mergeResult = localRepo.merge(subMon.newChild(5));
+ if (mergeResult.getFailingPaths() != null) {
+ String message = Messages.GitSyncService_16;
+ for (String s : mergeResult.getFailingPaths().keySet()) {
+ message += System.getProperty("line.separator") + s; //$NON-NLS-1$
+ }
+ throw new RemoteSyncException(message);
}
- throw new RemoteSyncException(message);
- }
- if (localRepo.inUnresolvedMergeState()) {
- throw new RemoteSyncMergeConflictException(Messages.GitSyncService_8);
- // Even if we later decide not to throw an exception, it is important not to proceed after a merge conflict.
- // return;
+ if (localRepo.inUnresolvedMergeState()) {
+ throw new RemoteSyncMergeConflictException(Messages.GitSyncService_8);
+ // Even if we later decide not to throw an exception, it is important not to proceed after a merge conflict.
+ // return;
+ }
}
} catch (TransportException e) {
if (e.getMessage().startsWith("Remote does not have ")) { //$NON-NLS-1$
// Means that the remote branch isn't set up yet (and thus nothing to fetch). Can be ignored and local to
// remote sync can proceed.
- // Note: It is important, though, that we do not merge if fetch fails. Merge will fail because remote ref is
- // not created.
+ // Note: It is important, though, that we do not merge if fetch fails. Merge will fail because remote ref
+ // is not created.
} else {
throw new RemoteSyncException(e);
}
@@ -581,10 +631,13 @@
}
// Push local repository to remote
- if (localRepo.getGit().branchList().call().size() > 0) { // check whether master was already created
- subMon.subTask(Messages.GitSyncService_18);
- localRepo.push(remoteRepo.getRemoteLocation(), subMon.newChild(20));
- remoteRepo.merge(subMon.newChild(10));
+ if ((!localChangesPushed.contains(lrpair)) && (syncFlags.contains(SyncFlag.SYNC_LR))) {
+ if (localRepo.getGit().branchList().call().size() > 0) { // check whether master was already created
+ subMon.subTask(Messages.GitSyncService_18);
+ localRepo.push(remoteRepo.getRemoteLocation(), subMon.newChild(20));
+ remoteRepo.merge(subMon.newChild(10));
+ localChangesPushed.add(lrpair);
+ }
}
} catch (final IOException e) {
throw new RemoteSyncException(e);
diff --git a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/JGitRepo.java b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/JGitRepo.java
index 96d6f68..8dcb617 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/JGitRepo.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/JGitRepo.java
@@ -33,7 +33,9 @@
import org.eclipse.jgit.api.StatusCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.AmbiguousObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.errors.RevisionSyntaxException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
@@ -293,6 +295,23 @@
}
/**
+ * Check if the given commit exists.
+ *
+ * @return whether the commit exists
+ * @throws IOException
+ * on problems accessing the file system
+ * @throws IncorrectObjectTypeException
+ * @throws AmbiguousObjectException
+ * @throws RevisionSyntaxException
+ * exceptions that most likely indicate JGit had problems handling the passed id
+ */
+ boolean commitExists(String commitId) throws RevisionSyntaxException, AmbiguousObjectException, IncorrectObjectTypeException,
+ IOException {
+ ObjectId commitObjectId = this.getRepository().resolve(commitId);
+ return this.getRepository().hasObject(commitObjectId);
+ }
+
+ /**
* Fetch files from the given remote. This only transmits the files. It does not update the local repository.
*
* @param remoteLoc
diff --git a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/messages/Messages.java b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/messages/Messages.java
index f7e9039..2bf9f13 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/messages/Messages.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/messages/Messages.java
@@ -47,6 +47,9 @@
public static String GitSyncService_18;
public static String GitSyncService_19;
public static String GitSyncService_2;
+ public static String GitSyncService_20;
+ public static String GitSyncService_21;
+ public static String GitSyncService_22;
public static String GitSyncService_3;
public static String GitSyncService_4;
public static String GitSyncService_5;
diff --git a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/messages/messages.properties b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/messages/messages.properties
index 54b1389..c5ce84d 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/messages/messages.properties
+++ b/rdt/org.eclipse.ptp.rdt.sync.git.core/src/org/eclipse/ptp/internal/rdt/sync/git/core/messages/messages.properties
@@ -41,6 +41,9 @@
GitSyncService_18=Pushing local changes to remote
GitSyncService_19=Missing sync thread waiting count that should have already been initialized.
GitSyncService_2=Creating remote Git repository
+GitSyncService_20=Missing LR sync thread count that should have already been initialized.
+GitSyncService_21=Missing RL sync thread count that should have already been initialized.
+GitSyncService_22=Check if there are unknown remote changes
GitSyncService_3=Internal error - Git sync service constructor called more than once
GitSyncService_4=Synchronization canceled
GitSyncService_5=Synchronization interrupted
diff --git a/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/ResourceChangeListener.java b/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/ResourceChangeListener.java
index 2786e9a..a69f7f4 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/ResourceChangeListener.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/ResourceChangeListener.java
@@ -12,6 +12,9 @@
*******************************************************************************/
package org.eclipse.ptp.internal.rdt.sync.ui;
+import java.util.EnumSet;
+import java.util.Set;
+
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
@@ -76,19 +79,17 @@
*/
if (syncConfig != null) {
try {
+ Set<SyncFlag> f = SyncFlag.RL_ONLY;
if (delta.getKind() == IResourceDelta.CHANGED && syncConfig.isSyncOnSave()) {
- // Do a non-forced sync to update any changes reported in delta. Sync'ing is necessary even if user
- // has turned it off. This allows for some bookkeeping but no files are transferred.
- if (syncMode == SyncMode.UNAVAILABLE) {
+ // Do a local-to-remote sync to update any changes reported in delta.
+ if ((syncMode == SyncMode.UNAVAILABLE) || (!syncOn)) {
continue;
- } else if (!syncOn) {
- SyncManager.sync(delta, project, SyncFlag.NO_SYNC, null);
} else if (syncMode == SyncMode.ALL) {
- SyncManager.syncAll(delta, project, SyncFlag.NO_FORCE, new CommonSyncExceptionHandler(true,
+ SyncManager.syncAll(delta, project, SyncFlag.LR_ONLY, new CommonSyncExceptionHandler(true,
false));
} else if (syncMode == SyncMode.ACTIVE) {
SyncManager
- .sync(delta, project, SyncFlag.NO_FORCE, new CommonSyncExceptionHandler(true, false));
+ .sync(delta, project, SyncFlag.LR_ONLY, new CommonSyncExceptionHandler(true, false));
}
}
} catch (CoreException e) {
diff --git a/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/menus/SyncMenuOperation.java b/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/menus/SyncMenuOperation.java
index a720120..7339c9c 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/menus/SyncMenuOperation.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/menus/SyncMenuOperation.java
@@ -73,16 +73,16 @@
// On sync request, sync regardless of the flags
try {
if (command.equals(syncActiveCommand)) {
- SyncManager.sync(null, project, SyncFlag.FORCE, syncExceptionHandler);
+ SyncManager.sync(null, project, SyncFlag.BOTH, syncExceptionHandler);
} else if (command.equals(syncAllCommand)) {
- SyncManager.syncAll(null, project, SyncFlag.FORCE, syncExceptionHandler);
+ SyncManager.syncAll(null, project, SyncFlag.BOTH, syncExceptionHandler);
// If user switches to active or all, assume the user wants to sync right away
} else if (command.equals(setActiveCommand)) {
SyncManager.setSyncMode(project, SyncMode.ACTIVE);
- SyncManager.sync(null, project, SyncFlag.FORCE, syncExceptionHandler);
+ SyncManager.sync(null, project, SyncFlag.BOTH, syncExceptionHandler);
} else if (command.equals(setAllCommand)) {
SyncManager.setSyncMode(project, SyncMode.ALL);
- SyncManager.syncAll(null, project, SyncFlag.FORCE, syncExceptionHandler);
+ SyncManager.syncAll(null, project, SyncFlag.BOTH, syncExceptionHandler);
} else if (command.equals(setNoneCommand)) {
SyncManager.setSyncMode(project, SyncMode.NONE);
} else if (command.equals(syncAutoCommand)) {
@@ -91,9 +91,9 @@
if (SyncManager.getSyncAuto()) {
SyncMode syncMode = SyncManager.getSyncMode(project);
if (syncMode == SyncMode.ACTIVE) {
- SyncManager.sync(null, project, SyncFlag.FORCE, syncExceptionHandler);
+ SyncManager.sync(null, project, SyncFlag.BOTH, syncExceptionHandler);
} else if (syncMode == SyncMode.ALL) {
- SyncManager.syncAll(null, project, SyncFlag.FORCE, syncExceptionHandler);
+ SyncManager.syncAll(null, project, SyncFlag.BOTH, syncExceptionHandler);
}
}
} else if (command.equals(syncExcludeCommand) || command.equals(syncIncludeCommand)) {
diff --git a/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/wizards/NewSyncProjectWizard.java b/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/wizards/NewSyncProjectWizard.java
index 348ba8f..1c9baa8 100644
--- a/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/wizards/NewSyncProjectWizard.java
+++ b/rdt/org.eclipse.ptp.rdt.sync.ui/src/org/eclipse/ptp/internal/rdt/sync/ui/wizards/NewSyncProjectWizard.java
@@ -409,7 +409,7 @@
// Force an initial sync
try {
- SyncManager.sync(null, project, SyncFlag.FORCE, new CommonSyncExceptionHandler(false, true));
+ SyncManager.sync(null, project, SyncFlag.BOTH, new CommonSyncExceptionHandler(false, true));
} catch (CoreException e) {
// This should never happen because only a blocking sync can throw a core exception.
RDTSyncUIPlugin.log(Messages.NewSyncProjectWizard_Unexpected_core_exception, e);
diff --git a/tools/gem/org.eclipse.ptp.gem/src/org/eclipse/ptp/internal/gem/util/GemUtilities.java b/tools/gem/org.eclipse.ptp.gem/src/org/eclipse/ptp/internal/gem/util/GemUtilities.java
index 5f4607b..473efc9 100644
--- a/tools/gem/org.eclipse.ptp.gem/src/org/eclipse/ptp/internal/gem/util/GemUtilities.java
+++ b/tools/gem/org.eclipse.ptp.gem/src/org/eclipse/ptp/internal/gem/util/GemUtilities.java
@@ -1347,7 +1347,7 @@
final IProgressMonitor monitor = new NullProgressMonitor();
final IProject project = getCurrentProject(gemActiveResource);
try {
- SyncManager.syncBlocking(null, project, SyncFlag.FORCE, monitor);
+ SyncManager.syncBlocking(null, project, SyncFlag.BOTH, monitor);
} catch (final CoreException e) {
logExceptionDetail(e);
}