Merge "Applying a Git patch needs too much user work"
diff --git a/bundles/org.eclipse.compare.core/src/org/eclipse/compare/internal/core/patch/PatchReader.java b/bundles/org.eclipse.compare.core/src/org/eclipse/compare/internal/core/patch/PatchReader.java
index adf5deb..983b7f6 100644
--- a/bundles/org.eclipse.compare.core/src/org/eclipse/compare/internal/core/patch/PatchReader.java
+++ b/bundles/org.eclipse.compare.core/src/org/eclipse/compare/internal/core/patch/PatchReader.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * Copyright (c) 2006, 2012 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
@@ -20,6 +20,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.StringTokenizer;
+import java.util.regex.Pattern;
 
 import org.eclipse.compare.patch.IFilePatch2;
 import org.eclipse.core.runtime.Assert;
@@ -52,6 +53,7 @@
 	};
 
 	private boolean fIsWorkspacePatch;
+	private boolean fIsGitPatch;
 	private DiffProject[] fDiffProjects;
 	private FilePatch2[] fDiffs;
 
@@ -61,7 +63,9 @@
 	public static final String MULTIPROJECTPATCH_VERSION= "1.0"; //$NON-NLS-1$
 
 	public static final String MULTIPROJECTPATCH_PROJECT= "#P"; //$NON-NLS-1$
-	
+
+	private static final Pattern GIT_PATCH_PATTERN = Pattern.compile("^diff --git a/.+ b/.+[\r\n]+$");
+
 	/**
 	 * Create a patch reader for the default date formats.
 	 */
@@ -92,6 +96,7 @@
 		// which will be replaced by the target selected by the user in the preview pane
 		String projectName= ""; //$NON-NLS-1$
 		this.fIsWorkspacePatch= false;
+		this.fIsGitPatch = false;
 
 		LineReader lr= new LineReader(reader);
 		if (!Platform.WS_CARBON.equals(Platform.getWS()))
@@ -102,6 +107,8 @@
 		if (line != null && line.startsWith(PatchReader.MULTIPROJECTPATCH_HEADER)) {
 			this.fIsWorkspacePatch= true;
 		} else {
+			if (line != null && GIT_PATCH_PATTERN.matcher(line).matches())
+				this.fIsGitPatch = true;
 			parse(lr, line);
 			return;
 		}
@@ -661,6 +668,10 @@
 		return this.fIsWorkspacePatch;
 	}
 
+	public boolean isGitPatch() {
+		return this.fIsGitPatch;
+	}
+
 	public DiffProject[] getDiffProjects() {
 		return this.fDiffProjects;
 	}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java
index 7248fcc..544fdc3 100644
--- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -214,7 +214,6 @@
 
 		// If this is a workspace patch we don't need to set a target as the targets will be figured out from 
 		// all of the projects that make up the patch and continue on to final preview page 
-		// else go on to target selection page
 		if (patcher.isWorkspacePatch()) {
 			// skip 'Patch Target' page
 			IWizardPage page = super.getNextPage();
@@ -222,6 +221,18 @@
 				return page.getNextPage();
 		}
 
+		// If this is a git patch set the workspace root as the target and skip the target selection page
+		if (patcher.isGitPatch()) {
+			// skip 'Patch Target' page
+			IWizardPage page = super.getNextPage();
+			if (page.getName().equals(PatchTargetPage.PATCHTARGETPAGE_NAME)) {
+				// set the workspace root as the target
+				patcher.setTarget(ResourcesPlugin.getWorkspace().getRoot());
+				return page.getNextPage();
+			}
+		}
+
+		// else go on to target selection page
 		return super.getNextPage();
 	}
 
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizard.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizard.java
index ede6e06..3f7f02d 100644
--- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizard.java
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizard.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -104,8 +104,10 @@
 	public void addPages() {
 		if (patch == null)
 			addPage(fPatchWizardPage = new InputPatchPage(this));
-		if (patch == null || !fPatcher.isWorkspacePatch())
+		if (patch == null || (!fPatcher.isWorkspacePatch() && !fPatcher.isGitPatch()))
 			addPage(fPatchTargetPage = new PatchTargetPage(fPatcher));
+		else if (fPatcher.isGitPatch())
+			fPatcher.setTarget(ResourcesPlugin.getWorkspace().getRoot());
 		fPreviewPage2 = new PreviewPatchPage2(fPatcher, fConfiguration);
 		addPage(fPreviewPage2);
 	}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PreviewPatchPage2.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PreviewPatchPage2.java
index 72763cc..f4201ef 100644
--- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PreviewPatchPage2.java
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PreviewPatchPage2.java
@@ -347,6 +347,11 @@
 		//Need to handle input and rebuild tree only when becoming visible
 		if (visible){
 			fillSegmentCombo();
+			if (getPatcher().isGitPatch()) {
+				int ignore = getPatcher().calculateStripGitPrefixSegments();
+				fStripPrefixSegments.select(ignore);
+				getPatcher().setStripPrefixSegments(ignore);
+			}
 			// TODO: We should only do this if the tree needs to be rebuilt
 			rebuildTree();
 			updateEnablements();
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspacePatcher.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspacePatcher.java
index baecbe1..d63a246 100644
--- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspacePatcher.java
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspacePatcher.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -48,6 +48,7 @@
 
 	private DiffProject[] fDiffProjects;
 	private boolean fIsWorkspacePatch= false;
+	private boolean fIsGitPatch = false;
 	private final Map retargetedDiffs = new HashMap();
 
 	public WorkspacePatcher() {
@@ -62,6 +63,7 @@
 		super.patchParsed(patchReader);
 		fDiffProjects = patchReader.getDiffProjects();
 		fIsWorkspacePatch = patchReader.isWorkspacePatch();
+		fIsGitPatch = patchReader.isGitPatch() && calculateStripGitPrefixSegments() > -1;
 	}
 	
 	public DiffProject[] getDiffProjects() {
@@ -72,6 +74,10 @@
 		return fIsWorkspacePatch;
 	}
 
+	public boolean isGitPatch() {
+		return fIsGitPatch;
+	}
+
 	//---- parsing patch files
 
 	public void applyAll(IProgressMonitor pm, IFileValidator validator) throws CoreException {
@@ -381,5 +387,50 @@
 			return 0;
 		return super.getStripPrefixSegments();
 	}
-    
+
+	int calculateStripGitPrefixSegments() {
+		FilePatch2[] diffs = getDiffs();
+		if (diffs.length == 0)
+			return -1;
+		int skip = -1;
+		for (int i = 0; i < diffs.length; i++) {
+			IPath oldPath = diffs[i].getPath(false);
+			IPath newPath = diffs[i].getPath(true);
+			if (checkFirstSegments(new IPath[] { oldPath, newPath },
+					new String[][] { { "a", "b" }, // change //$NON-NLS-1$ //$NON-NLS-2$
+							{ "b", "b" }, // addition //$NON-NLS-1$ //$NON-NLS-2$
+							{ "a", "a" } }) // deletion //$NON-NLS-1$ //$NON-NLS-2$
+					&& oldPath.segmentCount() > 2 && newPath.segmentCount() > 2) {
+				for (int j = 1; j < Math.min(oldPath.segmentCount(),
+						newPath.segmentCount()); j++) {
+					if (projectExists(oldPath.segment(j))
+							|| projectExists(newPath.segment(j))) {
+						if (skip == -1)
+							skip = j;
+						else if (skip != j)
+							return -1; // a different number of segments to be
+										// skipped, abort
+						break;
+					}
+				}
+			} else
+				return -1; // not a git diff or custom prefixes used
+		}
+		return skip;
+	}
+
+	private boolean checkFirstSegments(IPath[] paths, String[][] segments) {
+		SEGMENTS: for (int i = 0; i < segments.length; i++) {
+			for (int j = 0; j < paths.length; j++) {
+				if (!paths[j].segment(0).equals(segments[i][j]))
+					continue SEGMENTS;
+			}
+			return true;
+		}
+		return false;
+	}
+
+	private boolean projectExists(final String name) {
+		return ResourcesPlugin.getWorkspace().getRoot().getProject(name).exists();
+	}
 }