diff options
14 files changed, 512 insertions, 176 deletions
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/op/CreatePatchOperationTest.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/op/CreatePatchOperationTest.java new file mode 100644 index 0000000000..4a56181900 --- /dev/null +++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/op/CreatePatchOperationTest.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2011, Tasktop Technologies + * + * 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: + * Benjamin Muskalla (Tasktop Technologies) - initial implementation + *******************************************************************************/ +package org.eclipse.egit.core.test.op; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.File; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.egit.core.op.CreatePatchOperation; +import org.eclipse.egit.core.test.GitTestCase; +import org.eclipse.egit.core.test.TestRepository; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.revwalk.RevCommit; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class CreatePatchOperationTest extends GitTestCase { + + private static final String SIMPLE_GIT_PATCH_CONTENT = "From 6dcd097c7d39e9ba0b31a380981d3fb46017d6c2 Sat, 23 Jul 2011 20:33:24 -0330\n" + + "From: J. Git <j.git@egit.org>\n" + + "Date: Sat, 15 Aug 2009 20:12:58 -0330\n" + + "Subject: [PATCH] 2nd commit\n" + + "\n" + + "diff --git a/test-file b/test-file\n" + + "index e69de29..eb5f2c9 100644\n" + + "--- a/test-file\n" + + "+++ b/test-file\n" + + "@@ -0,0 +1 @@\n" + + "+another line\n" + + "\\ No newline at end of file"; + + private static final String SIMPLE_PATCH_CONTENT = "diff --git a/test-file b/test-file\n" + + "index e69de29..eb5f2c9 100644\n" + + "--- a/test-file\n" + + "+++ b/test-file\n" + + "@@ -0,0 +1 @@\n" + + "+another line\n" + + "\\ No newline at end of file"; + + private RevCommit commit; + + private File file; + + private TestRepository testRepository; + + @Before + public void setUp() throws Exception { + super.setUp(); + gitDir = new File(project.getProject().getLocationURI().getPath(), + Constants.DOT_GIT); + testRepository = new TestRepository(gitDir); + testRepository.connect(project.getProject()); + + file = testRepository.createFile(project.getProject(), "test-file"); + + commit = testRepository.addAndCommit(project.getProject(), file, + "new file"); + } + + @After + public void tearDown() throws Exception { + testRepository.dispose(); + super.tearDown(); + } + + @Test + public void testSimpleGitPatch() throws Exception { + RevCommit secondCommit = testRepository.appendContentAndCommit( + project.getProject(), file, "another line", "2nd commit"); + + CreatePatchOperation operation = new CreatePatchOperation( + testRepository.getRepository(), secondCommit); + + operation.execute(new NullProgressMonitor()); + + String patchContent = operation.getPatchContent(); + assertNotNull(patchContent); + assertGitPatch(SIMPLE_GIT_PATCH_CONTENT, patchContent); + } + + @Test + public void testSimplePatch() throws Exception { + RevCommit secondCommit = testRepository.appendContentAndCommit( + project.getProject(), file, "another line", "2nd commit"); + + CreatePatchOperation operation = new CreatePatchOperation( + testRepository.getRepository(), secondCommit); + + operation.useGitFormat(false); + operation.execute(new NullProgressMonitor()); + + String patchContent = operation.getPatchContent(); + assertNotNull(patchContent); + assertPatch(SIMPLE_PATCH_CONTENT, patchContent); + } + + @Test(expected = IllegalStateException.class) + public void testFirstCommit() throws Exception { + CreatePatchOperation operation = new CreatePatchOperation( + testRepository.getRepository(), commit); + + operation.execute(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testNullCommit() throws Exception { + new CreatePatchOperation(testRepository.getRepository(), null); + } + + @Test(expected = IllegalArgumentException.class) + public void testNullRepo() throws Exception { + new CreatePatchOperation(null, commit); + } + + @Test(expected = IllegalStateException.class) + public void testExecuteFirst() throws Exception { + CreatePatchOperation operation = new CreatePatchOperation( + testRepository.getRepository(), commit); + operation.getPatchContent(); + } + + @Test + public void testNullMonitor() throws Exception { + RevCommit secondCommit = testRepository.appendContentAndCommit( + project.getProject(), file, "another line", "2nd commit"); + CreatePatchOperation operation = new CreatePatchOperation( + testRepository.getRepository(), secondCommit); + operation.execute(null); + } + + @Test + public void testSuggestName() throws Exception { + RevCommit aCommit = testRepository.appendContentAndCommit( + project.getProject(), file, "another line", "2nd commit"); + assertEquals("2nd-commit.patch", CreatePatchOperation.suggestFileName(aCommit)); + + aCommit = testRepository.appendContentAndCommit( + project.getProject(), file, "another line", "[findBugs] Change visibility of repositoryFile to package"); + assertEquals("findBugs-Change-visibility-of-repositoryFile-to-pack.patch", CreatePatchOperation.suggestFileName(aCommit)); + + aCommit = testRepository.appendContentAndCommit( + project.getProject(), file, "another line", "Add collapse/expand all utility method for tree viewers."); + assertEquals("Add-collapse-expand-all-utility-method-for-tree-view.patch", CreatePatchOperation.suggestFileName(aCommit)); + } + + private void assertGitPatch(String expected, String actual) { + assertEquals(expected.substring(0,45), actual.substring(0,45)); + assertEquals(expected.substring(expected.indexOf("\n")), actual.substring(actual.indexOf("\n"))); + } + + private void assertPatch(String expected, String actual) { + assertEquals(expected, actual); + } + +} diff --git a/org.eclipse.egit.core/META-INF/MANIFEST.MF b/org.eclipse.egit.core/META-INF/MANIFEST.MF index a033530dcf..328a0408d9 100644 --- a/org.eclipse.egit.core/META-INF/MANIFEST.MF +++ b/org.eclipse.egit.core/META-INF/MANIFEST.MF @@ -12,7 +12,7 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.4.0,4.0.0)", org.eclipse.core.filesystem;bundle-version="[1.2.0,2.0.0)", org.eclipse.equinox.security;bundle-version="[1.0.0,2.0.0)" Export-Package: org.eclipse.egit.core;version="1.1.0", - org.eclipse.egit.core.internal;version="1.1.0";x-internal:=true, + org.eclipse.egit.core.internal;version="1.1.0";x-friends:="org.eclipse.egit.ui", org.eclipse.egit.core.internal.storage;version="1.1.0";x-friends:="org.eclipse.egit.ui,org.eclipse.egit.core.test", org.eclipse.egit.core.internal.trace;version="1.1.0";x-internal:=true, org.eclipse.egit.core.internal.util;version="1.1.0";x-friends:="org.eclipse.egit.ui", @@ -25,13 +25,15 @@ Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 Import-Package: org.eclipse.jgit.api;version="[1.1.0,1.2.0)", org.eclipse.jgit.api.errors;version="[1.1.0,1.2.0)", + org.eclipse.jgit.diff;version="[1.1.0,1.2.0)", org.eclipse.jgit.dircache;version="[1.1.0,1.2.0)", org.eclipse.jgit.errors;version="[1.1.0,1.2.0)", org.eclipse.jgit.lib;version="[1.1.0,1.2.0)", org.eclipse.jgit.merge;version="[1.1.0,1.2.0)", + org.eclipse.jgit.patch;version="[1.1.0,1.2.0)", org.eclipse.jgit.revwalk;version="[1.1.0,1.2.0)", - org.eclipse.jgit.storage.file;version="[1.1.0,1.2.0)", org.eclipse.jgit.revwalk.filter;version="[1.1.0,1.2.0)", + org.eclipse.jgit.storage.file;version="[1.1.0,1.2.0)", org.eclipse.jgit.transport;version="[1.1.0,1.2.0)", org.eclipse.jgit.treewalk;version="[1.1.0,1.2.0)", org.eclipse.jgit.treewalk.filter;version="[1.1.0,1.2.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 f827a6851a..2a3b9cda65 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 @@ -198,6 +198,12 @@ public class CoreText extends NLS { public static String CreateLocalBranchOperation_CreatingBranchMessage; /** */ + public static String CreatePatchOperation_commitRequired; + + /** */ + public static String CreatePatchOperation_repoRequired; + + /** */ public static String IndexFileRevision_errorLookingUpPath; /** */ 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 6eba656c2a..06dbc274c5 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 @@ -81,6 +81,8 @@ CloneOperation_initializingRepository=Initializing local repository CloneOperation_title=Cloning from {0} CloneOperation_writingIndex=Writing index CreateLocalBranchOperation_CreatingBranchMessage=Creating branch {0} +CreatePatchOperation_commitRequired=A commit is required to create a patch +CreatePatchOperation_repoRequired=A repository is required to create a patch IndexFileRevision_errorLookingUpPath=IO error looking up path {0} in index. IndexFileRevision_indexEntryNotFound=Git index entry for path {1} not found diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CompareCoreUtils.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CompareCoreUtils.java new file mode 100644 index 0000000000..2e2eab7530 --- /dev/null +++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CompareCoreUtils.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: + * Stefan Lay (SAP AG) - initial implementation + * Benjamin Muskalla (Tasktop Technologies) - moved into Core for reusability + *******************************************************************************/ +package org.eclipse.egit.core.internal; + +import org.eclipse.core.resources.IEncodedStorage; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Repository; + +/** + * Utility class for compare-related functionality + */ +public class CompareCoreUtils { + + /** + * Determine the encoding used by Eclipse for the resource which belongs to + * repoPath in the eclipse workspace or null if no resource is found + * + * @param db + * the repository + * @param repoPath + * the path in the git repository + * @return the encoding used in eclipse for the resource or null if + * + */ + public static String getResourceEncoding(Repository db, String repoPath) { + if (db.isBare()) + return null; + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IWorkspaceRoot root = workspace.getRoot(); + IPath absolutePath = new Path(db.getWorkTree().getAbsolutePath()) + .append(repoPath); + IResource resource = root.getFileForLocation(absolutePath); + if (resource == null) + return null; + + return getResourceEncoding(resource); + } + + /** + * Determine the encoding used by eclipse for the resource. + * + * @param resource + * must be an instance of IEncodedStorage + * @return the encoding used in Eclipse for the resource if found or null + */ + public static String getResourceEncoding(IResource resource) { + // Get the encoding for the current version. As a matter of + // principle one might want to use the eclipse settings for the + // version we are retrieving as that may be defined by the + // project settings, but there is no historic API for this. + String charset; + IEncodedStorage encodedStorage = ((IEncodedStorage) resource); + try { + charset = encodedStorage.getCharset(); + if (charset == null) + charset = resource.getParent().getDefaultCharset(); + } catch (CoreException e) { + charset = Constants.CHARACTER_ENCODING; + } + return charset; + } + +} diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/CreatePatchOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/CreatePatchOperation.java new file mode 100644 index 0000000000..a5b91b49cd --- /dev/null +++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/CreatePatchOperation.java @@ -0,0 +1,208 @@ +/******************************************************************************* + * 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: + * Stefan Lay (SAP AG) - initial implementation + * Benjamin Muskalla (Tasktop Technologies) - extract into operation + *******************************************************************************/ +package org.eclipse.egit.core.op; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.egit.core.Activator; +import org.eclipse.egit.core.CoreText; +import org.eclipse.egit.core.EclipseGitProgressTransformer; +import org.eclipse.egit.core.internal.CompareCoreUtils; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.DiffFormatter; +import org.eclipse.jgit.diff.DiffEntry.ChangeType; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; + +/** + * Creates a patch for a specific commit + */ +public class CreatePatchOperation implements IEGitOperation { + + private final RevCommit commit; + + private final Repository repository; + + private boolean useGitFormat = true; + + // the encoding for the currently processed file + private String currentEncoding = null; + + private String patchContent; + + /** + * Creates the new operation. + * + * @param repository + * @param commit + */ + public CreatePatchOperation(Repository repository, RevCommit commit) { + if (repository == null) + throw new IllegalArgumentException( + CoreText.CreatePatchOperation_repoRequired); + if (commit == null) + throw new IllegalArgumentException( + CoreText.CreatePatchOperation_commitRequired); + this.repository = repository; + this.commit = commit; + } + + public void execute(IProgressMonitor monitor) throws CoreException { + EclipseGitProgressTransformer gitMonitor; + if (monitor == null) + gitMonitor = new EclipseGitProgressTransformer( + new NullProgressMonitor()); + else + gitMonitor = new EclipseGitProgressTransformer(monitor); + + final StringBuilder sb = new StringBuilder(); + final DiffFormatter diffFmt = new DiffFormatter( + new ByteArrayOutputStream() { + + @Override + public synchronized void write(byte[] b, int off, int len) { + super.write(b, off, len); + if (currentEncoding == null) + sb.append(toString()); + else try { + sb.append(toString(currentEncoding)); + } catch (UnsupportedEncodingException e) { + sb.append(toString()); + } + reset(); + } + }); + + diffFmt.setProgressMonitor(gitMonitor); + + RevCommit[] parents = commit.getParents(); + if (parents.length > 1) + throw new IllegalStateException( + "Cannot create patch for merge commit"); //$NON-NLS-1$ + + if (parents.length == 0) + throw new IllegalStateException( + "Cannot create patch for first commit"); //$NON-NLS-1$ + + if (useGitFormat) + writeGitPatchHeader(sb); + + try { + diffFmt.setRepository(repository); + List<DiffEntry> diffs = diffFmt.scan(parents[0].getId(), commit.getId()); + for (DiffEntry ent : diffs) { + String path; + if (ChangeType.DELETE.equals(ent.getChangeType())) + path = ent.getOldPath(); + else + path = ent.getNewPath(); + currentEncoding = CompareCoreUtils.getResourceEncoding(repository, path); + diffFmt.format(ent); + } + } catch (IOException e) { + Activator.logError("Patch file could not be written", e); //$NON-NLS-1$ + } + + patchContent = sb.toString(); + // trim newline + if (patchContent.endsWith("\n")) //$NON-NLS-1$ + patchContent = patchContent.substring(0, patchContent.length() - 1); + } + + /** + * Retrieves the content of the requested patch + * + * @return the content of the patch + */ + public String getPatchContent() { + if (patchContent == null) + throw new IllegalStateException( + "#execute needs to be called before this method."); //$NON-NLS-1$ + return patchContent; + } + + private void writeGitPatchHeader(StringBuilder sb) { + final SimpleDateFormat dtfmt; + dtfmt = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.US); //$NON-NLS-1$ + dtfmt.setTimeZone(commit.getAuthorIdent().getTimeZone()); + sb.append("From").append(" ") //$NON-NLS-1$ //$NON-NLS-2$ + .append(commit.getId().getName()).append(" ") //$NON-NLS-1$ + .append(dtfmt.format(Long.valueOf(System.currentTimeMillis()))) + .append("\n"); //$NON-NLS-1$ + sb.append("From") //$NON-NLS-1$ + .append(": ") //$NON-NLS-1$ + .append(commit.getAuthorIdent().getName()) + .append(" <").append(commit.getAuthorIdent().getEmailAddress()) //$NON-NLS-1$ + .append(">\n"); //$NON-NLS-1$ + sb.append("Date").append(": ") //$NON-NLS-1$ //$NON-NLS-2$ + .append(dtfmt.format(commit.getAuthorIdent().getWhen())) + .append("\n"); //$NON-NLS-1$ + sb.append("Subject").append(": [PATCH] ") //$NON-NLS-1$ //$NON-NLS-2$ + .append(commit.getShortMessage()); + + String message = commit.getFullMessage().substring( + commit.getShortMessage().length()); + sb.append(message).append("\n\n"); //$NON-NLS-1$ + } + + /** + * Decides whether to use the git format for patches. + * + * @param useFormat + */ + public void useGitFormat(boolean useFormat) { + this.useGitFormat = useFormat; + } + + /** + * Suggests a file name for the patch given the commit. + * + * @param commit + * @return a file name for a patch + */ + public static String suggestFileName(RevCommit commit) { + String name = commit.getShortMessage(); + + name = name.trim(); + StringBuilder filteredBuilder = new StringBuilder(); + char[] charArray = name.toCharArray(); + for (char c : charArray) { + if(Character.isLetter(c) || Character.isDigit(c)) + filteredBuilder.append(c); + if(Character.isWhitespace(c) || c == '/') + filteredBuilder.append("-"); //$NON-NLS-1$ + } + name = filteredBuilder.toString(); + if (name.length() > 52) + name = name.substring(0, 52); + while (name.endsWith(".")) //$NON-NLS-1$ + name = name.substring(0, name.length() - 1); + name = name.concat(".patch"); //$NON-NLS-1$ + + return name; + } + + public ISchedulingRule getSchedulingRule() { + return null; + } + +} diff --git a/org.eclipse.egit.ui/META-INF/MANIFEST.MF b/org.eclipse.egit.ui/META-INF/MANIFEST.MF index 884e3fe450..20cd6998a7 100644 --- a/org.eclipse.egit.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.egit.ui/META-INF/MANIFEST.MF @@ -32,6 +32,7 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.4.0,4.0.0)", Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 Import-Package: org.eclipse.egit.core;version="[1.1.0,1.2.0)", + org.eclipse.egit.core.internal;version="[1.1.0,1.2.0)", org.eclipse.egit.core.internal.storage;version="[1.1.0,1.2.0)", org.eclipse.egit.core.internal.util;version="[1.1.0,1.2.0)", org.eclipse.egit.core.op;version="[1.1.0,1.2.0)", diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/CompareUtils.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/CompareUtils.java index f6c16e9fa5..018e9a9427 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/CompareUtils.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/CompareUtils.java @@ -18,22 +18,17 @@ import java.io.IOException; import org.eclipse.compare.CompareEditorInput; import org.eclipse.compare.CompareUI; import org.eclipse.compare.ITypedElement; -import org.eclipse.core.resources.IEncodedStorage; import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.egit.core.internal.CompareCoreUtils; import org.eclipse.egit.core.internal.storage.GitFileRevision; import org.eclipse.egit.core.internal.storage.WorkingTreeFileRevision; import org.eclipse.egit.core.internal.storage.WorkspaceFileRevision; @@ -114,7 +109,7 @@ public class CompareUtils { IFileRevision nextFile = getFileRevision(gitPath, commit, db, blobId); if (nextFile != null) { - String encoding = getResourceEncoding(db, gitPath); + String encoding = CompareCoreUtils.getResourceEncoding(db, gitPath); right = new FileRevisionTypedElement(nextFile, encoding); } } catch (IOException e) { @@ -179,55 +174,6 @@ public class CompareUtils { } /** - * Determine the encoding used by Eclipse for the resource which belongs to - * repoPath in the eclipse workspace or null if no resource is found - * - * @param db - * the repository - * @param repoPath - * the path in the git repository - * @return the encoding used in eclipse for the resource or null if - * - */ - public static String getResourceEncoding(Repository db, String repoPath) { - if (db.isBare()) - return null; - IWorkspace workspace = ResourcesPlugin.getWorkspace(); - IWorkspaceRoot root = workspace.getRoot(); - IPath absolutePath = new Path(db.getWorkTree().getAbsolutePath()) - .append(repoPath); - IResource resource = root.getFileForLocation(absolutePath); - if (resource == null) - return null; - - return getResourceEncoding(resource); - } - - /** - * Determine the encoding used by eclipse for the resource. - * - * @param resource - * must be an instance of IEncodedStorage - * @return the encoding used in Eclipse for the resource if found or null - */ - public static String getResourceEncoding(IResource resource) { - // Get the encoding for the current version. As a matter of - // principle one might want to use the eclipse settings for the - // version we are retrieving as that may be defined by the - // project settings, but there is no historic API for this. - String charset; - IEncodedStorage encodedStorage = ((IEncodedStorage) resource); - try { - charset = encodedStorage.getCharset(); - if (charset == null) - charset = resource.getParent().getDefaultCharset(); - } catch (CoreException e) { - charset = Constants.CHARACTER_ENCODING; - } - return charset; - } - - /** * @param element * @param adapterType * @param load diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffStyleRangeFormatter.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffStyleRangeFormatter.java index 82ef5bb01c..d05931c420 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffStyleRangeFormatter.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffStyleRangeFormatter.java @@ -15,7 +15,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; -import org.eclipse.egit.ui.internal.CompareUtils; +import org.eclipse.egit.core.internal.CompareCoreUtils; import org.eclipse.egit.ui.internal.commit.DiffStyleRangeFormatter.DiffStyleRange.Type; import org.eclipse.egit.ui.internal.history.FileDiff; import org.eclipse.jface.text.BadLocationException; @@ -159,7 +159,7 @@ public class DiffStyleRangeFormatter extends DiffFormatter { */ public DiffStyleRangeFormatter write(Repository repository, FileDiff diff) throws IOException { - this.stream.charset = CompareUtils.getResourceEncoding(repository, + this.stream.charset = CompareCoreUtils.getResourceEncoding(repository, diff.getPath()); diff.outputDiff(null, repository, this, true); flush(); diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitDocument.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitDocument.java index af6e24ad18..cf61b79356 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitDocument.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitDocument.java @@ -14,10 +14,10 @@ import java.util.WeakHashMap; import org.eclipse.core.resources.IResource; import org.eclipse.egit.core.GitProvider; +import org.eclipse.egit.core.internal.CompareCoreUtils; import org.eclipse.egit.core.project.RepositoryMapping; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.UIText; -import org.eclipse.egit.ui.internal.CompareUtils; import org.eclipse.egit.ui.internal.trace.GitTraceLocation; import org.eclipse.jface.text.Document; import org.eclipse.jgit.events.ListenerHandle; @@ -172,7 +172,7 @@ class GitDocument extends Document implements RefsChangedListener { ObjectLoader loader = repository.open(id, Constants.OBJ_BLOB); byte[] bytes = loader.getBytes(); String charset; - charset = CompareUtils.getResourceEncoding(resource); + charset = CompareCoreUtils.getResourceEncoding(resource); // Finally we could consider validating the content with respect // to the content. We don't do that here. String s = new String(bytes, charset); diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CompareTreeView.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CompareTreeView.java index c820fb6149..c7830e9eb8 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CompareTreeView.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CompareTreeView.java @@ -35,6 +35,7 @@ import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.egit.core.AdaptableFileTreeIterator; +import org.eclipse.egit.core.internal.CompareCoreUtils; import org.eclipse.egit.core.internal.storage.GitFileRevision; import org.eclipse.egit.core.project.RepositoryMapping; import org.eclipse.egit.ui.Activator; @@ -256,7 +257,7 @@ public class CompareTreeView extends ViewPart { UIText.CompareTreeView_ItemNotFoundInVersionMessage, res.getName(), getCompareVersion())); } else { - String encoding = CompareUtils.getResourceEncoding(res); + String encoding = CompareCoreUtils.getResourceEncoding(res); right = new FileRevisionTypedElement(rightRevision, encoding); } GitCompareFileRevisionEditorInput compareInput = new GitCompareFileRevisionEditorInput( diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitInfoBuilder.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitInfoBuilder.java index acd9097cac..4e751b642e 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitInfoBuilder.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitInfoBuilder.java @@ -31,10 +31,10 @@ import java.util.regex.Pattern; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.egit.core.internal.CompareCoreUtils; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.UIPreferences; import org.eclipse.egit.ui.UIText; -import org.eclipse.egit.ui.internal.CompareUtils; import org.eclipse.egit.ui.internal.history.CommitMessageViewer.ObjectLink; import org.eclipse.egit.ui.internal.trace.GitTraceLocation; import org.eclipse.jgit.diff.DiffFormatter; @@ -410,7 +410,7 @@ public class CommitInfoBuilder { String path = currentDiff.getPath(); monitor.setTaskName(NLS.bind( UIText.CommitMessageViewer_BuildDiffTaskName, path)); - currentEncoding[0] = CompareUtils.getResourceEncoding(db, + currentEncoding[0] = CompareCoreUtils.getResourceEncoding(db, path); d.append(formatPathLine(path)).append(LF); currentDiff.outputDiff(d, db, diffFmt, true); diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitCreatePatchWizard.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitCreatePatchWizard.java index 9caba48d1d..eb6d172f14 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitCreatePatchWizard.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitCreatePatchWizard.java @@ -11,25 +11,20 @@ *******************************************************************************/ package org.eclipse.egit.ui.internal.history; -import java.io.BufferedOutputStream; import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.io.Writer; import java.lang.reflect.InvocationTargetException; -import java.net.URLEncoder; -import java.text.SimpleDateFormat; -import java.util.Locale; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; +import org.eclipse.egit.core.op.CreatePatchOperation; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.UIIcons; import org.eclipse.egit.ui.UIText; -import org.eclipse.egit.ui.internal.CompareUtils; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.operation.IRunnableWithProgress; @@ -37,10 +32,8 @@ import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.wizard.Wizard; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; @@ -65,17 +58,12 @@ public class GitCreatePatchWizard extends Wizard { private RevCommit commit; - private TreeWalk walker; - private Repository db; private LocationPage locationPage; private OptionsPage optionsPage; - // the encoding for the currently processed file - private String currentEncoding = null; - // The initial size of this wizard. private final static int INITIAL_WIDTH = 300; @@ -85,14 +73,12 @@ public class GitCreatePatchWizard extends Wizard { * * @param part * @param commit - * @param walker * @param db */ public static void run(IWorkbenchPart part, final RevCommit commit, - TreeWalk walker, Repository db) { + Repository db) { final String title = UIText.GitCreatePatchWizard_CreatePatchTitle; - final GitCreatePatchWizard wizard = new GitCreatePatchWizard(commit, - walker, db); + final GitCreatePatchWizard wizard = new GitCreatePatchWizard(commit, db); wizard.setWindowTitle(title); WizardDialog dialog = new WizardDialog(part.getSite().getShell(), wizard); @@ -103,15 +89,13 @@ public class GitCreatePatchWizard extends Wizard { /** * Creates a wizard which is used to export the changes introduced by a - * commit, filtered by a TreeWalk + * commit. * * @param commit - * @param walker * @param db */ - public GitCreatePatchWizard(RevCommit commit, TreeWalk walker, Repository db) { + public GitCreatePatchWizard(RevCommit commit, Repository db) { this.commit = commit; - this.walker = walker; this.db = db; } @@ -133,59 +117,30 @@ public class GitCreatePatchWizard extends Wizard { @Override public boolean performFinish() { - final boolean isGit = optionsPage.gitFormat.getSelection(); + final CreatePatchOperation operation = new CreatePatchOperation(db, + commit); + operation.useGitFormat(optionsPage.gitFormat.getSelection()); + final boolean isFile = locationPage.fsRadio.getSelection(); final String fileName = locationPage.fsPathText.getText(); try { getContainer().run(true, true, new IRunnableWithProgress() { - public void run(IProgressMonitor monitor) { - final StringBuilder sb = new StringBuilder(); - final DiffFormatter diffFmt = new DiffFormatter( - new BufferedOutputStream(new ByteArrayOutputStream() { - - @Override - public synchronized void write(byte[] b, int off, int len) { - super.write(b, off, len); - if (currentEncoding == null) - sb.append(toString()); - else try { - sb.append(toString(currentEncoding)); - } catch (UnsupportedEncodingException e) { - sb.append(toString()); - } - reset(); - } - - })); - - if (isGit) - writeGitPatchHeader(sb); + public void run(IProgressMonitor monitor) + throws InvocationTargetException { try { - FileDiff[] diffs = FileDiff.compute(walker, commit); - for (FileDiff diff : diffs) { - currentEncoding = CompareUtils. - getResourceEncoding(db, diff.getPath()); - diff.outputDiff(sb, db, diffFmt, isGit); - diffFmt.flush(); - } + operation.execute(monitor); + String content = operation.getPatchContent(); if (isFile) { - Writer output = new BufferedWriter(new FileWriter( - fileName)); - try { - // FileWriter always assumes default encoding is - // OK! - output.write(sb.toString()); - } finally { - output.close(); - } + writeToFile(fileName, content); } else { - copyToClipboard(sb.toString()); + copyToClipboard(content); } } catch (IOException e) { - Activator - .logError("Patch file could not be written", e); //$NON-NLS-1$ + throw new InvocationTargetException(e); + } catch (CoreException e) { + throw new InvocationTargetException(e); } } }); @@ -202,31 +157,6 @@ public class GitCreatePatchWizard extends Wizard { return true; } - private void writeGitPatchHeader(StringBuilder sb) { - - final SimpleDateFormat dtfmt; - dtfmt = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.US); //$NON-NLS-1$ - dtfmt.setTimeZone(commit.getAuthorIdent().getTimeZone()); - sb.append("From").append(" ") //$NON-NLS-1$ //$NON-NLS-2$ - .append(commit.getId().getName()).append(" ") //$NON-NLS-1$ - .append(dtfmt.format(Long.valueOf(System.currentTimeMillis()))) - .append("\n"); //$NON-NLS-1$ - sb.append("From") //$NON-NLS-1$ - .append(": ") //$NON-NLS-1$ - .append(commit.getAuthorIdent().getName()) - .append(" <").append(commit.getAuthorIdent().getEmailAddress()) //$NON-NLS-1$ - .append(">\n"); //$NON-NLS-1$ - sb.append("Date").append(": ") //$NON-NLS-1$ //$NON-NLS-2$ - .append(dtfmt.format(commit.getAuthorIdent().getWhen())) - .append("\n"); //$NON-NLS-1$ - sb.append("Subject").append(": [PATCH] ") //$NON-NLS-1$ //$NON-NLS-2$ - .append(commit.getShortMessage()); - - String message = commit.getFullMessage().substring( - commit.getShortMessage().length()); - sb.append(message).append("\n\n"); //$NON-NLS-1$ - } - private void copyToClipboard(final String content) { getShell().getDisplay().syncExec(new Runnable() { public void run() { @@ -239,6 +169,18 @@ public class GitCreatePatchWizard extends Wizard { }); } + private void writeToFile(final String fileName, String content) + throws IOException { + Writer output = new BufferedWriter(new FileWriter(fileName)); + try { + // FileWriter always assumes default encoding is + // OK! + output.write(content); + } finally { + output.close(); + } + } + /** * A wizard page to choose the target location */ @@ -358,23 +300,9 @@ public class GitCreatePatchWizard extends Wizard { } private String createFileName() { - String name = commit.getShortMessage(); - - name = name.trim(); - try { - name = URLEncoder.encode(name, "UTF-8"); //$NON-NLS-1$ - } catch (UnsupportedEncodingException e) { - // We're pretty sure that UTF-8 will be supported in future - } - if (name.length() > 80) - name = name.substring(0, 80); - while (name.endsWith(".")) //$NON-NLS-1$ - name = name.substring(0, name.length() - 1); - name = name.concat(".patch"); //$NON-NLS-1$ - + String suggestedFileName = CreatePatchOperation.suggestFileName(commit); String defaultPath = db.getWorkTree().getAbsolutePath(); - - return (new File(defaultPath, name)).getPath(); + return (new File(defaultPath, suggestedFileName)).getPath(); } /** diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/command/CreatePatchHandler.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/command/CreatePatchHandler.java index ddd8eefcca..3df9d7d548 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/command/CreatePatchHandler.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/command/CreatePatchHandler.java @@ -15,8 +15,6 @@ import org.eclipse.egit.ui.internal.history.GitHistoryPage; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.treewalk.TreeWalk; -import org.eclipse.jgit.treewalk.filter.TreeFilter; /** * Create a patch based on a commit. @@ -28,10 +26,7 @@ public class CreatePatchHandler extends AbstractHistoryCommandHandler { if (selection.size() == 1) { RevCommit commit = (RevCommit) selection.getFirstElement(); Repository repo = getRepository(event); - TreeWalk fileWalker = new TreeWalk(repo); - fileWalker.setRecursive(true); - fileWalker.setFilter(TreeFilter.ANY_DIFF); - GitCreatePatchWizard.run(getPart(event), commit, fileWalker, repo); + GitCreatePatchWizard.run(getPart(event), commit, repo); } return null; } |