aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDariusz Luksza2010-04-17 22:35:08 (EDT)
committerChris Aniszczyk2010-06-17 20:09:09 (EDT)
commit6a4218ebd200b139c292807fe41b4d05bf9284ff (patch)
tree9339ea6485db6250a6779bac49bce64aaf038afb
parent13110abd2a8a9d0843cf24a08ab32a0b3b270fb2 (diff)
downloadegit-6a4218ebd200b139c292807fe41b4d05bf9284ff.zip
egit-6a4218ebd200b139c292807fe41b4d05bf9284ff.tar.gz
egit-6a4218ebd200b139c292807fe41b4d05bf9284ff.tar.bz2
Add preliminary synchronization support within Eclipse for branches.refs/changes/77/577/16
Implements synchronization participants and resource variants for allowing clients to synchronize Eclipse resources against the Git index and a given branch. Synchronization can be launched for project's context menu by 'Team -> Synchronize...' and from 'Repositories View' from branch context menu. In 'Synchronize repository' dialog user can choose source and destination branch (or tag). He can also define does local uncommited changes should be included in 'Synchronize view'. CQ: 4122 Bug: 309582 Also-by: Remy Suen <remysuen@ca.ibm.com> Change-Id: Ib838a6c88eccbef8c7ffb6aa1c602bdc8778dbc6 Signed-off-by: Dariusz Luksza <dariusz@luksza.org> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
-rw-r--r--org.eclipse.egit.core/META-INF/MANIFEST.MF24
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java15
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties9
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitBaseResourceVariantTree.java38
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitBlobResourceVariant.java122
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitFolderResourceVariant.java59
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitRemoteResourceVariantTree.java38
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariant.java36
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantComparator.java163
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantTree.java425
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantTreeSubscriber.java124
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitSyncInfo.java185
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/dto/GitSynchronizeData.java176
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/dto/GitSynchronizeDataSet.java88
-rw-r--r--org.eclipse.egit.ui/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.egit.ui/plugin.properties8
-rw-r--r--org.eclipse.egit.ui/plugin.xml78
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java57
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/RepositoryAction.java16
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SynchronizeWithAction.java128
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoriesView.java2
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SynchronizeCommand.java80
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/CommitAction.java32
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/CommitOperation.java52
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSubscriberParticipant.java101
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronize.java68
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronizeWizard.java61
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronizeWizardPage.java355
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/RemoteSelectionCombo.java90
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/SelectSynchronizeResourceDialog.java159
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/SyncRepoEntity.java94
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties26
32 files changed, 2902 insertions, 10 deletions
diff --git a/org.eclipse.egit.core/META-INF/MANIFEST.MF b/org.eclipse.egit.core/META-INF/MANIFEST.MF
index a153072..86dbec0 100644
--- a/org.eclipse.egit.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.egit.core/META-INF/MANIFEST.MF
@@ -10,11 +10,29 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.4.0,4.0.0)",
org.eclipse.team.core;bundle-version="[3.4.0,4.0.0)",
org.eclipse.core.resources;bundle-version="[3.4.0,4.0.0)",
org.eclipse.core.filesystem;bundle-version="[1.2.0,2.0.0)"
-Export-Package: org.eclipse.egit.core;version="0.9.0",
+Export-Package: org.eclipse.egit.core;version="0.9.0";
+ uses:="org.eclipse.core.runtime,
+ org.eclipse.egit.core.project,
+ org.eclipse.core.resources.team,
+ org.eclipse.team.core.history,
+ org.eclipse.core.runtime.preferences,
+ org.eclipse.jgit.lib,
+ org.eclipse.osgi.util,
+ org.eclipse.jgit.treewalk,
+ org.eclipse.team.core,
+ org.eclipse.core.resources,
+ org.osgi.framework",
org.eclipse.egit.core.internal.storage;version="0.9.0";x-friends:="org.eclipse.egit.ui,org.eclipse.egit.core.test",
org.eclipse.egit.core.internal.util;version="0.9.0";x-friends:="org.eclipse.egit.ui",
- org.eclipse.egit.core.op;version="0.9.0",
- org.eclipse.egit.core.project;version="0.9.0"
+ org.eclipse.egit.core.op;version="0.9.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.core.runtime,
+ org.eclipse.core.runtime.jobs,
+ org.eclipse.core.resources,
+ org.eclipse.jgit.transport",
+ org.eclipse.egit.core.project;version="0.9.0";uses:="org.eclipse.jgit.lib,org.eclipse.core.runtime,org.eclipse.core.resources",
+ org.eclipse.egit.core.synchronize;version="0.9.0",
+ org.eclipse.egit.core.synchronize.dto;version="0.9.0";uses:="org.eclipse.jgit.lib,org.eclipse.core.resources"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: org.eclipse.jgit.api;version="[0.9.0,0.10.0)",
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java
index c1e206c..1722c78 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java
@@ -274,6 +274,21 @@ public class CoreText extends NLS {
/** */
public static String TagOperation_objectIdNotFound;
+ /** */
+ public static String GitResourceVariantTree_couldNotFindBlob;
+
+ /** */
+ public static String GitResourceVariantTree_fetchingMembers;
+
+ /** */
+ public static String GitResourceVariantTree_fetchingVariant;
+
+ /** */
+ public static String GitResourceVariantTree_unableToReadRepository;
+
+ /** */
+ public static String GitBranchResourceVariantTreeSubscriber_gitRepository;
+
static {
initializeMessages("org.eclipse.egit.core.coretext", //$NON-NLS-1$
CoreText.class);
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties b/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties
index bc636ae..aac598b 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties
@@ -112,4 +112,11 @@ BranchOperation_performingBranch=Performing branch to {0}
TagOperation_performingTagging=Making tag {0}
TagOperation_taggingFailure=Tag {0} creation failed (cause: {1})
-TagOperation_objectIdNotFound=Could not find object Id associated with tag {0} (cause: {1}) \ No newline at end of file
+TagOperation_objectIdNotFound=Could not find object Id associated with tag {0} (cause: {1})
+
+GitResourceVariantTree_couldNotFindBlob=Could not find blob member for {0}
+GitResourceVariantTree_fetchingMembers=Fetching members of {0}
+GitResourceVariantTree_fetchingVariant=Fetching variant for: {0}
+GitResourceVariantTree_unableToReadRepository="Unable to read repository for {0}
+
+GitBranchResourceVariantTreeSubscriber_gitRepository=Git without local changes \ No newline at end of file
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitBaseResourceVariantTree.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitBaseResourceVariantTree.java
new file mode 100644
index 0000000..3c5c4e0
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitBaseResourceVariantTree.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize;
+
+import java.io.IOException;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Tree;
+import org.eclipse.team.core.variants.ResourceVariantByteStore;
+
+class GitBaseResourceVariantTree extends GitResourceVariantTree {
+
+ GitBaseResourceVariantTree(GitSynchronizeDataSet data, ResourceVariantByteStore store) {
+ super(data, store);
+ }
+
+ @Override
+ Tree getRevTree(IResource resource) throws IOException {
+ return getSyncData().getData(resource.getProject()).mapSrcTree();
+ }
+
+ @Override
+ ObjectId getRevObjId(IResource resource) throws IOException {
+ return getSyncData().getData(resource.getProject()).getSrcObjectId();
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitBlobResourceVariant.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitBlobResourceVariant.java
new file mode 100644
index 0000000..47e5e4c
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitBlobResourceVariant.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.resources.IEncodedStorage;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevCommitList;
+import org.eclipse.team.core.TeamException;
+
+/**
+ * This is a representation of a file's blob in some branch.
+ */
+class GitBlobResourceVariant extends GitResourceVariant {
+
+ private ObjectId id;
+
+ private Repository repository;
+
+ private IStorage storage;
+
+ private RevCommitList<RevCommit> commitList;
+
+ GitBlobResourceVariant(IResource resource, Repository repository,
+ ObjectId id, RevCommitList<RevCommit> commitList) {
+ super(resource);
+ this.repository = repository;
+ this.id = id;
+ this.commitList = commitList;
+ }
+
+ ObjectId getId() {
+ return id;
+ }
+
+ RevCommitList<RevCommit> getCommitList() {
+ return commitList;
+ }
+
+ public boolean isContainer() {
+ return false;
+ }
+
+ public IStorage getStorage(IProgressMonitor monitor) throws TeamException {
+ if (storage == null) {
+ try {
+ ObjectLoader ol = repository.openBlob(id);
+ final byte[] bytes = ol.getBytes();
+ storage = new IEncodedStorage() {
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ public boolean isReadOnly() {
+ return true;
+ }
+
+ public String getName() {
+ return GitBlobResourceVariant.this.getName();
+ }
+
+ public IPath getFullPath() {
+ return null;
+ }
+
+ public InputStream getContents() throws CoreException {
+ return new ByteArrayInputStream(bytes);
+ }
+
+ public String getCharset() throws CoreException {
+ IContentTypeManager manager = Platform
+ .getContentTypeManager();
+ try {
+ IContentDescription description = manager
+ .getDescriptionFor(getContents(),
+ getName(), IContentDescription.ALL);
+ return description == null ? null : description
+ .getCharset();
+ } catch (IOException e) {
+ throw new CoreException(new Status(IStatus.ERROR,
+ Activator.getPluginId(), e.getMessage(), e));
+ }
+ }
+ };
+ } catch (IOException e) {
+ throw new TeamException(new Status(IStatus.ERROR, Activator
+ .getPluginId(), e.getMessage(), e));
+ }
+ }
+ return storage;
+ }
+
+ public String getContentIdentifier() {
+ return id.name();
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitFolderResourceVariant.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitFolderResourceVariant.java
new file mode 100644
index 0000000..75182d5
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitFolderResourceVariant.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+
+class GitFolderResourceVariant extends GitResourceVariant {
+
+ GitFolderResourceVariant(IResource resource) {
+ super(resource);
+ }
+
+ IContainer getContainer() {
+ return (IContainer) getResource();
+ }
+
+ public boolean isContainer() {
+ return true;
+ }
+
+ public IStorage getStorage(IProgressMonitor monitor) throws TeamException {
+ return null;
+ }
+
+ public String getContentIdentifier() {
+ return getName();
+ }
+
+ @Override
+ public int hashCode() {
+ return getResource().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj == null) {
+ return false;
+ } else if (getClass() != obj.getClass()) {
+ return false;
+ }
+ GitFolderResourceVariant other = (GitFolderResourceVariant) obj;
+ return getResource().equals(other.getResource());
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitRemoteResourceVariantTree.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitRemoteResourceVariantTree.java
new file mode 100644
index 0000000..1ce2e4d
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitRemoteResourceVariantTree.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize;
+
+import java.io.IOException;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Tree;
+import org.eclipse.team.core.variants.ResourceVariantByteStore;
+
+class GitRemoteResourceVariantTree extends GitResourceVariantTree {
+
+ GitRemoteResourceVariantTree(GitSynchronizeDataSet data, ResourceVariantByteStore store) {
+ super(data, store);
+ }
+
+ @Override
+ Tree getRevTree(IResource resource) throws IOException {
+ return getSyncData().getData(resource.getProject()).mapDstTree();
+ }
+
+ @Override
+ ObjectId getRevObjId(IResource resource) throws IOException {
+ return getSyncData().getData(resource.getProject()).getDstObjectId();
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariant.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariant.java
new file mode 100644
index 0000000..b24658b
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariant.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.team.core.variants.IResourceVariant;
+
+abstract class GitResourceVariant implements IResourceVariant {
+
+ private IResource resource;
+
+ GitResourceVariant(IResource resource) {
+ this.resource = resource;
+ }
+
+ public IResource getResource() {
+ return resource;
+ }
+
+ public String getName() {
+ return resource.getName();
+ }
+
+ public byte[] asBytes() {
+ return getContentIdentifier().getBytes();
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantComparator.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantComparator.java
new file mode 100644
index 0000000..26fbd07
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantComparator.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.synchronize.GitBlobResourceVariant;
+import org.eclipse.egit.core.synchronize.GitFolderResourceVariant;
+import org.eclipse.egit.core.synchronize.GitResourceVariant;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.core.variants.IResourceVariantComparator;
+import org.eclipse.team.core.variants.ResourceVariantByteStore;
+
+class GitResourceVariantComparator implements IResourceVariantComparator {
+
+ private final GitSynchronizeDataSet gsd;
+ private final ResourceVariantByteStore store;
+
+ public GitResourceVariantComparator(GitSynchronizeDataSet dataSet, ResourceVariantByteStore store) {
+ gsd = dataSet;
+ this.store = store;
+ }
+
+ public boolean compare(IResource local, IResourceVariant remote) {
+ if (!local.exists() || remote == null) {
+ return false;
+ }
+
+ if (local instanceof IFile) {
+ if (remote.isContainer()) {
+ return false;
+ }
+
+ InputStream stream = null;
+ InputStream remoteStream = null;
+ try {
+ remoteStream = remote.getStorage(
+ new NullProgressMonitor()).getContents();
+ stream = getLocal(local);
+ byte[] remoteBytes = new byte[8096];
+ byte[] bytes = new byte[8096];
+
+ int remoteRead = remoteStream.read(remoteBytes);
+ int read = stream.read(bytes);
+ if (remoteRead != read) {
+ return false;
+ }
+
+ while (Arrays.equals(bytes, remoteBytes)) {
+ remoteRead = remoteStream.read(remoteBytes);
+ read = stream.read(bytes);
+ if (remoteRead != read) {
+ // didn't read the same amount, it's uneven
+ return false;
+ } else if (read == -1) {
+ // both at EOF, check their contents
+ return Arrays.equals(bytes, remoteBytes);
+ }
+ }
+ } catch (IOException e) {
+ logException(e);
+ return false;
+ } catch (CoreException e) {
+ logException(e);
+ return false;
+ } finally {
+ closeStream(stream);
+ closeStream(remoteStream);
+ }
+ } else if (local instanceof IContainer) {
+ if (!remote.isContainer()) {
+ return false;
+ }
+
+ GitFolderResourceVariant gitVariant = (GitFolderResourceVariant) remote;
+ return local.getFullPath().equals(
+ gitVariant.getResource().getFullPath());
+ }
+ return false;
+ }
+
+ public boolean compare(IResourceVariant base, IResourceVariant remote) {
+ GitResourceVariant gitBase = (GitResourceVariant) base;
+ GitResourceVariant gitRemote = (GitResourceVariant) remote;
+ IResource resourceBase = gitBase.getResource();
+ IResource resourceRemote = gitRemote.getResource();
+
+ if (!resourceBase.exists() || !resourceRemote.exists()) {
+ return false;
+ }
+
+ if (base.isContainer()) {
+ if (remote.isContainer()) {
+ return resourceBase.getFullPath().equals(
+ resourceRemote.getFullPath());
+ }
+ return false;
+ } else if (remote.isContainer()) {
+ return false;
+ }
+
+ GitBlobResourceVariant baseBlob = (GitBlobResourceVariant) base;
+ GitBlobResourceVariant remoteBlob = (GitBlobResourceVariant) remote;
+ return baseBlob.getId().equals(remoteBlob.getId());
+ }
+
+ public boolean isThreeWay() {
+ return true;
+ }
+
+ private InputStream getLocal(IResource resource) throws CoreException {
+ if (gsd.getData(resource.getProject()).shouldIncludeLocal()) {
+ return ((IFile) resource).getContents();
+ } else {
+ try {
+ byte[] bytes = store.getBytes(resource);
+ return new ByteArrayInputStream(bytes != null ? bytes : new byte[0]);
+ } catch (TeamException e) {
+ throw new CoreException(e.getStatus());
+ }
+ }
+
+ }
+
+ private void logException(Exception e) {
+ IStatus error = new Status(IStatus.ERROR, Activator
+ .getPluginId(), e.getMessage(), e);
+ Activator.getDefault().getLog().log(error);
+ }
+
+ private void closeStream(InputStream stream) {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ logException(e);
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantTree.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantTree.java
new file mode 100644
index 0000000..bf316a6
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantTree.java
@@ -0,0 +1,425 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeData;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.jgit.lib.AbstractIndexTreeVisitor;
+import org.eclipse.jgit.lib.IndexTreeWalker;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Tree;
+import org.eclipse.jgit.lib.TreeEntry;
+import org.eclipse.jgit.lib.GitIndex.Entry;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevCommitList;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.variants.AbstractResourceVariantTree;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.core.variants.ResourceVariantByteStore;
+
+abstract class GitResourceVariantTree extends AbstractResourceVariantTree {
+
+ /**
+ * A map of a given resource's trail of commits.
+ */
+ private Map<String, RevCommitList<RevCommit>> dates = new HashMap<String, RevCommitList<RevCommit>>();
+
+ /**
+ * A map of a given resource to its latest blob within the branch.
+ */
+ private Map<String, ObjectId> updated = new HashMap<String, ObjectId>();
+
+ /**
+ * A map of repositories to their trees.
+ */
+ private Map<Repository, Tree> trees = new HashMap<Repository, Tree>();
+
+ private GitSynchronizeDataSet gsdData;
+
+ private final ResourceVariantByteStore store;
+
+ GitResourceVariantTree(GitSynchronizeDataSet data,
+ ResourceVariantByteStore store) {
+ this.store = store;
+ this.gsdData = data;
+ }
+
+ public IResource[] roots() {
+ Set<IResource> roots = new HashSet<IResource>();
+ for (GitSynchronizeData gsd : gsdData) {
+ roots.addAll(gsd.getProjects());
+ }
+ return roots.toArray(new IResource[roots.size()]);
+ }
+
+ public IResource[] members(IResource resource) throws TeamException {
+ if (resource.exists() && resource instanceof IContainer) {
+ GitSynchronizeData gsd = getSyncData().getData(
+ resource.getProject());
+ if (gsd.shouldIncludeLocal()) {
+ try {
+ return ((IContainer) resource).members();
+ } catch (CoreException e) {
+ throw new TeamException(e.getStatus());
+ }
+ } else {
+ return getMembersAndStore(resource, gsd);
+ }
+ }
+ return new IResource[0];
+ }
+
+ /**
+ * Returns whether this file is of interest to this resource variant tree.
+ * Due to the fact that a repository may have many, many files, we only want
+ * to retrieve and store information about files that the user is actually
+ * interested in. That is, if they only wish to synchronize on one project,
+ * then there is no reason for this tree to be storing information about
+ * other projects that are contained within the repository.
+ *
+ * @param file
+ * the file to check
+ * @return <code>true</code> if the blob information about this file is of
+ * interest to this tree, <code>false</code> otherwise
+ */
+ private boolean contains(File file) {
+ for (GitSynchronizeData gsd : gsdData) {
+ if (gsd.contains(file)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieves the name of the branch that this variant tree should be
+ * compared against for the given resource.
+ *
+ * @param resource
+ * the resource that is being compared for
+ * @return the name of the target comparison branch
+ * @throws IOException
+ */
+ abstract Tree getRevTree(IResource resource) throws IOException;
+
+ abstract ObjectId getRevObjId(IResource resource) throws IOException;
+
+ /**
+ * Initializes the repository information for the specified resource.
+ *
+ * @param resource
+ * the resource that needs to have its repository information
+ * initialized for
+ * @throws IOException
+ * if an error occurs while walking the branch
+ */
+ private synchronized void initialize(IResource resource) throws IOException {
+ IProject project = resource.getProject();
+ if (!gsdData.contains(project)) {
+ return;
+ }
+
+ Repository db = gsdData.getData(project).getRepository();
+ if (!trees.containsKey(db)) {
+ Tree tree = getRevTree(resource);
+ ObjectId objId = getRevObjId(resource);
+
+ trees.put(db, tree);
+ // walk the tree to retrieve information
+ walk(db, objId, tree);
+ }
+ }
+
+ private void walk(final Repository db, final ObjectId objId, Tree merge)
+ throws IOException {
+ IndexTreeWalker walker = new IndexTreeWalker(db.getIndex(), merge, db
+ .getWorkDir(), new AbstractIndexTreeVisitor() {
+ public void visitEntry(TreeEntry treeEntry, Entry indexEntry,
+ File file) throws IOException {
+ if (treeEntry != null && contains(file)) {
+ store(db, objId, treeEntry);
+ }
+ }
+ });
+ walker.walk();
+ }
+
+ private void store(Repository db, ObjectId objId, TreeEntry treeEntry)
+ throws IOException {
+ String entry = treeEntry.getFullName();
+ RevWalk walk = new RevWalk(db);
+ walk.sort(RevSort.COMMIT_TIME_DESC, true);
+ walk.sort(RevSort.BOUNDARY, true);
+ walk.markStart(walk.parseCommit(objId));
+ walk.setTreeFilter(AndTreeFilter.create(PathFilterGroup
+ .createFromStrings(Collections.singleton(entry)),
+ TreeFilter.ANY_DIFF));
+
+ RevCommitList<RevCommit> list = new RevCommitList<RevCommit>();
+ list.source(walk);
+
+ int lastSize = 0;
+ do {
+ lastSize = list.size();
+ list.fillTo(Integer.MAX_VALUE);
+ } while (lastSize != list.size());
+
+ dates.put(entry, list);
+ updated.put(entry, treeEntry.getId());
+ }
+
+ public IResourceVariant getResourceVariant(IResource resource)
+ throws TeamException {
+ return fetchVariant(resource, 0, new NullProgressMonitor());
+ }
+
+ private IResourceVariant findFolderVariant(IResource resource,
+ Repository repository) {
+ File workDir = repository.getWorkDir();
+ File resourceLocation = resource.getLocation().toFile();
+ String resLocationAbsolutePath = resourceLocation.getAbsolutePath();
+
+ for (Map.Entry<String, ObjectId> entry : updated.entrySet()) {
+ String entryName = entry.getKey();
+ File file = new File(workDir, entryName);
+
+ if (file.getAbsolutePath().startsWith(resLocationAbsolutePath)) {
+ return new GitFolderResourceVariant(resource);
+ }
+
+ }
+
+ return null;
+ }
+
+ private IResourceVariant findFileVariant(IResource resource,
+ Repository repository) throws TeamException {
+ String gitPath = RepositoryMapping.getMapping(resource)
+ .getRepoRelativePath(resource);
+ ObjectId objectId = updated.get(gitPath);
+ if (objectId != null) {
+ File root = repository.getWorkDir();
+ File file = new File(root, gitPath);
+
+ if (resource.getLocation().toFile().equals(file)) {
+ try {
+ Tree merge = trees.get(repository);
+ TreeEntry tree = merge.findBlobMember(gitPath);
+ GitBlobResourceVariant variant = new GitBlobResourceVariant(
+ resource, repository, tree.getId(), dates
+ .get(gitPath));
+ return variant;
+ } catch (IOException e) {
+ throw new TeamException(new Status(IStatus.ERROR, Activator
+ .getPluginId(), NLS.bind(
+ CoreText.GitResourceVariantTree_couldNotFindBlob,
+ gitPath), e));
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean hasResourceVariant(IResource resource) throws TeamException {
+ return getResourceVariant(resource) != null;
+ }
+
+ public void flushVariants(IResource resource, int depth)
+ throws TeamException {
+ // nothing do to here
+ // TODO implement ?
+ }
+
+ @Override
+ protected IResourceVariant[] fetchMembers(IResourceVariant variant,
+ IProgressMonitor progress) throws TeamException {
+ if (!variant.isContainer()) {
+ return new IResourceVariant[0];
+ }
+
+ IProgressMonitor monitor = SubMonitor.convert(progress);
+
+ Set<IResourceVariant> members = new HashSet<IResourceVariant>();
+ try {
+ GitFolderResourceVariant folderVariant = (GitFolderResourceVariant) variant;
+ IContainer container = folderVariant.getContainer();
+ File resourceLocation = container.getLocation().toFile();
+ IProject project = container.getProject();
+
+ Repository repository = gsdData.getData(project).getRepository();
+
+ monitor.beginTask(NLS.bind(
+ CoreText.GitResourceVariantTree_fetchingMembers, container
+ .getLocation()), updated.size());
+ File root = repository.getWorkDir();
+
+ for (Map.Entry<String, ObjectId> entry : updated.entrySet()) {
+ String entryName = entry.getKey();
+ File file = new File(root, entryName);
+
+ if (file.getAbsolutePath().startsWith(
+ resourceLocation.getAbsolutePath())) {
+ members.add(getMember(container, repository, entryName));
+ }
+
+ monitor.worked(1);
+ }
+ } finally {
+ monitor.done();
+ }
+ return members.toArray(new IResourceVariant[members.size()]);
+ }
+
+ private IResourceVariant getMember(IContainer container,
+ Repository repository, String entryName) throws TeamException {
+ String gitPath = RepositoryMapping.getMapping(container)
+ .getRepoRelativePath(container);
+ Tree merge = trees.get(repository);
+ try {
+ TreeEntry tree = merge.findBlobMember(entryName);
+ GitBlobResourceVariant blobVariant = new GitBlobResourceVariant(
+ container.getFile(new Path(entryName)), repository, tree
+ .getId(), dates.get(entryName));
+ return blobVariant;
+ } catch (IOException e) {
+ throw new TeamException(new Status(IStatus.ERROR, Activator
+ .getPluginId(), NLS.bind(
+ CoreText.GitResourceVariantTree_couldNotFindBlob, gitPath),
+ e));
+ }
+ }
+
+ private IResourceVariant fetchVariant(IResource resource,
+ IProgressMonitor monitor) throws TeamException {
+ try {
+ monitor.beginTask(NLS.bind(
+ CoreText.GitResourceVariantTree_fetchingVariant, resource
+ .getLocation()), 5);
+ initialize(resource);
+ monitor.worked(4);
+ } catch (IOException e) {
+ throw new TeamException(new Status(IStatus.ERROR, Activator
+ .getPluginId(), NLS.bind(
+ CoreText.GitResourceVariantTree_unableToReadRepository,
+ resource.getName()), e));
+ }
+
+ Repository repository = gsdData.getData(resource.getProject())
+ .getRepository();
+
+ if (resource instanceof IProject) {
+ return new GitFolderResourceVariant(resource);
+ } else if (resource instanceof IFolder) {
+ return findFolderVariant(resource, repository);
+ }
+
+ return findFileVariant(resource, repository);
+ }
+
+ @Override
+ protected IResourceVariant fetchVariant(IResource resource, int depth,
+ IProgressMonitor monitor) throws TeamException {
+ try {
+ return fetchVariant(resource, monitor);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ @Override
+ protected boolean setVariant(IResource local, IResourceVariant remote)
+ throws TeamException {
+ return true;
+ }
+
+ protected GitSynchronizeDataSet getSyncData() {
+ return gsdData;
+ }
+
+ private IResource[] getMembersAndStore(IResource resource,
+ GitSynchronizeData gsd) throws TeamException {
+ Repository repo = gsd.getRepository();
+ try {
+ Tree tree = gsd.mapSrcTree();
+ IResource[] members = ((IContainer) resource).members();
+ Set<IResource> membersSet = getAllMembers(repo, tree, members);
+
+ return membersSet.toArray(new IResource[membersSet.size()]);
+ } catch (IOException e) {
+ throw new TeamException(e.getMessage(), e);
+ } catch (CoreException e) {
+ throw TeamException.asTeamException(e);
+ }
+ }
+
+ private Set<IResource> getAllMembers(Repository repo, Tree tree,
+ IResource[] members) throws IOException, TeamException {
+ Set<IResource> membersSet = new HashSet<IResource>();
+
+ for (IResource member : members) {
+ if (member.getType() == IResource.FILE) {
+ String repoWorkDir = repo.getWorkDir().toString();
+ String memberRelPath = member.getLocation().toString();
+ memberRelPath = memberRelPath.replace(repoWorkDir, ""); //$NON-NLS-1$
+ if (memberRelPath.startsWith(File.separator)) {
+ memberRelPath = memberRelPath.substring(1);
+ }
+ TreeEntry entry = tree.findBlobMember(memberRelPath);
+ if (entry != null) {
+ ObjectLoader objLoader = repo.openBlob(entry.getId());
+ store.setBytes(member, objLoader.getCachedBytes());
+ membersSet.add(member);
+ }
+ } else if (member.getType() == IResource.FOLDER ) {
+ try {
+ IResource[] resources = ((IContainer) member).members();
+ membersSet.addAll(getAllMembers(repo, tree, resources));
+ } catch (CoreException e) {
+ throw new TeamException(e.getStatus());
+ }
+ }
+ }
+ return membersSet;
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantTreeSubscriber.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantTreeSubscriber.java
new file mode 100644
index 0000000..780f45b
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitResourceVariantTreeSubscriber.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.core.variants.IResourceVariantComparator;
+import org.eclipse.team.core.variants.IResourceVariantTree;
+import org.eclipse.team.core.variants.ResourceVariantByteStore;
+import org.eclipse.team.core.variants.ResourceVariantTreeSubscriber;
+
+/**
+ *
+ */
+public class GitResourceVariantTreeSubscriber extends
+ ResourceVariantTreeSubscriber {
+
+ private final ResourceVariantByteStore store;
+
+ /**
+ * A resource variant tree of the remote branch(es).
+ */
+ private IResourceVariantTree remoteTree;
+
+ /**
+ * A resource variant tree against HEAD.
+ */
+ private IResourceVariantTree baseTree;
+
+ private GitSynchronizeDataSet gitSynchronizeDataSet;
+
+ /**
+ * @param data
+ * @param store
+ */
+ public GitResourceVariantTreeSubscriber(GitSynchronizeDataSet data,
+ ResourceVariantByteStore store) {
+ this.store = store;
+ this.gitSynchronizeDataSet = data;
+ }
+
+ private IResource[] roots;
+
+ @Override
+ public boolean isSupervised(IResource resource) throws TeamException {
+ return true; //gitSynchronizeDataSet.contains(resource.getProject());
+ }
+
+ @Override
+ public IResource[] roots() {
+ if (roots != null) {
+ return roots;
+ }
+
+ roots = gitSynchronizeDataSet.getAllResources();
+ return roots;
+ }
+
+ /**
+ * @param data
+ */
+ public void reset(GitSynchronizeDataSet data) {
+ gitSynchronizeDataSet = data;
+ store.dispose();
+
+ roots = null;
+ baseTree = null;
+ remoteTree = null;
+ }
+
+ @Override
+ public IResource[] members(IResource resource) throws TeamException {
+ return getBaseTree().members(resource);
+ }
+
+ @Override
+ public String getName() {
+ return CoreText.GitBranchResourceVariantTreeSubscriber_gitRepository;
+ }
+
+ @Override
+ public IResourceVariantComparator getResourceComparator() {
+ return new GitResourceVariantComparator(gitSynchronizeDataSet, store);
+ }
+
+ @Override
+ protected IResourceVariantTree getBaseTree() {
+ if (baseTree == null) {
+ baseTree = new GitBaseResourceVariantTree(gitSynchronizeDataSet, store);
+ }
+ return baseTree;
+ }
+
+ @Override
+ protected IResourceVariantTree getRemoteTree() {
+ if (remoteTree == null) {
+ remoteTree = new GitRemoteResourceVariantTree(gitSynchronizeDataSet, store);
+ }
+ return remoteTree;
+ }
+
+ @Override
+ protected SyncInfo getSyncInfo(IResource local, IResourceVariant base,
+ IResourceVariant remote) throws TeamException {
+ GitSyncInfo info = new GitSyncInfo(local, base, remote,
+ getResourceComparator());
+ info.init();
+ return info;
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitSyncInfo.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitSyncInfo.java
new file mode 100644
index 0000000..2898cbc
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitSyncInfo.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevCommitList;
+import org.eclipse.team.core.Team;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.core.variants.IResourceVariantComparator;
+
+class GitSyncInfo extends SyncInfo {
+
+ GitSyncInfo(IResource local, IResourceVariant base,
+ IResourceVariant remote, IResourceVariantComparator comparator) {
+ super(local, base, remote, comparator);
+ }
+
+ @Override
+ protected int calculateKind() throws TeamException {
+ IResource local = getLocal();
+ IResourceVariantComparator comparator = getComparator();
+ GitResourceVariant base = (GitResourceVariant) getBase();
+ GitResourceVariant remote = (GitResourceVariant) getRemote();
+
+ int description = IN_SYNC;
+
+ boolean localExists = local.exists();
+
+ if (Team.isIgnoredHint(local)) {
+ // assume that ignored and transient files are always synchronized
+ // in sync
+ } else if (base == null) {
+ description = hadnleNullBase(local, comparator, remote);
+ } else {
+ if (!localExists) {
+ description = handleLocalDoesntExist(comparator, base, remote);
+ } else {
+ if (remote == null) {
+ if (comparator.compare(local, base))
+ description = INCOMING | DELETION;
+ else
+ description = CONFLICTING | CHANGE;
+ } else {
+ description = calculateDescrBasedOnGitData(local, base,
+ remote);
+ }
+ }
+ }
+
+ return description;
+ }
+
+ private int hadnleNullBase(IResource local,
+ IResourceVariantComparator comparator, GitResourceVariant remote) {
+ int description;
+ boolean localExists = local.exists();
+
+ if (remote == null) {
+ if (!localExists) {
+ description = IN_SYNC;
+ } else {
+ description = OUTGOING | ADDITION;
+ }
+ } else {
+ if (!localExists) {
+ description = INCOMING | ADDITION;
+ } else {
+ description = CONFLICTING | ADDITION;
+ if (comparator.compare(local, remote)) {
+ description |= PSEUDO_CONFLICT;
+ }
+ }
+ }
+
+ return description;
+ }
+
+ private int handleLocalDoesntExist(IResourceVariantComparator comparator,
+ GitResourceVariant base, GitResourceVariant remote) {
+ int description;
+
+ if (remote == null) {
+ description = CONFLICTING | DELETION | PSEUDO_CONFLICT;
+ } else {
+ if (comparator.compare(base, remote))
+ description = OUTGOING | DELETION;
+ else
+ description = CONFLICTING | CHANGE;
+ }
+
+ return description;
+ }
+
+ private int calculateDescrBasedOnGitData(IResource local,
+ GitResourceVariant base, GitResourceVariant remote) {
+ int description = IN_SYNC;
+
+ if (base instanceof GitBlobResourceVariant) {
+ if (remote instanceof GitBlobResourceVariant) {
+ if (getComparator().compare(local, base)) {
+ GitBlobResourceVariant baseBlob = (GitBlobResourceVariant) base;
+ GitBlobResourceVariant remoteBlob = (GitBlobResourceVariant) remote;
+
+ if (!baseBlob.getId().equals(remoteBlob.getId())) {
+ description = calculateDescBasedOnGitCommits(baseBlob,
+ remoteBlob);
+ }
+ } else {
+ description = OUTGOING | CHANGE;
+ }
+ } else {
+ // file in local, folder on remote branch
+ description = CONFLICTING | CHANGE;
+ }
+ } else if (base.isContainer()) {
+ if (remote.isContainer()) {
+ description = compareTwoContainers(base, remote);
+ } else {
+ // folder in local, file in remote branch
+ description = CONFLICTING | CHANGE;
+ }
+ }
+
+ return description;
+ }
+
+ private int calculateDescBasedOnGitCommits(GitBlobResourceVariant baseBlob,
+ GitBlobResourceVariant remoteBlob) {
+
+ RevCommitList<RevCommit> baseList = baseBlob.getCommitList();
+ RevCommitList<RevCommit> remoteList = remoteBlob.getCommitList();
+ RevCommit recentRemoteCommit = remoteList.get(0);
+ RevCommit recentBaseCommit = baseList.get(0);
+
+ // TODO can we implement it better ?
+ for (RevCommit baseCommit : baseList) {
+ if (recentRemoteCommit.name().equals(baseCommit.name())) {
+ return OUTGOING | CHANGE;
+ }
+ }
+
+ // TODO can we implement it better ?
+ for (RevCommit remoteCommit : remoteList) {
+ if (recentBaseCommit.name().equals(remoteCommit.name())) {
+ return INCOMING | CHANGE;
+ }
+ }
+
+ return CONFLICTING | CHANGE;
+ }
+
+ private int compareTwoContainers(GitResourceVariant base,
+ GitResourceVariant remote) {
+ final int description;
+ boolean baseExists = base.getResource().exists();
+ boolean remoteExists = remote.getResource().exists();
+
+ if (baseExists && remoteExists) {
+ // there are no changes
+ description = IN_SYNC;
+ } else if (baseExists && remoteExists) {
+ // folder doesn't exist locally but it exists in
+ // common accessor and remote
+ description = INCOMING | ADDITION;
+ } else {
+ // in all other cases
+ description = CONFLICTING | CHANGE;
+ }
+
+ return description;
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/dto/GitSynchronizeData.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/dto/GitSynchronizeData.java
new file mode 100644
index 0000000..1233a36
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/dto/GitSynchronizeData.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Dariusz Luksza <dariusz@luksza.org>
+ *
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize.dto;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Tag;
+import org.eclipse.jgit.lib.Tree;
+
+/**
+ *
+ */
+public class GitSynchronizeData {
+
+ private final boolean includeLocal;
+
+ private final Repository repo;
+
+ private final String srcRev;
+
+ private final String dstRev;
+
+ private final Set<IProject> projects;
+
+ private final String repoParentPath;
+
+ /**
+ * @param repository
+ * @param srcRef
+ * @param dstRef
+ * @param project
+ * @param includeLocal <code>true</code> if local changes should be included in comparison
+ */
+ public GitSynchronizeData(Repository repository, String srcRef,
+ String dstRef, IProject project, boolean includeLocal) {
+ this(repository, srcRef, dstRef, new HashSet<IProject>(Arrays
+ .asList(project)), includeLocal);
+ }
+
+ /**
+ * @param repository
+ * @param srcRev
+ * @param dstRev
+ * @param projects
+ * @param includeLocal <code>true</code> if local changes should be included in comparison
+ */
+ public GitSynchronizeData(Repository repository, String srcRev,
+ String dstRev, Set<IProject> projects, boolean includeLocal) {
+ repo = repository;
+ this.srcRev = srcRev;
+ this.dstRev = dstRev;
+ this.projects = projects;
+ this.includeLocal = includeLocal;
+ repoParentPath = repo.getDirectory().getParentFile().getAbsolutePath();
+ }
+
+ /**
+ * @return instance of repository that should be synchronized
+ */
+ public Repository getRepository() {
+ return repo;
+ }
+
+ /**
+ * @return synchronize source rev name
+ */
+ public String getSrcRev() {
+ return srcRev;
+ }
+
+ /**
+ * @return synchronize destination rev name
+ */
+ public String getDstRev() {
+ return dstRev;
+ }
+
+ /**
+ * @return source {@link Tree}
+ * @throws IOException
+ */
+ public Tree mapSrcTree() throws IOException {
+ return mapTree(srcRev);
+ }
+
+ /**
+ * @return destination {@link Tree}
+ * @throws IOException
+ */
+ public Tree mapDstTree() throws IOException {
+ return mapTree(dstRev);
+ }
+
+ /**
+ * @return source {@link ObjectId}
+ * @throws IOException
+ */
+ public ObjectId getSrcObjectId() throws IOException {
+ return getObjecId(srcRev);
+ }
+
+ /**
+ * @return destination {@link ObjectId}
+ * @throws IOException
+ */
+ public ObjectId getDstObjectId() throws IOException {
+ return getObjecId(dstRev);
+ }
+
+ /**
+ * @return list of project's that are connected with this repository
+ */
+ public Set<IProject> getProjects() {
+ return Collections.unmodifiableSet(projects);
+ }
+
+ /**
+ *
+ * @param resource
+ * @return <true> if given {@link IResource} is contained by this repository
+ */
+ public boolean contains(IResource resource) {
+ return resource.getFullPath().toString().startsWith(repoParentPath);
+ }
+
+ /**
+ * @param file
+ * @return <true> if given {@link File} is contained by this repository
+ */
+ public boolean contains(File file) {
+ return file.getAbsoluteFile().toString().startsWith(repoParentPath);
+ }
+
+ /**
+ * @return <code>true</code> if local changes should be included in comparison
+ */
+ public boolean shouldIncludeLocal() {
+ return includeLocal;
+ }
+
+ private Tree mapTree(String rev) throws IOException {
+ if (rev.startsWith(Constants.R_TAGS)) {
+ Tag tag = repo.mapTag(rev);
+ Commit commit = repo.mapCommit(tag.getObjId());
+ return commit.getTree();
+ } else {
+ return repo.mapTree(rev);
+ }
+ }
+
+ private ObjectId getObjecId(String rev) throws IOException {
+ if (rev.startsWith(Constants.R_TAGS)) {
+ return repo.mapTag(rev).getObjId();
+ } else {
+ return repo.mapCommit(rev).getCommitId();
+ }
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/dto/GitSynchronizeDataSet.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/dto/GitSynchronizeDataSet.java
new file mode 100644
index 0000000..1a586bb
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/dto/GitSynchronizeDataSet.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Dariusz Luksza <dariusz@luksza.org>
+ *
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.synchronize.dto;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+
+/**
+ *
+ */
+public class GitSynchronizeDataSet implements Iterable<GitSynchronizeData> {
+
+ private final Set<GitSynchronizeData> gsd;
+
+ private final Map<IProject, GitSynchronizeData> projectMapping;
+
+ /**
+ * Constructs GitSynchronizeDataSet.
+ */
+ public GitSynchronizeDataSet() {
+ gsd = new HashSet<GitSynchronizeData>();
+ projectMapping = new HashMap<IProject, GitSynchronizeData>();
+ }
+
+ /**
+ * Constructs GitSynchronizeDataSet and adds given element to set.
+ *
+ * @param data
+ */
+ public GitSynchronizeDataSet(GitSynchronizeData data) {
+ this();
+ add(data);
+ }
+
+ /**
+ * @param data
+ */
+ public void add(GitSynchronizeData data) {
+ gsd.add(data);
+ for (IProject proj : data.getProjects()) {
+ projectMapping.put(proj, data);
+ }
+ }
+
+ /**
+ * @param project
+ * @return <code>true</code> if project has corresponding data
+ */
+ public boolean contains(IProject project) {
+ return projectMapping.containsKey(project);
+ }
+
+ /**
+ * @param project
+ * @return <code>null</code> if project does not have corresponding data
+ */
+ public GitSynchronizeData getData(IProject project) {
+ return projectMapping.get(project);
+ }
+
+ public Iterator<GitSynchronizeData> iterator() {
+ return gsd.iterator();
+ }
+
+ /**
+ * @return list of all resources
+ */
+ public IResource[] getAllResources() {
+ Set<IResource> resource = new HashSet<IResource>();
+ for (GitSynchronizeData data : gsd) {
+ resource.addAll(data.getProjects());
+ }
+ return resource.toArray(new IResource[resource.size()]);
+ }
+
+}
diff --git a/org.eclipse.egit.ui/META-INF/MANIFEST.MF b/org.eclipse.egit.ui/META-INF/MANIFEST.MF
index 0a0a840..c9f293d 100644
--- a/org.eclipse.egit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.egit.ui/META-INF/MANIFEST.MF
@@ -32,6 +32,8 @@ Import-Package: org.eclipse.egit.core;version="[0.9.0,0.10.0)",
org.eclipse.egit.core.internal.util;version="[0.9.0,0.10.0)",
org.eclipse.egit.core.op;version="[0.9.0,0.10.0)",
org.eclipse.egit.core.project;version="[0.9.0,0.10.0)",
+ org.eclipse.egit.core.synchronize;version="[0.9.0,0.10.0)",
+ org.eclipse.egit.core.synchronize.dto;version="[0.9.0,0.10.0)",
org.eclipse.jgit.diff;version="[0.9.0,0.10.0)",
org.eclipse.jgit.dircache;version="[0.9.0,0.10.0)",
org.eclipse.jgit.errors;version="[0.9.0,0.10.0)",
@@ -47,3 +49,4 @@ Import-Package: org.eclipse.egit.core;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)",
org.eclipse.jgit.util;version="[0.9.0,0.10.0)",
org.eclipse.jgit.util.io;version="[0.9.0,0.10.0)"
+
diff --git a/org.eclipse.egit.ui/plugin.properties b/org.eclipse.egit.ui/plugin.properties
index d4b0d74..0ee9988 100644
--- a/org.eclipse.egit.ui/plugin.properties
+++ b/org.eclipse.egit.ui/plugin.properties
@@ -72,7 +72,11 @@ MergeAction_tooltip=Fast-forward Merge with another branch
TagAction_label=&Tag...
TagAction_tooltip=Create or edit tag
+SynchronizeWith_label=&Synchronize...
+SynchronizeWith_toolTip=Synchronize with selected remote or local repository
+
FetchAction_label=&Fetch...
+FetchAction_label=&Fetch From...
FetchAction_tooltip=Fetch from another repository
PushAction_label=&Push...
@@ -127,6 +131,7 @@ ShareProjectCommandParameter_name = Project
GitRepositoriesView_name = Git Repositories
GitCategory_name = Git
GitRepositoryPerspective_name = Git Repository Exploring
+Synchronize_Name=Git
ApplyPatchAction_label=Appl&y Patch...
GitRepositoriesContentName = Git Repositories
@@ -153,4 +158,5 @@ RemovePushCommand = Delete Push
RemoveFetchCommand = Delete Fetch
OpenInEditorCommand = Open in Text Editor
OpenCommand = Open
-LinkWithSelectionCommand = Link with Selection \ No newline at end of file
+LinkWithSelectionCommand = Link with Selection
+SynchronizeCommand = Synchronize...
diff --git a/org.eclipse.egit.ui/plugin.xml b/org.eclipse.egit.ui/plugin.xml
index 010dd85..0b9763c 100644
--- a/org.eclipse.egit.ui/plugin.xml
+++ b/org.eclipse.egit.ui/plugin.xml
@@ -59,15 +59,22 @@
icon="$nl$/icons/obj16/reset.gif"
id="org.eclipse.egit.ui.internal.actions.ResetAction"
label="%ResetAction_label"
- menubarPath="team.main/group4"
+ menubarPath="team.main/group5"
tooltip="%ResetAction_tooltip">
</action>
<action
+ class="org.eclipse.egit.ui.internal.actions.SynchronizeWithAction"
+ id="org.eclipse.egit.ui.internal.actions.SynchronizeWithAction"
+ icon="$nl$/icons/obj16/remotespec.gif"
+ label="%SynchronizeWith_label"
+ menubarPath="team.main/group3"
+ tooltip="%SynchronizeWith_tooltip"/>
+ <action
class="org.eclipse.egit.ui.internal.actions.TagAction"
icon="$nl$/icons/obj16/tags.gif"
id="org.eclipse.egit.ui.internal.actions.TagAction"
label="%TagAction_label"
- menubarPath="team.main/group4"
+ menubarPath="team.main/group5"
tooltip="%TagAction_tooltip"/>
<action
class="org.eclipse.egit.ui.internal.actions.MergeAction"
@@ -81,7 +88,7 @@
icon="$nl$/icons/obj16/branch_obj.gif"
id="org.eclipse.egit.ui.internal.actions.BranchAction"
label="%BranchAction_label"
- menubarPath="team.main/group4"
+ menubarPath="team.main/group5"
tooltip="%BranchAction_tooltip"/>
</objectContribution>
<objectContribution
@@ -117,7 +124,7 @@
icon="$nl$/icons/obj16/refresh.gif"
id="org.eclipse.egit.ui.internal.actions.Update"
label="%UpdateAction_label"
- menubarPath="team.main/group3"
+ menubarPath="team.main/group4"
tooltip="%UpdateAction_tooltip"/>
<action
class="org.eclipse.egit.ui.internal.actions.CommitAction"
@@ -163,7 +170,7 @@
enablesFor="*"
id="org.eclipse.egit.ui.internal.actions.applyPatch"
label="%ApplyPatchAction_label"
- menubarPath="team.main/group5"
+ menubarPath="team.main/group6"
overrideActionId="org.eclipse.team.ui.applyPatch">
</action>
</objectContribution>
@@ -725,6 +732,40 @@
</visibleWhen>
</command>
<command
+ commandId="org.eclipse.egit.ui.RepositoriesViewSynchronize"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <and>
+ <or>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.TagNode">
+ </instanceof>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.RefNode">
+ </instanceof>
+ </or>
+ <not>
+ <test
+ property="GitRepository.isRefCheckedOut">
+ </test>
+ </not>
+ <not>
+ <test
+ property="GitRepository.isBare">
+ </test>
+ </not>
+ </and>
+ </iterate>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
commandId="org.eclipse.egit.ui.RepositoriesViewFetchConfigured"
style="push">
<visibleWhen
@@ -1253,6 +1294,13 @@
class="org.eclipse.egit.ui.internal.repository.tree.command.LinkWithSelectionCommand">
</defaultHandler>
</command>
+ <command
+ id="org.eclipse.egit.ui.RepositoriesViewSynchronize"
+ name="%SynchronizeCommand">
+ <defaultHandler
+ class="org.eclipse.egit.ui.internal.repository.tree.command.SynchronizeCommand">
+ </defaultHandler>
+ </command>
</extension>
<extension
point="org.eclipse.ui.commandImages">
@@ -1306,5 +1354,25 @@
</instanceof>
</selectionEnablement>
</linkHelper>
+ </extension>
+ <extension
+ point="org.eclipse.team.ui.synchronizeWizards">
+ <wizard
+ class="org.eclipse.egit.ui.internal.synchronize.GitSynchronizeWizard"
+ description="description"
+ icon="icons/obj16/gitrepository.gif"
+ id="org.eclipse.egit.ui.wizard1"
+ name="%Synchronize_Name">
+ </wizard>
+ </extension>
+ <extension
+ point="org.eclipse.team.ui.synchronizeParticipants">
+ <participant
+ class="org.eclipse.egit.ui.internal.synchronize.GitSubscriberParticipant"
+ id="org.eclipse.egit.ui.synchronizeParticipant"
+ icon="icons/obj16/gitrepository.gif"
+ name="%Synchronize_Name"
+ persistent="false">
+ </participant>
</extension>
</plugin>
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java
index 66275dc..0d7ea0c 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java
@@ -1564,6 +1564,9 @@ public class UIText extends NLS {
public static String RepositoriesView_CheckingOutMessage;
/** */
+ public static String RepositoriesView_Synchronize_MenuItem;
+
+ /** */
public static String RepositoriesView_CheckOut_MenuItem;
/** */
@@ -2017,6 +2020,60 @@ public class UIText extends NLS {
*/
private static final String BUNDLE_NAME = "org.eclipse.egit.ui.uitext"; //$NON-NLS-1$
+ /** */
+ public static String CommitAction_commit;
+
+ /** */
+ public static String GitSynchronizeWizard_synchronize;
+
+ /** */
+ public static String GitSynchronizeWizard_gitResourceSynchronization;
+
+ /** */
+ public static String GitBranchSynchronizeWizardPage_title;
+
+ /** */
+ public static String GitBranchSynchronizeWizardPage_description;
+
+ /** */
+ public static String GitBranchSynchronizeWizardPage_repositories;
+
+ /** */
+ public static String GitBranchSynchronizeWizardPage_branches;
+
+ /** */
+ public static String GitBranchSynchronizeWizardPage_selectAll;
+
+ /** */
+ public static String GitBranchSynchronizeWizardPage_deselectAll;
+
+ /** */
+ public static String GitBranchSubscriberParticipant_git;
+
+ /** */
+ public static String RemoteSelectionCombo_remoteName;
+
+ /** */
+ public static String RemoteSelectionCombo_remoteRef;
+
+ /** */
+ public static String SelectSynchronizeResourceDialog_header;
+
+ /** */
+ public static String SelectSynchronizeResourceDialog_selectProject;
+
+ /** */
+ public static String SelectSynchronizeResourceDialog_srcRef;
+
+ /** */
+ public static String SelectSynchronizeResourceDialog_dstRef;
+
+ /** */
+ public static String SelectSynchronizeResourceDialog_includeUncommitedChnages;
+
+ /** */
+ public static String SynchronizeWithAction_localRepoName;
+
static {
initializeMessages(BUNDLE_NAME, UIText.class);
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/RepositoryAction.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/RepositoryAction.java
index 9fca0df..40ac4d0 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/RepositoryAction.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/RepositoryAction.java
@@ -76,6 +76,22 @@ public abstract class RepositoryAction extends TeamAction {
}
/**
+ * @param repo
+ * @return set of project connected with this particular repository
+ */
+ protected Set<IProject> getProcjetsInRepository(Repository repo) {
+ Set<IProject> ret = new HashSet<IProject>();
+ final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ for (IProject project : projects) {
+ RepositoryMapping mapping = RepositoryMapping.getMapping(project);
+ if (mapping != null && mapping.getRepository() == repo) {
+ ret.add(project);
+ }
+ }
+ return ret;
+ }
+
+ /**
* Figure out which repository to use. All selected
* resources must map to the same Git repository.
*
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SynchronizeWithAction.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SynchronizeWithAction.java
new file mode 100644
index 0000000..d7a781e
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SynchronizeWithAction.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Dariusz Luksza <dariusz@luksza.org>
+ *
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.actions;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeData;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.internal.synchronize.GitSynchronize;
+import org.eclipse.egit.ui.internal.synchronize.SelectSynchronizeResourceDialog;
+import org.eclipse.egit.ui.internal.synchronize.SyncRepoEntity;
+import org.eclipse.egit.ui.internal.synchronize.SyncRepoEntity.SyncRefEntity;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.RemoteConfig;
+
+/**
+ * An action that launch synchronization with selected repository
+ */
+public class SynchronizeWithAction extends RepositoryAction {
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ protected void execute(IAction action) throws InvocationTargetException,
+ InterruptedException {
+ Repository[] repos = getRepositories();
+
+ if (repos.length != repos.length) {
+ return;
+ }
+
+ GitSynchronizeDataSet gsdSet = new GitSynchronizeDataSet();
+ for (Repository repo : repos) {
+ List<SyncRepoEntity> syncRepoEntitys = createSyncRepoEntitys(repo);
+ SelectSynchronizeResourceDialog dialog = new SelectSynchronizeResourceDialog(
+ getShell(), repo.getDirectory(), syncRepoEntitys);
+
+ if (dialog.open() != IDialogConstants.OK_ID)
+ return;
+
+ gsdSet.add(new GitSynchronizeData(repo, dialog.getSrcRef(), dialog
+ .getDstRef(), getProcjetsInRepository(repo), dialog.shouldIncludeLocal()));
+ }
+
+ new GitSynchronize(gsdSet);
+ }
+
+ private List<SyncRepoEntity> createSyncRepoEntitys(Repository repo)
+ throws InvocationTargetException {
+ RefDatabase refDatabase = repo.getRefDatabase();
+ List<RemoteConfig> remoteConfigs = getRemoteConfigs(repo);
+ List<SyncRepoEntity> syncRepoEntitys = new ArrayList<SyncRepoEntity>();
+
+ syncRepoEntitys.add(getLocalSyncRepo(repo));
+ for (RemoteConfig rc : remoteConfigs) {
+ syncRepoEntitys.add(getRemoteSyncRepo(refDatabase, rc));
+ }
+ return syncRepoEntitys;
+ }
+
+ private List<RemoteConfig> getRemoteConfigs(Repository repo)
+ throws InvocationTargetException {
+ try {
+ return RemoteConfig.getAllRemoteConfigs(repo.getConfig());
+ } catch (URISyntaxException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ private SyncRepoEntity getLocalSyncRepo(Repository repo) {
+ Set<String> allRefs = repo.getAllRefs().keySet();
+ SyncRepoEntity local = new SyncRepoEntity(
+ UIText.SynchronizeWithAction_localRepoName);
+ for (String ref : allRefs) {
+ if (!ref.startsWith(Constants.R_REMOTES)) {
+ String name = ref.substring(ref.lastIndexOf('/') + 1);
+ local.addRef(new SyncRefEntity(name, ref));
+ }
+ }
+ return local;
+ }
+
+ private SyncRepoEntity getRemoteSyncRepo(RefDatabase refDatabase,
+ RemoteConfig rc) throws InvocationTargetException {
+ SyncRepoEntity syncRepoEnt = new SyncRepoEntity(rc.getName());
+ Collection<Ref> remoteRefs = getRemoteRef(refDatabase, rc.getName());
+
+ for (Ref ref : remoteRefs) {
+ String refName = ref.getName();
+ String refHumanName = refName
+ .substring(refName.lastIndexOf('/') + 1);
+ syncRepoEnt.addRef(new SyncRefEntity(refHumanName, refName));
+ }
+ return syncRepoEnt;
+ }
+
+ private Collection<Ref> getRemoteRef(RefDatabase refDb, String remoteName)
+ throws InvocationTargetException {
+ try {
+ return refDb
+ .getRefs(Constants.R_REMOTES + remoteName + "/").values(); //$NON-NLS-1$
+ } catch (IOException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoriesView.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoriesView.java
index 0faa38c..1baa636 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoriesView.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoriesView.java
@@ -7,6 +7,7 @@
*
* Contributors:
* Mathias Kinzler (SAP AG) - initial implementation
+ * Dariusz Luksza <dariusz@luksza.org> - add synchronization feature
*******************************************************************************/
package org.eclipse.egit.ui.internal.repository;
@@ -572,4 +573,5 @@ public class RepositoriesView extends CommonNavigator {
// }
//
// });
+
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SynchronizeCommand.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SynchronizeCommand.java
new file mode 100644
index 0000000..0e38eab
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SynchronizeCommand.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2010 SAP AG.
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Mathias Kinzler (SAP AG) - initial implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.repository.tree.command;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeData;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode;
+import org.eclipse.egit.ui.internal.synchronize.GitSynchronize;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Implements "Synchornize"
+ *
+ */
+public class SynchronizeCommand extends
+ RepositoriesViewCommandHandler<RepositoryTreeNode> {
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ final RepositoryTreeNode node = getSelectedNodes(event).get(0);
+ Object object = node.getObject();
+ if (!(object instanceof Ref)) {
+ return null;
+ }
+
+ final Ref ref = (Ref) object;
+ final Repository repo = node.getRepository();
+ Job job = new Job(NLS.bind(
+ UIText.SelectSynchronizeResourceDialog_selectProject, repo
+ .getDirectory())) {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ Set<IProject> repoProjects = new HashSet<IProject>();
+ final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ for (IProject project : projects) {
+ RepositoryMapping mapping = RepositoryMapping.getMapping(project);
+ if (mapping != null && mapping.getRepository() == repo) {
+ repoProjects.add(project);
+ }
+ }
+
+ GitSynchronizeData data = new GitSynchronizeData(node
+ .getRepository(), Constants.HEAD, ref.getName(),
+ repoProjects, false);
+
+ new GitSynchronize(data);
+
+ return Status.OK_STATUS;
+ }
+ };
+ job.setUser(true);
+ job.schedule();
+
+ return null;
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/CommitAction.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/CommitAction.java
new file mode 100644
index 0000000..097c0fa
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/CommitAction.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.synchronize;
+
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
+import org.eclipse.team.ui.synchronize.SynchronizeModelAction;
+import org.eclipse.team.ui.synchronize.SynchronizeModelOperation;
+
+class CommitAction extends SynchronizeModelAction {
+
+ CommitAction(ISynchronizePageConfiguration configuration) {
+ super(UIText.CommitAction_commit, configuration);
+ }
+
+ @Override
+ protected SynchronizeModelOperation getSubscriberOperation(
+ ISynchronizePageConfiguration configuration, IDiffElement[] elements) {
+ return new CommitOperation(configuration, elements);
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/CommitOperation.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/CommitOperation.java
new file mode 100644
index 0000000..b6658f5
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/CommitOperation.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.synchronize;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.egit.ui.internal.actions.CommitAction;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
+import org.eclipse.team.ui.synchronize.SynchronizeModelOperation;
+
+class CommitOperation extends SynchronizeModelOperation {
+
+ private ISynchronizePageConfiguration configuration;
+
+ CommitOperation(ISynchronizePageConfiguration configuration,
+ IDiffElement[] elements) {
+ super(configuration, elements);
+ this.configuration = configuration;
+ }
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ final CommitAction action = new CommitAction() {
+ @Override
+ protected IStructuredSelection getSelection() {
+ // retrieve the selection from the view directly
+ return new StructuredSelection(getSyncInfoSet().getSyncInfos());
+ }
+ };
+
+ configuration.getSite().getShell().getDisplay().syncExec(new Runnable() {
+ public void run() {
+ // ideally the code in CommitAction should be refactored so it can be
+ // consumed in better ways
+ action.run(null);
+ }
+ });
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSubscriberParticipant.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSubscriberParticipant.java
new file mode 100644
index 0000000..8974032
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSubscriberParticipant.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.synchronize;
+
+import org.eclipse.egit.core.synchronize.GitResourceVariantTreeSubscriber;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.team.core.subscribers.Subscriber;
+import org.eclipse.team.core.variants.ResourceVariantByteStore;
+import org.eclipse.team.core.variants.SessionResourceVariantByteStore;
+import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
+import org.eclipse.team.ui.synchronize.SubscriberParticipant;
+import org.eclipse.team.ui.synchronize.SynchronizePageActionGroup;
+
+/**
+ * Git synchronize participant that displays synchronization information for
+ * local resources that are managed via a {@link Subscriber}. It maintains a
+ * dynamic collection of all out-of-sync resources by listening to workspace
+ * resource changes and remote changes thus creating a live view of changes in
+ * the workspace.
+ */
+public class GitSubscriberParticipant extends SubscriberParticipant {
+
+ /**
+ * Name of Git synchronization participant
+ */
+ public static final String PARTICIPANT_NAME = "org.eclipse.egit.ui.synchronizeParticipant"; //$NON-NLS-1$
+
+ /**
+ * Construct GitBranchSubscriberParticipant.
+ *
+ * @param data
+ */
+ public GitSubscriberParticipant(GitSynchronizeDataSet data) {
+ ResourceVariantByteStore store = new SessionResourceVariantByteStore();
+ setSubscriber(new GitResourceVariantTreeSubscriber(data, store));
+ setName(UIText.GitBranchSubscriberParticipant_git);
+ }
+
+ @Override
+ protected void initializeConfiguration(
+ ISynchronizePageConfiguration configuration) {
+ super.initializeConfiguration(configuration);
+
+ configuration.addActionContribution(new SynchronizePageActionGroup() {
+ public void initialize(
+ ISynchronizePageConfiguration pageConfiguration) {
+ super.initialize(pageConfiguration);
+ appendToGroup(ISynchronizePageConfiguration.P_CONTEXT_MENU,
+ ISynchronizePageConfiguration.SYNCHRONIZE_GROUP,
+ new CommitAction(pageConfiguration));
+ }
+ });
+
+ configuration
+ .setSupportedModes(ISynchronizePageConfiguration.ALL_MODES);
+ configuration.setMode(ISynchronizePageConfiguration.BOTH_MODE);
+ }
+
+ @Override
+ public String getId() {
+ // note, this value needs to match the value in the plugin.xml
+ return PARTICIPANT_NAME;
+ }
+
+ @Override
+ public String getSecondaryId() {
+ // need to figure out what this is for, null is supposed to be
+ // acceptable but Team throws an NPE, see bug 256961
+ return "secondaryId"; //$NON-NLS-1$
+ }
+
+ /**
+ * @param data
+ *
+ */
+ public void refresh(GitSynchronizeDataSet data) {
+ refresh(data.getAllResources(),
+ UIText.GitSynchronizeWizard_gitResourceSynchronization, null,
+ null);
+ }
+
+ /**
+ * @param data
+ */
+ void reset(GitSynchronizeDataSet data) {
+ GitResourceVariantTreeSubscriber subscriber = (GitResourceVariantTreeSubscriber) getSubscriber();
+ subscriber.reset(data);
+ reset();
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronize.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronize.java
new file mode 100644
index 0000000..ad9e858
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronize.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Dariusz Luksza <dariusz@luksza.org>
+ *
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.synchronize;
+
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeData;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.ui.TeamUI;
+import org.eclipse.team.ui.synchronize.ISynchronizeManager;
+import org.eclipse.team.ui.synchronize.ISynchronizeParticipant;
+import org.eclipse.team.ui.synchronize.ISynchronizeParticipantReference;
+
+/**
+ * Perform synchronize action with selected branches on selected resources.
+ */
+public class GitSynchronize {
+
+ /**
+ * Constructs GitSynchronize
+ * @param data
+ */
+ public GitSynchronize(GitSynchronizeData data) {
+ this(new GitSynchronizeDataSet(data));
+ }
+
+ /**
+ * Constructs GitSynchronize
+ * @param data
+ */
+ public GitSynchronize(GitSynchronizeDataSet data) {
+ GitSubscriberParticipant participant = getParticipant(data);
+ participant.refresh(data);
+ }
+
+ private GitSubscriberParticipant getParticipant(GitSynchronizeDataSet data) {
+ ISynchronizeManager synchronizeManager = TeamUI.getSynchronizeManager();
+ ISynchronizeParticipantReference[] participants = synchronizeManager
+ .get(GitSubscriberParticipant.PARTICIPANT_NAME);
+
+ GitSubscriberParticipant participant;
+
+ if (participants.length == 0) {
+ participant = createDefaultParticipant(data);
+ } else {
+ try {
+ participant = (GitSubscriberParticipant) participants[0].getParticipant();
+ participant.reset(data);
+ } catch (TeamException e) {
+ participant = createDefaultParticipant(data);
+ }
+ }
+ return participant;
+ }
+
+ private GitSubscriberParticipant createDefaultParticipant(GitSynchronizeDataSet data) {
+ GitSubscriberParticipant participant = new GitSubscriberParticipant(data);
+ TeamUI.getSynchronizeManager().addSynchronizeParticipants(new ISynchronizeParticipant[] { participant });
+
+ return participant;
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronizeWizard.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronizeWizard.java
new file mode 100644
index 0000000..64b3ebf
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronizeWizard.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.synchronize;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeData;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Synchronization wizard for Git repositories
+ */
+public class GitSynchronizeWizard extends Wizard {
+
+ private GitSynchronizeWizardPage page;
+
+ /**
+ * Instantiates a new wizard for synchronizing resources that are being
+ * managed by EGit.
+ */
+ public GitSynchronizeWizard() {
+ setWindowTitle(UIText.GitSynchronizeWizard_synchronize);
+ }
+
+ @Override
+ public void addPages() {
+ page = new GitSynchronizeWizardPage();
+ addPage(page);
+ }
+
+ @Override
+ public boolean performFinish() {
+ Set<IProject> projects = page.getSelectedProjects();
+ GitSynchronizeDataSet gsdSet = new GitSynchronizeDataSet();
+
+ Map<Repository, String> branches = page.getSelectedBranches();
+ for (Repository repo : branches.keySet()) {
+ gsdSet.add(new GitSynchronizeData(repo, Constants.HEAD, branches.get(repo), projects, false));
+ }
+
+ new GitSynchronize(gsdSet);
+
+ return true;
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronizeWizardPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronizeWizardPage.java
new file mode 100644
index 0000000..5899e54
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/GitSynchronizeWizardPage.java
@@ -0,0 +1,355 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dariusz Luksza <dariusz@luksza.org>
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.synchronize;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.egit.ui.UIIcons;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ComboBoxCellEditor;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ContainerCheckedTreeViewer;
+import org.eclipse.ui.ide.IDE;
+
+class GitSynchronizeWizardPage extends WizardPage {
+
+ private CheckboxTreeViewer treeViewer;
+
+ private Map<Repository, Set<IProject>> repositories;
+
+ private Set<Repository> selectedRepositories = new HashSet<Repository>();
+
+ private Set<IProject> selectedProjects = new HashSet<IProject>();
+
+ private Map<Repository, String> selectedBranches = new HashMap<Repository, String>();
+
+ private Image branchesImage = UIIcons.BRANCHES.createImage();
+
+ private Image repositoryImage = UIIcons.REPOSITORY.createImage();
+
+ GitSynchronizeWizardPage() {
+ super(GitSynchronizeWizardPage.class.getName());
+ setTitle(UIText.GitBranchSynchronizeWizardPage_title);
+ setDescription(UIText.GitBranchSynchronizeWizardPage_description);
+ }
+
+ public void createControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout(1, false);
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ composite.setLayout(layout);
+
+ repositories = new HashMap<Repository, Set<IProject>>();
+ for (IProject project : ResourcesPlugin.getWorkspace().getRoot()
+ .getProjects()) {
+ RepositoryMapping mapping = RepositoryMapping.getMapping(project);
+ if (mapping != null) {
+ Repository repository = mapping.getRepository();
+ Set<IProject> set = repositories.get(repository);
+ if (set == null) {
+ set = new HashSet<IProject>();
+ repositories.put(repository, set);
+ }
+ set.add(project);
+ }
+ }
+
+ treeViewer = new ContainerCheckedTreeViewer(composite, SWT.BORDER
+ | SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION);
+ treeViewer.getTree().setLinesVisible(true);
+ treeViewer.getTree().setHeaderVisible(true);
+ treeViewer.getTree().setLayoutData(
+ new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ TreeViewerColumn repositoriesColumn = new TreeViewerColumn(treeViewer,
+ SWT.LEAD);
+ repositoriesColumn.getColumn().setText(
+ UIText.GitBranchSynchronizeWizardPage_repositories);
+ repositoriesColumn.getColumn().setImage(repositoryImage);
+ repositoriesColumn.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof Repository) {
+ return ((Repository) element).getDirectory()
+ .getAbsolutePath();
+ }
+ return ((IProject) element).getName();
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ if (element instanceof Repository) {
+ return repositoryImage;
+ }
+ return PlatformUI.getWorkbench().getSharedImages().getImage(
+ IDE.SharedImages.IMG_OBJ_PROJECT);
+ }
+ });
+
+ TreeViewerColumn branchesColumn = new TreeViewerColumn(treeViewer,
+ SWT.LEAD);
+ branchesColumn.getColumn().setText(UIText.GitBranchSynchronizeWizardPage_branches);
+ branchesColumn.getColumn().setImage(branchesImage);
+ branchesColumn.getColumn().setWidth(200);
+ final ComboBoxCellEditor branchesEditor = new ComboBoxCellEditor(
+ treeViewer.getTree(), new String[0]);
+ branchesColumn.setEditingSupport(new EditingSupport(treeViewer) {
+ @Override
+ protected void setValue(Object element, Object value) {
+ int intValue = ((Integer) value).intValue();
+ if (intValue == -1) {
+ return;
+ }
+
+ CCombo combo = (CCombo) branchesEditor.getControl();
+ String branch = combo.getItem(intValue);
+
+ if (element instanceof IProject) {
+ RepositoryMapping mapping = RepositoryMapping
+ .getMapping((IResource) element);
+ Repository repository = mapping.getRepository();
+ selectedBranches.put(repository, branch);
+ treeViewer.refresh(repository, true);
+ } else {
+ selectedBranches.put((Repository) element, branch);
+ treeViewer.refresh(element, true);
+ }
+
+ setPageComplete(selectedBranches.size() == selectedRepositories
+ .size());
+ }
+
+ @Override
+ protected Object getValue(Object element) {
+ if (element instanceof IProject) {
+ RepositoryMapping mapping = RepositoryMapping
+ .getMapping((IResource) element);
+ String branch = selectedBranches.get(mapping
+ .getRepository());
+ CCombo combo = (CCombo) branchesEditor.getControl();
+ int index = branch == null ? 0 : combo.indexOf(branch);
+ return new Integer(index);
+ } else {
+ String branch = selectedBranches.get(element);
+ CCombo combo = (CCombo) branchesEditor.getControl();
+ int index = branch == null ? 0 : combo.indexOf(branch);
+ return new Integer(index);
+ }
+ }
+
+ @Override
+ protected CellEditor getCellEditor(Object element) {
+ if (element instanceof IProject) {
+ RepositoryMapping mapping = RepositoryMapping
+ .getMapping((IResource) element);
+ Set<String> refs = mapping.getRepository().getAllRefs()
+ .keySet();
+ branchesEditor.setItems(refs
+ .toArray(new String[refs.size()]));
+ } else {
+ Set<String> refs = ((Repository) element).getAllRefs()
+ .keySet();
+ branchesEditor.setItems(refs
+ .toArray(new String[refs.size()]));
+ }
+ return branchesEditor;
+ }
+
+ @Override
+ protected boolean canEdit(Object element) {
+ return true;
+ }
+ });
+ branchesColumn.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof IProject) {
+ RepositoryMapping mapping = RepositoryMapping
+ .getMapping((IResource) element);
+ String branch = selectedBranches.get(mapping
+ .getRepository());
+ return branch == null ? "" : branch; //$NON-NLS-1$
+ } else {
+ String branch = selectedBranches.get(element);
+ return branch == null ? "" : branch; //$NON-NLS-1$
+ }
+ }
+ });
+
+ treeViewer.setContentProvider(new ITreeContentProvider() {
+ public void inputChanged(Viewer viewer, Object oldInput,
+ Object newInput) {
+ // nothing to do
+ }
+
+ public void dispose() {
+ // nothing to do
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return (Object[]) inputElement;
+ }
+
+ public boolean hasChildren(Object element) {
+ if (element instanceof Repository) {
+ return !repositories.get(element).isEmpty();
+ }
+ return false;
+ }
+
+ public Object getParent(Object element) {
+ return null;
+ }
+
+ public Object[] getChildren(Object parentElement) {
+ if (parentElement instanceof Repository) {
+ return repositories.get(parentElement).toArray();
+ }
+ return new Object[0];
+ }
+ });
+
+ final Object[] array = repositories.keySet().toArray();
+ treeViewer.setInput(array);
+ treeViewer.setCheckedElements(array);
+ repositoriesColumn.getColumn().pack();
+
+ save();
+
+ treeViewer.addCheckStateListener(new ICheckStateListener() {
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ selectedRepositories.clear();
+ selectedProjects.clear();
+
+ save();
+
+ if (event.getChecked()) {
+ setPageComplete(selectedBranches.size() == selectedRepositories
+ .size());
+ } else if (treeViewer.getCheckedElements().length == 0) {
+ setPageComplete(false);
+ }
+ }
+ });
+
+ Composite buttonsComposite = new Composite(composite, SWT.NONE);
+ layout = new GridLayout(2, true);
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ buttonsComposite.setLayout(layout);
+ buttonsComposite.setLayoutData(new GridData(SWT.BEGINNING,
+ SWT.BEGINNING, false, false));
+
+ Button selectAllBtn = new Button(buttonsComposite, SWT.PUSH);
+ selectAllBtn.setText(UIText.GitBranchSynchronizeWizardPage_selectAll);
+ selectAllBtn.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ treeViewer.setCheckedElements(array);
+ save();
+ setPageComplete(selectedBranches.size() == selectedRepositories
+ .size());
+ }
+ });
+ selectAllBtn.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false,
+ false));
+
+ Button deselectAllBtn = new Button(buttonsComposite, SWT.PUSH);
+ deselectAllBtn.setText(UIText.GitBranchSynchronizeWizardPage_deselectAll);
+ deselectAllBtn.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ // uncheck everything
+ treeViewer.setCheckedElements(new Object[0]);
+ // clear all selection
+ selectedRepositories.clear();
+ selectedProjects.clear();
+ // mark page as being incomplete
+ setPageComplete(false);
+ }
+ });
+ deselectAllBtn.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING,
+ false, false));
+
+ Dialog.applyDialogFont(composite);
+ setPageComplete(false);
+ setControl(composite);
+ }
+
+ @Override
+ public void dispose() {
+ if (branchesImage != null) {
+ branchesImage.dispose();
+ }
+ if (repositoryImage != null) {
+ repositoryImage.dispose();
+ }
+ super.dispose();
+ }
+
+ private void save() {
+ // record any candidate repositories that should be synchronized
+ for (Object grayedElement : treeViewer.getGrayedElements()) {
+ selectedRepositories.add((Repository) grayedElement);
+ }
+
+ for (Object checkedElement : treeViewer.getCheckedElements()) {
+ if (checkedElement instanceof Repository) {
+ Repository repo = (Repository) checkedElement;
+ if (selectedRepositories.add(repo)) {
+ // if this repository hasn't been added yet, it implies it's
+ // a checked element which means all the projects it owns
+ // should be selected
+ selectedProjects.addAll(repositories.get(repo));
+ }
+ } else {
+ selectedProjects.add((IProject) checkedElement);
+ }
+ }
+ }
+
+ Map<Repository, String> getSelectedBranches() {
+ return selectedBranches;
+ }
+
+ Set<IProject> getSelectedProjects() {
+ return selectedProjects;
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/RemoteSelectionCombo.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/RemoteSelectionCombo.java
new file mode 100644
index 0000000..ad3f7e1
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/RemoteSelectionCombo.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Dariusz Luksza <dariusz@luksza.org>
+ *
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.synchronize;
+
+import java.util.List;
+
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.internal.synchronize.SyncRepoEntity.SyncRefEntity;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+class RemoteSelectionCombo extends Composite {
+
+ private final Combo refsCombo;
+
+ private final Combo remotesCombo;
+
+ private final List<SyncRepoEntity> syncRepos;
+
+ public RemoteSelectionCombo(Composite parent, List<SyncRepoEntity> syncRepos) {
+ super(parent, SWT.NONE);
+ this.syncRepos = syncRepos;
+
+ setLayout(GridLayoutFactory.swtDefaults().numColumns(2).create());
+
+ new Label(this, SWT.NONE).setText(UIText.RemoteSelectionCombo_remoteName);
+ new Label(this, SWT.NONE).setText(UIText.RemoteSelectionCombo_remoteRef);
+
+ remotesCombo = new Combo(this, SWT.NONE);
+ refsCombo = new Combo(this, SWT.NONE);
+
+ remotesCombo.setLayoutData(GridDataFactory.fillDefaults().grab(true,
+ false).hint(150, SWT.DEFAULT).create());
+ refsCombo.setLayoutData(GridDataFactory.fillDefaults()
+ .grab(true, false).hint(150, SWT.DEFAULT).create());
+
+ for (SyncRepoEntity syncRepoEnt : syncRepos) {
+ remotesCombo.add(syncRepoEnt.getName());
+ }
+
+ remotesCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ fillRefsCombo();
+ }
+ });
+ }
+
+ public String getValue() {
+ int refSelectedIndex = refsCombo.getSelectionIndex();
+ int remoteSelectedIndex = remotesCombo.getSelectionIndex();
+
+ if (remoteSelectedIndex < 0 && refSelectedIndex < 0) {
+ return ""; //$NON-NLS-1$
+ }
+
+ return syncRepos.get(remoteSelectedIndex).getRefList().get(
+ refSelectedIndex).getValue();
+ }
+
+ private void fillRefsCombo() {
+ int selected = remotesCombo.getSelectionIndex();
+ if (selected < 0) {
+ return;
+ }
+
+ refsCombo.removeAll();
+ SyncRepoEntity syncRepoEnt = syncRepos.get(selected);
+ for (SyncRefEntity syncRefEnt : syncRepoEnt.getRefList()) {
+ refsCombo.add(syncRefEnt.getDescription());
+ }
+
+ if (refsCombo.getItemCount() > 0) {
+ refsCombo.select(0);
+ }
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/SelectSynchronizeResourceDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/SelectSynchronizeResourceDialog.java
new file mode 100644
index 0000000..e55a271
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/SelectSynchronizeResourceDialog.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Dariusz Luksza <dariusz@luksza.org>
+ *
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.synchronize;
+
+import java.io.File;
+import java.util.List;
+
+import org.eclipse.egit.ui.UIIcons;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Dialog for selecting repositories to synchronization
+ */
+public class SelectSynchronizeResourceDialog extends TitleAreaDialog {
+
+ private String dstRef;
+
+ private String srcRef;
+
+ private boolean shouldIncluldeLocal;
+
+ private final String repoName;
+
+ private final List<SyncRepoEntity> syncRepos;
+
+ private RemoteSelectionCombo dstRefCombo;
+
+ private RemoteSelectionCombo srcRefCombo;
+
+ private Button shouldIncludeLocalButton;
+
+ /**
+ * Construct dialog that gives user possibility to choose with which
+ * repository and branch he want to synchronize
+ *
+ * @param parent
+ * @param repoDirectory
+ * @param syncRepos
+ */
+ public SelectSynchronizeResourceDialog(Shell parent, File repoDirectory,
+ List<SyncRepoEntity> syncRepos) {
+ super(parent);
+ this.repoName = repoDirectory.getParentFile().getName()
+ + File.separator + repoDirectory.getName();
+ this.syncRepos = syncRepos;
+ }
+
+ /**
+ * @return remote ref name
+ */
+ public String getValue() {
+ return dstRef;
+ }
+
+ /**
+ * @return destination ref
+ */
+ public String getDstRef() {
+ return dstRef;
+ }
+
+ /**
+ * @return source ref
+ */
+ public String getSrcRef() {
+ return srcRef;
+ }
+
+ /**
+ * @return <code>true</code> if local uncommited changes should be included
+ * in comparison
+ */
+ public boolean shouldIncludeLocal() {
+ return shouldIncluldeLocal;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite composite = (Composite) super.createDialogArea(parent);
+ composite.setLayout(GridLayoutFactory.swtDefaults().create());
+
+ GridData data = new GridData(GridData.GRAB_HORIZONTAL
+ | GridData.HORIZONTAL_ALIGN_FILL
+ | GridData.VERTICAL_ALIGN_CENTER);
+ data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH / 2);
+
+ new Label(composite, SWT.WRAP)
+ .setText(UIText.SelectSynchronizeResourceDialog_srcRef);
+
+ srcRefCombo = new RemoteSelectionCombo(composite, syncRepos);
+ srcRefCombo.setLayoutData(data);
+ srcRefCombo.setLayoutData(GridDataFactory.fillDefaults().grab(true,
+ false).create());
+
+ shouldIncludeLocalButton = new Button(composite, SWT.CHECK | SWT.WRAP);
+ shouldIncludeLocalButton
+ .setText(UIText.SelectSynchronizeResourceDialog_includeUncommitedChnages);
+
+ new Label(composite, SWT.WRAP)
+ .setText(UIText.SelectSynchronizeResourceDialog_dstRef);
+
+ dstRefCombo = new RemoteSelectionCombo(composite, syncRepos);
+ dstRefCombo.setLayoutData(data);
+ dstRefCombo.setLayoutData(GridDataFactory.fillDefaults().grab(true,
+ false).create());
+
+ setTitle(NLS.bind(UIText.SelectSynchronizeResourceDialog_selectProject,
+ repoName));
+ setMessage(UIText.SelectSynchronizeResourceDialog_header);
+ setTitleImage(UIIcons.WIZBAN_CONNECT_REPO.createImage());
+
+ return composite;
+ }
+
+ @Override
+ protected void buttonPressed(int buttonId) {
+ if (buttonId == IDialogConstants.OK_ID) {
+ dstRef = dstRefCombo.getValue();
+ srcRef = srcRefCombo.getValue();
+ shouldIncluldeLocal = shouldIncludeLocalButton.getSelection();
+ }
+ super.buttonPressed(buttonId);
+ }
+
+ @Override
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell
+ .setText(NLS.bind(
+ UIText.SelectSynchronizeResourceDialog_selectProject,
+ repoName));
+
+ newShell.setMinimumSize(600, 180);
+ }
+
+ @Override
+ protected boolean isResizable() {
+ return true;
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/SyncRepoEntity.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/SyncRepoEntity.java
new file mode 100644
index 0000000..ed63662
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/synchronize/SyncRepoEntity.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Dariusz Luksza <dariusz@luksza.org>
+ *
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.synchronize;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Simple entity for remote and local repositories containing only repo name and
+ * list of refs associated wit it.
+ */
+public class SyncRepoEntity {
+
+ /**
+ * Simple entity for refs containing only human readable ref name and git
+ * ref path
+ */
+ public static class SyncRefEntity {
+ private final String descr;
+
+ private final String value;
+
+ /**
+ * @param descr
+ * human readable description of repository
+ * @param value
+ * value that will be associated with this repo eg. HEAD,
+ * refs/heads/master, etc
+ */
+ public SyncRefEntity(String descr, String value) {
+ this.descr = descr;
+ this.value = value;
+ }
+
+ /**
+ * @return human readable description of ref
+ */
+ public String getDescription() {
+ return descr;
+ }
+
+ /**
+ * @return value that is associated with this ref eg. HEAD,
+ * refs/heads/master, etc.
+ */
+ public String getValue() {
+ return value;
+ }
+ }
+
+ private final String name;
+
+ private final List<SyncRefEntity> refs;
+
+ /**
+ * @param name
+ * of repository eg. local, origin, etc.
+ */
+ public SyncRepoEntity(String name) {
+ this.name = name;
+ refs = new ArrayList<SyncRefEntity>();
+ }
+
+ /**
+ * @return name of repository
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param ref
+ * that will be added to this repository
+ */
+ public void addRef(SyncRefEntity ref) {
+ refs.add(ref);
+ }
+
+ /**
+ *
+ * @return list of refs associated with this repository
+ */
+ public List<SyncRefEntity> getRefList() {
+ return Collections.unmodifiableList(refs);
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties
index fe68816..ef1bebc 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties
@@ -569,6 +569,7 @@ RepositoriesView_BranchCreationFailureMessage=Branch creation failed
RepositoriesView_BranchDeletionFailureMessage=Branch deletion failed
RepositoriesView_Branches_Nodetext=Branches
RepositoriesView_CheckingOutMessage=Checking out {0}
+RepositoriesView_Synchronize_MenuItem=&Synchronize
RepositoriesView_CheckOut_MenuItem=&Check out
RepositoriesView_ClipboardContentNoGitRepoMessage=Path {0} does not appear to be a Git Repository location
RepositoriesView_ClipboardContentNotDirectoryMessage=Clipboard content is not a directory path
@@ -713,3 +714,28 @@ CreateTagDialog_clearButton=C&lear
CreateTagDialog_clearButtonTooltip=Clear all dialog fields.
CommitCombo_showSuggestedCommits=Start typing SHA-1 of existing commit or part of first line in commit message to see suggested commits.
+
+CommitAction_commit=Commit...
+
+GitSynchronizeWizard_synchronize=Synchronize
+GitSynchronizeWizard_gitResourceSynchronization=Git Resource Synchronization
+
+GitBranchSynchronizeWizardPage_title=Synchronize Git
+GitBranchSynchronizeWizardPage_description=Select the resources to be synchronized.
+GitBranchSynchronizeWizardPage_repositories=Repositories
+GitBranchSynchronizeWizardPage_branches=Branches
+GitBranchSynchronizeWizardPage_selectAll=Select All
+GitBranchSynchronizeWizardPage_deselectAll=Deselect All
+
+GitBranchSubscriberParticipant_git=Git
+
+RemoteSelectionCombo_remoteName=Remote Repository:
+RemoteSelectionCombo_remoteRef=Ref:
+
+SelectSynchronizeResourceDialog_selectProject=Synchronize repository: {0}
+SelectSynchronizeResourceDialog_header=Select which branch or tag to synchronize.
+SelectSynchronizeResourceDialog_srcRef=Select which branch or tag should be treat as source:
+SelectSynchronizeResourceDialog_dstRef=Select which branch or tag should be treat as destination:
+SelectSynchronizeResourceDialog_includeUncommitedChnages=Include local uncommited changes in comparison
+
+SynchronizeWithAction_localRepoName=local .git