aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java42
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java189
-rw-r--r--org.eclipse.jgit.ui/build.properties3
-rw-r--r--org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AWTPlotRenderer.java7
-rw-r--r--org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java1
-rw-r--r--org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java5
-rw-r--r--org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml4
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java80
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java120
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java18
16 files changed, 419 insertions, 78 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index de75fd5b61..48ede966f9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -48,7 +48,9 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
import org.eclipse.jgit.api.MergeResult.MergeStatus;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.lib.StoredConfig;
@@ -125,6 +127,46 @@ public class PullCommandTest extends RepositoryTestCase {
assertFileContentsEqual(targetFile, result);
}
+ public void testPullLocalConflict() throws Exception {
+ target.branchCreate().setName("basedOnMaster").setStartPoint(
+ "refs/heads/master").setUpstreamMode(SetupUpstreamMode.TRACK)
+ .call();
+ target.getRepository().updateRef(Constants.HEAD).link(
+ "refs/heads/basedOnMaster");
+ PullResult res = target.pull().call();
+ // nothing to update since we don't have different data yet
+ assertNull(res.getFetchResult());
+ assertTrue(res.getMergeResult().getMergeStatus().equals(
+ MergeStatus.ALREADY_UP_TO_DATE));
+
+ assertFileContentsEqual(targetFile, "Hello world");
+
+ // change the file in master
+ target.getRepository().updateRef(Constants.HEAD).link(
+ "refs/heads/master");
+ writeToFile(targetFile, "Master change");
+ target.add().addFilepattern("SomeFile.txt").call();
+ target.commit().setMessage("Source change in master").call();
+
+ // change the file in slave
+ target.getRepository().updateRef(Constants.HEAD).link(
+ "refs/heads/basedOnMaster");
+ writeToFile(targetFile, "Slave change");
+ target.add().addFilepattern("SomeFile.txt").call();
+ target.commit().setMessage("Source change in based on master").call();
+
+ res = target.pull().call();
+
+ String sourceChangeString = "Master change\n>>>>>>> branch 'refs/heads/master' of local repository";
+
+ assertNull(res.getFetchResult());
+ assertEquals(res.getMergeResult().getMergeStatus(),
+ MergeStatus.CONFLICTING);
+ String result = "<<<<<<< HEAD\nSlave change\n=======\n"
+ + sourceChangeString + "\n";
+ assertFileContentsEqual(targetFile, result);
+ }
+
@Override
protected void setUp() throws Exception {
super.setUp();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
new file mode 100644
index 0000000000..6839f8d3c1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.TestCase;
+
+public class ThreadSafeProgressMonitorTest extends TestCase {
+ public void testFailsMethodsOnBackgroundThread()
+ throws InterruptedException {
+ final MockProgressMonitor mock = new MockProgressMonitor();
+ final ThreadSafeProgressMonitor pm = new ThreadSafeProgressMonitor(mock);
+
+ runOnThread(new Runnable() {
+ public void run() {
+ try {
+ pm.start(1);
+ fail("start did not fail on background thread");
+ } catch (IllegalStateException notMainThread) {
+ // Expected result
+ }
+
+ try {
+ pm.beginTask("title", 1);
+ fail("beginTask did not fail on background thread");
+ } catch (IllegalStateException notMainThread) {
+ // Expected result
+ }
+
+ try {
+ pm.endTask();
+ fail("endTask did not fail on background thread");
+ } catch (IllegalStateException notMainThread) {
+ // Expected result
+ }
+ }
+ });
+
+ // Ensure we didn't alter the mock above when checking threads.
+ assertNull(mock.taskTitle);
+ assertEquals(0, mock.value);
+ }
+
+ public void testMethodsOkOnMainThread() {
+ final MockProgressMonitor mock = new MockProgressMonitor();
+ final ThreadSafeProgressMonitor pm = new ThreadSafeProgressMonitor(mock);
+
+ pm.start(1);
+ assertEquals(1, mock.value);
+
+ pm.beginTask("title", 42);
+ assertEquals("title", mock.taskTitle);
+ assertEquals(42, mock.value);
+
+ pm.update(1);
+ assertEquals(43, mock.value);
+
+ pm.update(2);
+ assertEquals(45, mock.value);
+
+ pm.endTask();
+ assertNull(mock.taskTitle);
+ assertEquals(0, mock.value);
+ }
+
+ public void testUpdateOnBackgroundThreads() throws InterruptedException {
+ final MockProgressMonitor mock = new MockProgressMonitor();
+ final ThreadSafeProgressMonitor pm = new ThreadSafeProgressMonitor(mock);
+
+ pm.startWorker();
+
+ final CountDownLatch doUpdate = new CountDownLatch(1);
+ final CountDownLatch didUpdate = new CountDownLatch(1);
+ final CountDownLatch doEndWorker = new CountDownLatch(1);
+
+ final Thread bg = new Thread() {
+ public void run() {
+ assertFalse(pm.isCancelled());
+
+ await(doUpdate);
+ pm.update(2);
+ didUpdate.countDown();
+
+ await(doEndWorker);
+ pm.update(1);
+ pm.endWorker();
+ }
+ };
+ bg.start();
+
+ pm.pollForUpdates();
+ assertEquals(0, mock.value);
+ doUpdate.countDown();
+
+ await(didUpdate);
+ pm.pollForUpdates();
+ assertEquals(2, mock.value);
+
+ doEndWorker.countDown();
+ pm.waitForCompletion();
+ assertEquals(3, mock.value);
+ }
+
+ private static void await(CountDownLatch cdl) {
+ try {
+ assertTrue("latch released", cdl.await(1000, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException ie) {
+ fail("Did not expect to be interrupted");
+ }
+ }
+
+ private static void runOnThread(Runnable task) throws InterruptedException {
+ Thread t = new Thread(task);
+ t.start();
+ t.join(1000);
+ assertFalse("thread has stopped", t.isAlive());
+ }
+
+ private static class MockProgressMonitor implements ProgressMonitor {
+ String taskTitle;
+
+ int value;
+
+ public void update(int completed) {
+ value += completed;
+ }
+
+ public void start(int totalTasks) {
+ value = totalTasks;
+ }
+
+ public void beginTask(String title, int totalWork) {
+ taskTitle = title;
+ value = totalWork;
+ }
+
+ public void endTask() {
+ taskTitle = null;
+ value = 0;
+ }
+
+ public boolean isCancelled() {
+ return false;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ui/build.properties b/org.eclipse.jgit.ui/build.properties
index aa1a008269..84f1c95cfa 100644
--- a/org.eclipse.jgit.ui/build.properties
+++ b/org.eclipse.jgit.ui/build.properties
@@ -1,4 +1,5 @@
-source.. = src/
+source.. = src/,\
+ resources/
output.. = bin/
bin.includes = META-INF/,\
.,\
diff --git a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AWTPlotRenderer.java b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AWTPlotRenderer.java
index 4a5d4603ca..e699a72cfa 100644
--- a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AWTPlotRenderer.java
+++ b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AWTPlotRenderer.java
@@ -49,6 +49,7 @@ import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
+import java.io.Serializable;
import org.eclipse.jgit.awtui.CommitGraphPane.GraphCellRender;
import org.eclipse.jgit.awtui.SwingCommitList.SwingLane;
@@ -57,11 +58,13 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revplot.AbstractPlotRenderer;
import org.eclipse.jgit.revplot.PlotCommit;
-final class AWTPlotRenderer extends AbstractPlotRenderer<SwingLane, Color> {
+final class AWTPlotRenderer extends AbstractPlotRenderer<SwingLane, Color>
+ implements Serializable {
+ private static final long serialVersionUID = 1L;
final GraphCellRender cell;
- Graphics2D g;
+ transient Graphics2D g;
AWTPlotRenderer(final GraphCellRender c) {
cell = c;
diff --git a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java
index 13d408f19f..9c9d1f4a5c 100644
--- a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java
+++ b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java
@@ -222,6 +222,7 @@ public class CommitGraphPane extends JTable {
PlotCommit<SwingLane> commit;
+ @SuppressWarnings("unchecked")
public Component getTableCellRendererComponent(final JTable table,
final Object value, final boolean isSelected,
final boolean hasFocus, final int row, final int column) {
diff --git a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java
index b58547a9e1..d8660e5ed6 100644
--- a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java
+++ b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java
@@ -87,5 +87,10 @@ class SwingCommitList extends PlotCommitList<SwingCommitList.SwingLane> {
public boolean equals(Object o) {
return super.equals(o) && color.equals(((SwingLane)o).color);
}
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ color.hashCode();
+ }
}
}
diff --git a/org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml b/org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml
index ba9d1d0996..0383ad9b87 100644
--- a/org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml
+++ b/org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml
@@ -4,7 +4,7 @@
memory mapped segments if the JVM heap is out of address space.
-->
<Match>
- <Class name="org.eclipse.jgit.lib.PackFile" />
+ <Class name="org.eclipse.jgit.storage.file.PackFile" />
<Method name="mmap" />
<Bug pattern="DM_GC" />
</Match>
@@ -21,7 +21,7 @@
here with == assuming .equals() style equality.
-->
<Match>
- <Class name="org.eclipse.jgit.lib.util.StringUtils" />
+ <Class name="org.eclipse.jgit.util.StringUtils" />
<Bug pattern="ES_COMPARING_PARAMETER_STRING_WITH_EQ" />
</Match>
</FindBugsFilter>
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
index 409b43492a..8af6e315fc 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
@@ -113,6 +113,7 @@ corruptionDetectedReReadingAt=Corruption detected re-reading at {0}
couldNotCheckOutBecauseOfConflicts=Could not check out because of conflicts
couldNotDeleteLockFileShouldNotHappen=Could not delete lock file. Should not happen
couldNotDeleteTemporaryIndexFileShouldNotHappen=Could not delete temporary index file. Should not happen
+couldNotGetAdvertisedRef=Could not get advertised Ref for branch {0}
couldNotLockHEAD=Could not lock HEAD
couldNotReadIndexInOneGo=Could not read index in one go, only {0} out of {1} read
couldNotRenameDeleteOldIndex=Could not rename delete old index
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
index d6c6018006..6a75a9eb86 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
@@ -173,6 +173,7 @@ public class JGitText extends TranslationBundle {
/***/ public String couldNotCheckOutBecauseOfConflicts;
/***/ public String couldNotDeleteLockFileShouldNotHappen;
/***/ public String couldNotDeleteTemporaryIndexFileShouldNotHappen;
+ /***/ public String couldNotGetAdvertisedRef;
/***/ public String couldNotLockHEAD;
/***/ public String couldNotReadIndexInOneGo;
/***/ public String couldNotRenameDeleteOldIndex;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index b7f09d9a79..acbf3f10aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -157,14 +157,6 @@ public class PullCommand extends GitCommand<PullResult> {
throw new InvalidConfigurationException(MessageFormat.format(
JGitText.get().missingConfigurationForKey, missingKey));
}
- final String remoteUri = repo.getConfig().getString("remote", remote,
- ConfigConstants.CONFIG_KEY_URL);
- if (remoteUri == null) {
- String missingKey = ConfigConstants.CONFIG_REMOTE_SECTION + DOT
- + remote + DOT + ConfigConstants.CONFIG_KEY_URL;
- throw new InvalidConfigurationException(MessageFormat.format(
- JGitText.get().missingConfigurationForKey, missingKey));
- }
// get the name of the branch in the remote repository
// stored in configuration key branch.<branch name>.merge
@@ -175,13 +167,14 @@ public class PullCommand extends GitCommand<PullResult> {
// check if the branch is configured for pull-rebase
remoteBranchName = repoConfig.getString(
ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
- ConfigConstants.CONFIG_KEY_MERGE);
+ ConfigConstants.CONFIG_KEY_REBASE);
if (remoteBranchName != null) {
// TODO implement pull-rebase
throw new JGitInternalException(
"Pull with rebase is not yet supported");
}
}
+
if (remoteBranchName == null) {
String missingKey = ConfigConstants.CONFIG_BRANCH_SECTION + DOT
+ branchName + DOT + ConfigConstants.CONFIG_KEY_MERGE;
@@ -189,45 +182,66 @@ public class PullCommand extends GitCommand<PullResult> {
JGitText.get().missingConfigurationForKey, missingKey));
}
- if (monitor.isCancelled())
- throw new CanceledException(MessageFormat.format(
- JGitText.get().operationCanceled,
- JGitText.get().pullTaskName));
+ final boolean isRemote = !remote.equals(".");
+ String remoteUri;
+ FetchResult fetchRes;
+ if (isRemote) {
+ remoteUri = repo.getConfig().getString("remote", remote,
+ ConfigConstants.CONFIG_KEY_URL);
+ if (remoteUri == null) {
+ String missingKey = ConfigConstants.CONFIG_REMOTE_SECTION + DOT
+ + remote + DOT + ConfigConstants.CONFIG_KEY_URL;
+ throw new InvalidConfigurationException(MessageFormat.format(
+ JGitText.get().missingConfigurationForKey, missingKey));
+ }
- FetchCommand fetch = new FetchCommand(repo);
- fetch.setRemote(remote);
- if (monitor != null)
- fetch.setProgressMonitor(monitor);
- fetch.setTimeout(this.timeout);
+ if (monitor.isCancelled())
+ throw new CanceledException(MessageFormat.format(
+ JGitText.get().operationCanceled,
+ JGitText.get().pullTaskName));
- FetchResult fetchRes = fetch.call();
+ FetchCommand fetch = new FetchCommand(repo);
+ fetch.setRemote(remote);
+ if (monitor != null)
+ fetch.setProgressMonitor(monitor);
+ fetch.setTimeout(this.timeout);
+
+ fetchRes = fetch.call();
+ } else {
+ // we can skip the fetch altogether
+ remoteUri = "local repository";
+ fetchRes = null;
+ }
monitor.update(1);
// we check the updates to see which of the updated branches corresponds
// to the remote branch name
- AnyObjectId commitToMerge = null;
+ AnyObjectId commitToMerge;
- Ref r = fetchRes.getAdvertisedRef(remoteBranchName);
- if (r == null)
- r = fetchRes.getAdvertisedRef(Constants.R_HEADS + remoteBranchName);
- if (r == null) {
- // TODO: we should be able to get the mapping also if nothing was
- // updated by the fetch; for the time being, use the naming
- // convention as fall back
- String remoteTrackingBranch = Constants.R_REMOTES + remote + '/'
- + branchName;
+ if (isRemote) {
+ Ref r = null;
+ if (fetchRes != null) {
+ r = fetchRes.getAdvertisedRef(remoteBranchName);
+ if (r == null)
+ r = fetchRes.getAdvertisedRef(Constants.R_HEADS
+ + remoteBranchName);
+ }
+ if (r == null)
+ throw new JGitInternalException(MessageFormat.format(JGitText
+ .get().couldNotGetAdvertisedRef, remoteBranchName));
+ else
+ commitToMerge = r.getObjectId();
+ } else {
try {
- commitToMerge = repo.resolve(remoteTrackingBranch);
+ commitToMerge = repo.resolve(remoteBranchName);
} catch (IOException e) {
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
e);
}
-
- } else
- commitToMerge = r.getObjectId();
+ }
if (monitor.isCancelled())
throw new CanceledException(MessageFormat.format(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java
index 3459109b6e..9d309d5076 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java
@@ -300,7 +300,7 @@ public class MyersDiff<S extends Sequence> {
final int getIndex(int d, int k) {
// TODO: remove
-if (((d + k - middleK) % 2) == 1)
+if (((d + k - middleK) % 2) != 0)
throw new RuntimeException(MessageFormat.format(JGitText.get().unexpectedOddResult, d, k, middleK));
return (d + k - middleK) / 2;
}
@@ -472,7 +472,7 @@ if (k < beginK || k > endK)
if (k < backward.beginK || k > backward.endK)
return false;
// TODO: move out of loop
- if (((d - 1 + k - backward.middleK) % 2) == 1)
+ if (((d - 1 + k - backward.middleK) % 2) != 0)
return false;
if (x < backward.getX(d - 1, k))
return false;
@@ -514,7 +514,7 @@ if (k < beginK || k > endK)
if (k < forward.beginK || k > forward.endK)
return false;
// TODO: move out of loop
- if (((d + k - forward.middleK) % 2) == 1)
+ if (((d + k - forward.middleK) % 2) != 0)
return false;
if (x > forward.getX(d, k))
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java
index 38937410d0..57bea42db4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.lib;
+import java.io.Serializable;
import java.text.MessageFormat;
import org.eclipse.jgit.JGitText;
@@ -61,7 +62,9 @@ import org.eclipse.jgit.util.RawParseUtils;
* This class converts the hex string into a binary form, to make it more
* efficient for matching against an object.
*/
-public final class AbbreviatedObjectId {
+public final class AbbreviatedObjectId implements Serializable {
+ private static final long serialVersionUID = 1L;
+
/**
* Test a string of characters to verify it is a hex format.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
index 9708bb2f92..9e8e256b01 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
@@ -43,16 +43,35 @@
package org.eclipse.jgit.lib;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* Wrapper around the general {@link ProgressMonitor} to make it thread safe.
+ *
+ * Updates to the underlying ProgressMonitor are made only from the thread that
+ * allocated this wrapper. Callers are responsible for ensuring the allocating
+ * thread uses {@link #pollForUpdates()} or {@link #waitForCompletion()} to
+ * update the underlying ProgressMonitor.
+ *
+ * Only {@link #update(int)}, {@link #isCancelled()}, and {@link #endWorker()}
+ * may be invoked from a worker thread. All other methods of the ProgressMonitor
+ * interface can only be called from the thread that allocates this wrapper.
*/
public class ThreadSafeProgressMonitor implements ProgressMonitor {
private final ProgressMonitor pm;
private final ReentrantLock lock;
+ private final Thread mainThread;
+
+ private final AtomicInteger workers;
+
+ private final AtomicInteger pendingUpdates;
+
+ private final Semaphore process;
+
/**
* Wrap a ProgressMonitor to be thread safe.
*
@@ -62,33 +81,87 @@ public class ThreadSafeProgressMonitor implements ProgressMonitor {
public ThreadSafeProgressMonitor(ProgressMonitor pm) {
this.pm = pm;
this.lock = new ReentrantLock();
+ this.mainThread = Thread.currentThread();
+ this.workers = new AtomicInteger(0);
+ this.pendingUpdates = new AtomicInteger(0);
+ this.process = new Semaphore(0);
}
public void start(int totalTasks) {
- lock.lock();
- try {
- pm.start(totalTasks);
- } finally {
- lock.unlock();
- }
+ if (!isMainThread())
+ throw new IllegalStateException();
+ pm.start(totalTasks);
}
public void beginTask(String title, int totalWork) {
- lock.lock();
- try {
- pm.beginTask(title, totalWork);
- } finally {
- lock.unlock();
+ if (!isMainThread())
+ throw new IllegalStateException();
+ pm.beginTask(title, totalWork);
+ }
+
+ /** Notify the monitor a worker is starting. */
+ public void startWorker() {
+ startWorkers(1);
+ }
+
+ /**
+ * Notify the monitor of workers starting.
+ *
+ * @param count
+ * the number of worker threads that are starting.
+ */
+ public void startWorkers(int count) {
+ workers.addAndGet(count);
+ }
+
+ /** Notify the monitor a worker is finished. */
+ public void endWorker() {
+ if (workers.decrementAndGet() == 0)
+ process.release();
+ }
+
+ /**
+ * Non-blocking poll for pending updates.
+ *
+ * This method can only be invoked by the same thread that allocated this
+ * ThreadSafeProgressMonior.
+ */
+ public void pollForUpdates() {
+ assert isMainThread();
+ doUpdates();
+ }
+
+ /**
+ * Process pending updates and wait for workers to finish.
+ *
+ * This method can only be invoked by the same thread that allocated this
+ * ThreadSafeProgressMonior.
+ *
+ * @throws InterruptedException
+ * if the main thread is interrupted while waiting for
+ * completion of workers.
+ */
+ public void waitForCompletion() throws InterruptedException {
+ assert isMainThread();
+ while (0 < workers.get()) {
+ doUpdates();
+ process.acquire();
}
+ doUpdates();
+ }
+
+ private void doUpdates() {
+ int cnt = pendingUpdates.getAndSet(0);
+ if (0 < cnt)
+ pm.update(cnt);
}
public void update(int completed) {
- lock.lock();
- try {
- pm.update(completed);
- } finally {
- lock.unlock();
- }
+ int old = pendingUpdates.getAndAdd(completed);
+ if (isMainThread())
+ doUpdates();
+ else if (old == 0)
+ process.release();
}
public boolean isCancelled() {
@@ -101,11 +174,12 @@ public class ThreadSafeProgressMonitor implements ProgressMonitor {
}
public void endTask() {
- lock.lock();
- try {
- pm.endTask();
- } finally {
- lock.unlock();
- }
+ if (!isMainThread())
+ throw new IllegalStateException();
+ pm.endTask();
+ }
+
+ private boolean isMainThread() {
+ return Thread.currentThread() == mainThread;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
index 2e4489cda4..96c8361adb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
@@ -800,6 +800,13 @@ public class RefDirectory extends RefDatabase {
final LooseRef n = scanRef(null, name);
if (n == null)
return packed.get(name);
+
+ // check whether the found new ref is the an additional ref. These refs
+ // should not go into looseRefs
+ for (int i = 0; i < additionalRefsNames.length; i++)
+ if (name.equals(additionalRefsNames[i]))
+ return n;
+
if (looseRefs.compareAndSet(curList, curList.add(idx, n)))
modCnt.incrementAndGet();
return n;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java
index 5e551e9d49..aa0374618a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java
@@ -46,7 +46,7 @@ package org.eclipse.jgit.storage.pack;
import java.util.concurrent.Callable;
import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
final class DeltaTask implements Callable<Object> {
private final PackConfig config;
@@ -55,7 +55,7 @@ final class DeltaTask implements Callable<Object> {
private final DeltaCache dc;
- private final ProgressMonitor pm;
+ private final ThreadSafeProgressMonitor pm;
private final int batchSize;
@@ -64,7 +64,8 @@ final class DeltaTask implements Callable<Object> {
private final ObjectToPack[] list;
DeltaTask(PackConfig config, ObjectReader reader, DeltaCache dc,
- ProgressMonitor pm, int batchSize, int start, ObjectToPack[] list) {
+ ThreadSafeProgressMonitor pm, int batchSize, int start,
+ ObjectToPack[] list) {
this.config = config;
this.templateReader = reader;
this.dc = dc;
@@ -82,6 +83,7 @@ final class DeltaTask implements Callable<Object> {
dw.search(pm, list, start, batchSize);
} finally {
or.release();
+ pm.endWorker();
}
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
index 20c4bb0f97..5986aca4e3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
@@ -59,7 +59,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
@@ -675,7 +674,7 @@ public class PackWriter {
}
final DeltaCache dc = new ThreadSafeDeltaCache(config);
- final ProgressMonitor pm = new ThreadSafeProgressMonitor(monitor);
+ final ThreadSafeProgressMonitor pm = new ThreadSafeProgressMonitor(monitor);
// Guess at the size of batch we want. Because we don't really
// have a way for a thread to steal work from another thread if
@@ -713,6 +712,7 @@ public class PackWriter {
i += batchSize;
myTasks.add(new DeltaTask(config, reader, dc, pm, batchSize, start, list));
}
+ pm.startWorkers(myTasks.size());
final Executor executor = config.getExecutor();
final List<Throwable> errors = Collections
@@ -720,7 +720,7 @@ public class PackWriter {
if (executor instanceof ExecutorService) {
// Caller supplied us a service, use it directly.
//
- runTasks((ExecutorService) executor, myTasks, errors);
+ runTasks((ExecutorService) executor, pm, myTasks, errors);
} else if (executor == null) {
// Caller didn't give us a way to run the tasks, spawn up a
@@ -728,7 +728,7 @@ public class PackWriter {
//
ExecutorService pool = Executors.newFixedThreadPool(threads);
try {
- runTasks(pool, myTasks, errors);
+ runTasks(pool, pm, myTasks, errors);
} finally {
pool.shutdown();
for (;;) {
@@ -746,7 +746,6 @@ public class PackWriter {
// asynchronous execution. Wrap everything and hope it
// can schedule these for us.
//
- final CountDownLatch done = new CountDownLatch(myTasks.size());
for (final DeltaTask task : myTasks) {
executor.execute(new Runnable() {
public void run() {
@@ -754,14 +753,12 @@ public class PackWriter {
task.call();
} catch (Throwable failure) {
errors.add(failure);
- } finally {
- done.countDown();
}
}
});
}
try {
- done.await();
+ pm.waitForCompletion();
} catch (InterruptedException ie) {
// We can't abort the other tasks as we have no handle.
// Cross our fingers and just break out anyway.
@@ -789,13 +786,14 @@ public class PackWriter {
}
}
- private void runTasks(ExecutorService pool, List<DeltaTask> tasks,
- List<Throwable> errors) throws IOException {
+ private void runTasks(ExecutorService pool, ThreadSafeProgressMonitor pm,
+ List<DeltaTask> tasks, List<Throwable> errors) throws IOException {
List<Future<?>> futures = new ArrayList<Future<?>>(tasks.size());
for (DeltaTask task : tasks)
futures.add(pool.submit(task));
try {
+ pm.waitForCompletion();
for (Future<?> f : futures) {
try {
f.get();