git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [EGIT PATCH 00/23] Push implementation
@ 2008-06-27 22:06 Marek Zawirski
  2008-06-27 22:06 ` [EGIT PATCH 01/23] Fix: let FetchProcess use fetch() instead of doFetch() Marek Zawirski
  2008-06-27 23:25 ` [EGIT PATCH 00/23] Push implementation Robin Rosenberg
  0 siblings, 2 replies; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Finally, series with push implementation. It has taken a "while" to
squash all bugs this time and polish the series, uhh.

Part of this series is signifficant refactor of .transport package to
support code reuse between fetch and push implementations + minor
fixes/improvements.

Needed push operation elements were created. Implementation provides
basis for different push protocols by common push process and its
dendendencies. Push over git-receive-pack based transports is
implemented. 
Shawn is now working on support for push over less git-inteligent
protocols. I'm moving into GUI stuff.

Beside of JUnit test cases for (IMO) 2 most complex classes that
revealed few bugs, I've tested manually pgm.Push extensively. 
Each protocol (SSH, local, git-daemon) was tested at least 1 time.


So now I can say, that this branch is available on 
http://repo.or.cz/w/egit/zawir.git?a=shortlog;h=refs/heads/push
and it was pushed with jgit push! :) Yeah!
It is based (together with packwriter stuff, not old packwrite branch)
on egit's master: 4cf736fdf3.

BTW, in case of any comments, requests, please consider that I'm
semi-off-line next ~2,5weeks, I can sync my laptop each few days,
but certainly not everyday.

PS I saw that Shawn's sftp-push branch also contains new 
TransportException constructors conversion, so we may have to 
resolve this duplication, I added it as last commit.

Marek Zawirski (23):
  Fix: let FetchProcess use fetch() instead of doFetch()
  RefUpdate: new possible result Result.IO_FAILURE
  Refactor TrackingRefUpdate to not hold RefSpec
  New constructor without RefSpec for TrackingRefUpdate
  Add RemoteRefUpdate class
  Refactor: extract superclass OperationResult from FetchResult
  Add PushResult class
  Support for fetchThin and pushThin options in Transport
  Big refactor: *Connection hierarchy
  Add ignoreMissingUninteresting option to PackWriter
  Add BasePackPushConnection implementing git-send-pack protocol
  Fix: let RevWalk reset correctly before isMergedInto()
  Add PushProcess class implementing git-send-pack logic
  Clarify Repository#resolve() documentation
  Add String versions of methods in RefSpec
  Transport* - general support for push() and implementations
  Test cases for PushProcess
  Test cases for RefSpec to RemoteRefUpdate conversions
  Repository search for command line tools
  Push command line utility
  Don't accept RefSpec with null source for fetch
  Add new handy constructors to TransportException,
    PackProtocolException
  Use new TransportException constructors

 .../tst/org/spearce/jgit/lib/PackWriterTest.java   |   45 ++-
 .../spearce/jgit/transport/PushProcessTest.java    |  407 ++++++++++++++++++++
 .../org/spearce/jgit/transport/TransportTest.java  |  181 +++++++++
 .../spearce/jgit/errors/PackProtocolException.java |   30 ++
 .../spearce/jgit/errors/TransportException.java    |   31 ++
 .../src/org/spearce/jgit/lib/PackWriter.java       |   37 ++-
 .../src/org/spearce/jgit/lib/RefUpdate.java        |   27 ++-
 .../src/org/spearce/jgit/lib/Repository.java       |    2 +-
 .../src/org/spearce/jgit/pgm/Fetch.java            |   41 +--
 .../src/org/spearce/jgit/pgm/Main.java             |   24 +-
 .../src/org/spearce/jgit/pgm/Push.java             |  235 +++++++++++
 .../src/org/spearce/jgit/pgm/TextBuiltin.java      |   21 +
 .../src/org/spearce/jgit/revwalk/RevWalk.java      |    2 +-
 .../org/spearce/jgit/transport/BaseConnection.java |  103 +++++
 .../jgit/transport/BaseFetchConnection.java        |   86 ++++
 .../spearce/jgit/transport/BasePackConnection.java |  217 +++++++++++
 ...onnection.java => BasePackFetchConnection.java} |  169 +--------
 .../jgit/transport/BasePackPushConnection.java     |  226 +++++++++++
 .../src/org/spearce/jgit/transport/Connection.java |  104 +++++
 .../spearce/jgit/transport/FetchConnection.java    |  127 +-----
 .../org/spearce/jgit/transport/FetchProcess.java   |    8 +-
 .../org/spearce/jgit/transport/FetchResult.java    |   74 +----
 .../spearce/jgit/transport/OperationResult.java    |  119 ++++++
 .../org/spearce/jgit/transport/PackTransport.java  |    3 +-
 .../org/spearce/jgit/transport/PushConnection.java |   56 +++-
 .../org/spearce/jgit/transport/PushProcess.java    |  224 +++++++++++
 .../src/org/spearce/jgit/transport/PushResult.java |   84 ++++
 .../src/org/spearce/jgit/transport/RefSpec.java    |   46 ++-
 .../spearce/jgit/transport/RemoteRefUpdate.java    |  315 +++++++++++++++
 .../spearce/jgit/transport/TrackingRefUpdate.java  |   19 +-
 .../src/org/spearce/jgit/transport/Transport.java  |  252 ++++++++++++-
 .../spearce/jgit/transport/TransportBundle.java    |    8 +-
 .../spearce/jgit/transport/TransportGitAnon.java   |   52 +++-
 .../spearce/jgit/transport/TransportGitSsh.java    |   67 +++-
 .../org/spearce/jgit/transport/TransportLocal.java |  114 ++++--
 .../org/spearce/jgit/transport/TransportSftp.java  |   12 +-
 .../jgit/transport/WalkFetchConnection.java        |    2 +-
 .../org/spearce/jgit/transport/WalkTransport.java  |    7 +
 38 files changed, 3103 insertions(+), 474 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/transport/PushProcessTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/BaseConnection.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/BaseFetchConnection.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
 rename org.spearce.jgit/src/org/spearce/jgit/transport/{PackFetchConnection.java => BasePackFetchConnection.java} (75%)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/Connection.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/OperationResult.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/PushResult.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [EGIT PATCH 01/23] Fix: let FetchProcess use fetch() instead of doFetch()
  2008-06-27 22:06 [EGIT PATCH 00/23] Push implementation Marek Zawirski
@ 2008-06-27 22:06 ` Marek Zawirski
  2008-06-27 22:06   ` [EGIT PATCH 02/23] RefUpdate: new possible result Result.IO_FAILURE Marek Zawirski
  2008-06-27 23:25 ` [EGIT PATCH 00/23] Push implementation Robin Rosenberg
  1 sibling, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

doFetch() call didn't check whether fetch() was already performed
(it is intended for internal use), while fetch() does.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../org/spearce/jgit/transport/FetchProcess.java   |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
index afaf9e2..e33b35b 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
@@ -139,7 +139,7 @@ class FetchProcess {
 				if (!askFor.isEmpty() && (!includedTags || !askForIsComplete())) {
 					reopenConnection();
 					if (!askFor.isEmpty())
-						conn.doFetch(monitor, askFor.values());
+						conn.fetch(monitor, askFor.values());
 				}
 			}
 		} finally {
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 02/23] RefUpdate: new possible result Result.IO_FAILURE
  2008-06-27 22:06 ` [EGIT PATCH 01/23] Fix: let FetchProcess use fetch() instead of doFetch() Marek Zawirski
@ 2008-06-27 22:06   ` Marek Zawirski
  2008-06-27 22:06     ` [EGIT PATCH 03/23] Refactor TrackingRefUpdate to not hold RefSpec Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

This result indicates that I/O error (beyond of IOException) occurred
during RefUpdate#update().

Hitherto behaviour was to just throw IOException and leave result with
value Result.NOT_ATTEMPTED. It was just less informative.

Fetch class from pgm package needed new conditions for printing. Other
classes were reviewed and should still work just fine.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../src/org/spearce/jgit/lib/RefUpdate.java        |   27 +++++++++++++++++--
 .../src/org/spearce/jgit/pgm/Fetch.java            |    5 +++
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
index 48044fb..369cb37 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
@@ -105,7 +105,18 @@ public class RefUpdate {
 		 * update to take place, so ref still contains the old value. No
 		 * previous history was lost.
 		 */
-		REJECTED
+		REJECTED,
+
+		/**
+		 * The ref was probably not updated because of I/O error.
+		 * <p>
+		 * Unexpected I/O error occurred when writing new ref. Such error may
+		 * result in uncertain state, but most probably ref was not updated.
+		 * <p>
+		 * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a
+		 * different case.
+		 */
+		IO_FAILURE
 	}
 
 	/** Repository the ref is stored in. */
@@ -256,7 +267,12 @@ public class RefUpdate {
 	 */
 	public Result forceUpdate() throws IOException {
 		requireCanDoUpdate();
-		return result = forceUpdateImpl();
+		try {
+			return result = forceUpdateImpl();
+		} catch (IOException x) {
+			result = Result.IO_FAILURE;
+			throw x;
+		}
 	}
 
 	private Result forceUpdateImpl() throws IOException {
@@ -310,7 +326,12 @@ public class RefUpdate {
 	 */
 	public Result update(final RevWalk walk) throws IOException {
 		requireCanDoUpdate();
-		return result = updateImpl(walk);
+		try {
+			return result = updateImpl(walk);
+		} catch (IOException x) {
+			result = Result.IO_FAILURE;
+			throw x;
+		}
 	}
 
 	private Result updateImpl(final RevWalk walk) throws IOException {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java
index 6277970..3a81575 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java
@@ -109,6 +109,9 @@ class Fetch extends TextBuiltin {
 		if (r == RefUpdate.Result.LOCK_FAILURE)
 			return "[lock fail]";
 
+		if (r == RefUpdate.Result.IO_FAILURE)
+			return "[i/o error]";
+
 		if (r == RefUpdate.Result.NEW) {
 			if (u.getRemoteName().startsWith(REFS_HEADS))
 				return "[new branch]";
@@ -143,6 +146,8 @@ class Fetch extends TextBuiltin {
 	private static char shortTypeOf(final RefUpdate.Result r) {
 		if (r == RefUpdate.Result.LOCK_FAILURE)
 			return '!';
+		if (r == RefUpdate.Result.IO_FAILURE)
+			return '!';
 		if (r == RefUpdate.Result.NEW)
 			return '*';
 		if (r == RefUpdate.Result.FORCED)
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 03/23] Refactor TrackingRefUpdate to not hold RefSpec
  2008-06-27 22:06   ` [EGIT PATCH 02/23] RefUpdate: new possible result Result.IO_FAILURE Marek Zawirski
@ 2008-06-27 22:06     ` Marek Zawirski
  2008-06-27 22:06       ` [EGIT PATCH 04/23] New constructor without RefSpec for TrackingRefUpdate Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Just remoteName is needed, not a whole RefSpec.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../spearce/jgit/transport/TrackingRefUpdate.java  |   10 +++++-----
 1 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TrackingRefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TrackingRefUpdate.java
index 56234a1..771e77a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TrackingRefUpdate.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TrackingRefUpdate.java
@@ -49,14 +49,14 @@ import org.spearce.jgit.revwalk.RevWalk;
 
 /** Update of a locally stored tracking branch. */
 public class TrackingRefUpdate {
-	private final RefSpec spec;
+	private final String remoteName;
 
 	private final RefUpdate update;
 
-	TrackingRefUpdate(final Repository db, final RefSpec s,
+	TrackingRefUpdate(final Repository db, final RefSpec spec,
 			final AnyObjectId nv, final String msg) throws IOException {
-		spec = s;
-		update = db.updateRef(s.getDestination());
+		remoteName = spec.getSource();
+		update = db.updateRef(spec.getDestination());
 		update.setForceUpdate(spec.isForceUpdate());
 		update.setNewObjectId(nv);
 		update.setRefLogMessage(msg, true);
@@ -70,7 +70,7 @@ public class TrackingRefUpdate {
 	 * @return the name used within the remote repository.
 	 */
 	public String getRemoteName() {
-		return spec.getSource();
+		return remoteName;
 	}
 
 	/**
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 04/23] New constructor without RefSpec for TrackingRefUpdate
  2008-06-27 22:06     ` [EGIT PATCH 03/23] Refactor TrackingRefUpdate to not hold RefSpec Marek Zawirski
@ 2008-06-27 22:06       ` Marek Zawirski
  2008-06-27 22:06         ` [EGIT PATCH 05/23] Add RemoteRefUpdate class Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

New constructor operates directly on RefSpec components: remote name,
local name, force flag.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../spearce/jgit/transport/TrackingRefUpdate.java  |   13 ++++++++++---
 1 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TrackingRefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TrackingRefUpdate.java
index 771e77a..a84b38a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TrackingRefUpdate.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TrackingRefUpdate.java
@@ -55,9 +55,16 @@ public class TrackingRefUpdate {
 
 	TrackingRefUpdate(final Repository db, final RefSpec spec,
 			final AnyObjectId nv, final String msg) throws IOException {
-		remoteName = spec.getSource();
-		update = db.updateRef(spec.getDestination());
-		update.setForceUpdate(spec.isForceUpdate());
+		this(db, spec.getDestination(), spec.getSource(), spec.isForceUpdate(),
+				nv, msg);
+	}
+
+	TrackingRefUpdate(final Repository db, final String localName,
+			final String remoteName, final boolean forceUpdate,
+			final AnyObjectId nv, final String msg) throws IOException {
+		this.remoteName = remoteName;
+		update = db.updateRef(localName);
+		update.setForceUpdate(forceUpdate);
 		update.setNewObjectId(nv);
 		update.setRefLogMessage(msg, true);
 	}
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 05/23] Add RemoteRefUpdate class
  2008-06-27 22:06       ` [EGIT PATCH 04/23] New constructor without RefSpec for TrackingRefUpdate Marek Zawirski
@ 2008-06-27 22:06         ` Marek Zawirski
  2008-06-27 22:06           ` [EGIT PATCH 06/23] Refactor: extract superclass OperationResult from FetchResult Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

This class holds specification and status of remote ref update during
push operation.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../spearce/jgit/transport/RemoteRefUpdate.java    |  315 ++++++++++++++++++++
 1 files changed, 315 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java
new file mode 100644
index 0000000..3737c7a
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * 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 remoteName of the Git Development Community 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.spearce.jgit.transport;
+
+import java.io.IOException;
+
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.revwalk.RevWalk;
+
+/**
+ * Represent request and status of a remote ref update. Specification is
+ * provided by client, while status is handled by {@link PushProcess} class,
+ * being read-only for client.
+ * <p>
+ * Client can create instances of this class directly, basing on user
+ * specification and advertised refs ({@link Connection} or through
+ * {@link Transport} helper methods. Apply this specification on remote
+ * repository using
+ * {@link Transport#push(org.spearce.jgit.lib.ProgressMonitor, java.util.Collection)}
+ * method.
+ * </p>
+ * 
+ */
+public class RemoteRefUpdate {
+	/**
+	 * Represent current status of a remote ref update.
+	 */
+	public static enum Status {
+		/**
+		 * Push process hasn't yet attempted to update this ref. This is the
+		 * default status, prior to push process execution.
+		 */
+		NOT_ATTEMPTED,
+
+		/**
+		 * Remote ref was up to date, there was no need to update anything.
+		 */
+		UP_TO_DATE,
+
+		/**
+		 * Remote ref update was rejected, as it would cause non fast-forward
+		 * update.
+		 */
+		REJECTED_NONFASTFORWARD,
+
+		/**
+		 * Remote ref update was rejected, because remote side doesn't
+		 * support/allow deleting refs.
+		 */
+		REJECTED_NODELETE,
+
+		/**
+		 * Remote ref update was rejected, because old object id on remote
+		 * repository wasn't the same as defined expected old object.
+		 */
+		REJECTED_REMOTE_CHANGED,
+
+		/**
+		 * Remote ref update was rejected for other reason, possibly described
+		 * in {@link RemoteRefUpdate#getMessage()}.
+		 */
+		REJECTED_OTHER_REASON,
+
+		/**
+		 * Remote ref didn't exist. Can occur on delete request of a non
+		 * existing ref.
+		 */
+		NON_EXISTING,
+
+		/**
+		 * Push process is awaiting update report from remote repository. This
+		 * is a temporary state or state after critical error in push process.
+		 */
+		AWAITING_REPORT,
+
+		/**
+		 * Remote ref was successfully updated.
+		 */
+		OK;
+	}
+
+	private final ObjectId expectedOldObjectId;
+
+	private final ObjectId newObjectId;
+
+	private final String remoteName;
+
+	private final TrackingRefUpdate trackingRefUpdate;
+
+	private String srcRef;
+
+	private final boolean forceUpdate;
+
+	private Status status;
+
+	private boolean fastForward;
+
+	private String message;
+
+	/**
+	 * Construct remote ref update request by providing an update specification.
+	 * Object is created with default {@link Status#NOT_ATTEMPTED} status and no
+	 * message.
+	 * 
+	 * @param db
+	 *            repository to push from.
+	 * @param srcRef
+	 *            source revision - any string resolvable by
+	 *            {@link Repository#resolve(String)}. This resolves to the new
+	 *            object that the caller want remote ref to be after update. Use
+	 *            null or {@link ObjectId#zeroId()} string for delete request.
+	 * @param remoteName
+	 *            full name of a remote ref to update, e.g. "refs/heads/master"
+	 *            (no wildcard, no short name).
+	 * @param forceUpdate
+	 *            true when caller want remote ref to be updated regardless
+	 *            whether it is fast-forward update (old object is ancestor of
+	 *            new object).
+	 * @param localName
+	 *            optional full name of a local stored tracking branch, to
+	 *            update after push, e.g. "refs/remotes/zawir/dirty" (no
+	 *            wildcard, no short name); null if no local tracking branch
+	 *            should be updated.
+	 * @param expectedOldObjectId
+	 *            optional object id that caller is expecting, requiring to be
+	 *            advertised by remote side before update; update will take
+	 *            place ONLY if remote side advertise exactly this expected id;
+	 *            null if caller doesn't care what object id remote side
+	 *            advertise. Use {@link ObjectId#zeroId()} when expecting no
+	 *            remote ref with this name.
+	 * @throws IOException
+	 *             when I/O error occurred during creating
+	 *             {@link TrackingRefUpdate} for local tracking branch.
+	 * @throws IllegalArgumentException
+	 *             if some required parameter was null or srcRef can't be
+	 *             resolved to any object.
+	 */
+	public RemoteRefUpdate(final Repository db, final String srcRef,
+			final String remoteName, final boolean forceUpdate,
+			final String localName, final ObjectId expectedOldObjectId)
+			throws IOException {
+		if (remoteName == null)
+			throw new IllegalArgumentException("remote name can't be null");
+		this.srcRef = srcRef;
+		this.newObjectId = (srcRef == null ? ObjectId.zeroId() : db
+				.resolve(srcRef));
+		if (newObjectId == null)
+			throw new IllegalArgumentException(
+					"source ref doesn't resolve to any object");
+		this.remoteName = remoteName;
+		this.forceUpdate = forceUpdate;
+		if (localName != null && db != null)
+			trackingRefUpdate = new TrackingRefUpdate(db, localName,
+					remoteName, forceUpdate, newObjectId, "push");
+		else
+			trackingRefUpdate = null;
+		this.expectedOldObjectId = expectedOldObjectId;
+		this.status = Status.NOT_ATTEMPTED;
+	}
+
+	/**
+	 * @return expectedOldObjectId required to be advertised by remote side, as
+	 *         set in constructor; may be null.
+	 */
+	public ObjectId getExpectedOldObjectId() {
+		return expectedOldObjectId;
+	}
+
+	/**
+	 * @return true if some object is required to be advertised by remote side,
+	 *         as set in constructor; false otherwise.
+	 */
+	public boolean isExpectingOldObjectId() {
+		return expectedOldObjectId != null;
+	}
+
+	/**
+	 * @return newObjectId for remote ref, as set in constructor.
+	 */
+	public ObjectId getNewObjectId() {
+		return newObjectId;
+	}
+
+	/**
+	 * @return true if this update is deleting update; false otherwise.
+	 */
+	public boolean isDelete() {
+		return ObjectId.zeroId().equals(newObjectId);
+	}
+
+	/**
+	 * @return name of remote ref to update, as set in constructor.
+	 */
+	public String getRemoteName() {
+		return remoteName;
+	}
+
+	/**
+	 * @return local tracking branch update if localName was set in constructor.
+	 */
+	public TrackingRefUpdate getTrackingRefUpdate() {
+		return trackingRefUpdate;
+	}
+
+	/**
+	 * @return source revision as specified by user (in constructor), could be
+	 *         any string parseable by {@link Repository#resolve(String)}; can
+	 *         be null if specified that way in constructor - this stands for
+	 *         delete request.
+	 */
+	public String getSrcRef() {
+		return srcRef;
+	}
+
+	/**
+	 * @return true if user specified a local tracking branch for remote update;
+	 *         false otherwise.
+	 */
+	public boolean hasTrackingRefUpdate() {
+		return trackingRefUpdate != null;
+	}
+
+	/**
+	 * @return true if this update is forced regardless of old remote ref
+	 *         object; false otherwise.
+	 */
+	public boolean isForceUpdate() {
+		return forceUpdate;
+	}
+
+	/**
+	 * @return status of remote ref update operation.
+	 */
+	public Status getStatus() {
+		return status;
+	}
+
+	/**
+	 * Check whether update was fast-forward. Note that this result is
+	 * meaningful only after successful update (when status is {@link Status#OK}).
+	 * 
+	 * @return true if update was fast-forward; false otherwise.
+	 */
+	public boolean isFastForward() {
+		return fastForward;
+	}
+
+	/**
+	 * @return message describing reasons of status when needed/possible; may be
+	 *         null.
+	 */
+	public String getMessage() {
+		return message;
+	}
+
+	protected void setStatus(final Status status) {
+		this.status = status;
+	}
+
+	protected void setFastForward(boolean fastForward) {
+		this.fastForward = fastForward;
+	}
+
+	protected void setMessage(final String message) {
+		this.message = message;
+	}
+
+	/**
+	 * Update locally stored tracking branch with the new object.
+	 * 
+	 * @param walk
+	 *            walker used for checking update properties.
+	 * @throws IOException
+	 *             when I/O error occurred during update
+	 */
+	protected void updateTrackingRef(final RevWalk walk) throws IOException {
+		trackingRefUpdate.update(walk);
+	}
+}
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 06/23] Refactor: extract superclass OperationResult from FetchResult
  2008-06-27 22:06         ` [EGIT PATCH 05/23] Add RemoteRefUpdate class Marek Zawirski
@ 2008-06-27 22:06           ` Marek Zawirski
  2008-06-27 22:06             ` [EGIT PATCH 07/23] Add PushResult class Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

New superclass holds information about advertised refs and updated
tracking refs, which is all common to fetch and push operations.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../org/spearce/jgit/transport/FetchResult.java    |   74 +------------
 .../spearce/jgit/transport/OperationResult.java    |  119 ++++++++++++++++++++
 2 files changed, 120 insertions(+), 73 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/OperationResult.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchResult.java b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchResult.java
index bd94b5f..cc8557f 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchResult.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchResult.java
@@ -40,94 +40,22 @@ package org.spearce.jgit.transport;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.spearce.jgit.lib.Ref;
 
 /**
  * Final status after a successful fetch from a remote repository.
  * 
  * @see Transport#fetch(org.spearce.jgit.lib.ProgressMonitor, Collection)
  */
-public class FetchResult {
-	private final SortedMap<String, TrackingRefUpdate> updates;
-
+public class FetchResult extends OperationResult {
 	private final List<FetchHeadRecord> forMerge;
 
-	private Map<String, Ref> advertisedRefs;
-
 	FetchResult() {
-		updates = new TreeMap<String, TrackingRefUpdate>();
 		forMerge = new ArrayList<FetchHeadRecord>();
-		advertisedRefs = Collections.<String, Ref> emptyMap();
-	}
-
-	void add(final TrackingRefUpdate u) {
-		updates.put(u.getLocalName(), u);
 	}
 
 	void add(final FetchHeadRecord r) {
 		if (!r.notForMerge)
 			forMerge.add(r);
 	}
-
-	void setAdvertisedRefs(final Map<String, Ref> ar) {
-		advertisedRefs = ar;
-	}
-
-	/**
-	 * Get the complete list of refs advertised by the remote.
-	 * <p>
-	 * The returned refs may appear in any order. If the caller needs these to
-	 * be sorted, they should be copied into a new array or List and then sorted
-	 * by the caller as necessary.
-	 * 
-	 * @return available/advertised refs. Never null. Not modifiable. The
-	 *         collection can be empty if the remote side has no refs (it is an
-	 *         empty/newly created repository).
-	 */
-	public Collection<Ref> getAdvertisedRefs() {
-		return advertisedRefs.values();
-	}
-
-	/**
-	 * Get a single advertised ref by name.
-	 * <p>
-	 * The name supplied should be valid ref name. To get a peeled value for a
-	 * ref (aka <code>refs/tags/v1.0^{}</code>) use the base name (without
-	 * the <code>^{}</code> suffix) and look at the peeled object id.
-	 * 
-	 * @param name
-	 *            name of the ref to obtain.
-	 * @return the requested ref; null if the remote did not advertise this ref.
-	 */
-	public final Ref getAdvertisedRef(final String name) {
-		return advertisedRefs.get(name);
-	}
-
-	/**
-	 * Get the status of all local tracking refs that were updated.
-	 * 
-	 * @return unmodifiable collection of local updates. Never null. Empty if
-	 *         there were no local tracking refs updated.
-	 */
-	public Collection<TrackingRefUpdate> getTrackingRefUpdates() {
-		return Collections.unmodifiableCollection(updates.values());
-	}
-
-	/**
-	 * Get the status for a specific local tracking ref update.
-	 * 
-	 * @param localName
-	 *            name of the local ref (e.g. "refs/remotes/origin/master").
-	 * @return status of the local ref; null if this local ref was not touched
-	 *         during this fetch.
-	 */
-	public TrackingRefUpdate getTrackingRefUpdate(final String localName) {
-		return updates.get(localName);
-	}
 }
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/OperationResult.java b/org.spearce.jgit/src/org/spearce/jgit/transport/OperationResult.java
new file mode 100644
index 0000000..9b411e1
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/OperationResult.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.spearce.jgit.lib.Ref;
+
+/**
+ * Class holding result of operation on remote repository. This includes refs
+ * advertised by remote repo and local tracking refs updates.
+ */
+public abstract class OperationResult {
+
+	protected Map<String, Ref> advertisedRefs = Collections.emptyMap();
+
+	protected final SortedMap<String, TrackingRefUpdate> updates = new TreeMap<String, TrackingRefUpdate>();
+
+	/**
+	 * Get the complete list of refs advertised by the remote.
+	 * <p>
+	 * The returned refs may appear in any order. If the caller needs these to
+	 * be sorted, they should be copied into a new array or List and then sorted
+	 * by the caller as necessary.
+	 * 
+	 * @return available/advertised refs. Never null. Not modifiable. The
+	 *         collection can be empty if the remote side has no refs (it is an
+	 *         empty/newly created repository).
+	 */
+	public Collection<Ref> getAdvertisedRefs() {
+		return Collections.unmodifiableCollection(advertisedRefs.values());
+	}
+
+	/**
+	 * Get a single advertised ref by name.
+	 * <p>
+	 * The name supplied should be valid ref name. To get a peeled value for a
+	 * ref (aka <code>refs/tags/v1.0^{}</code>) use the base name (without
+	 * the <code>^{}</code> suffix) and look at the peeled object id.
+	 * 
+	 * @param name
+	 *            name of the ref to obtain.
+	 * @return the requested ref; null if the remote did not advertise this ref.
+	 */
+	public final Ref getAdvertisedRef(final String name) {
+		return advertisedRefs.get(name);
+	}
+
+	/**
+	 * Get the status of all local tracking refs that were updated.
+	 * 
+	 * @return unmodifiable collection of local updates. Never null. Empty if
+	 *         there were no local tracking refs updated.
+	 */
+	public Collection<TrackingRefUpdate> getTrackingRefUpdates() {
+		return Collections.unmodifiableCollection(updates.values());
+	}
+
+	/**
+	 * Get the status for a specific local tracking ref update.
+	 * 
+	 * @param localName
+	 *            name of the local ref (e.g. "refs/remotes/origin/master").
+	 * @return status of the local ref; null if this local ref was not touched
+	 *         during this operation.
+	 */
+	public TrackingRefUpdate getTrackingRefUpdate(final String localName) {
+		return updates.get(localName);
+	}
+
+	protected void setAdvertisedRefs(final Map<String, Ref> ar) {
+		advertisedRefs = ar;
+	}
+
+	protected void add(final TrackingRefUpdate u) {
+		updates.put(u.getLocalName(), u);
+	}
+}
\ No newline at end of file
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 07/23] Add PushResult class
  2008-06-27 22:06           ` [EGIT PATCH 06/23] Refactor: extract superclass OperationResult from FetchResult Marek Zawirski
@ 2008-06-27 22:06             ` Marek Zawirski
  2008-06-27 22:06               ` [EGIT PATCH 08/23] Support for fetchThin and pushThin options in Transport Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Class represents result of push operation. In addition to the data
provided by OperationResult it also holds information about remote
refs updates.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../src/org/spearce/jgit/transport/PushResult.java |   84 ++++++++++++++++++++
 1 files changed, 84 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/PushResult.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PushResult.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PushResult.java
new file mode 100644
index 0000000..11e5928
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PushResult.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * 
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Result of push operation to the remote repository. Holding information of
+ * {@link OperationResult} and remote refs updates status.
+ * 
+ * @see Transport#push(org.spearce.jgit.lib.ProgressMonitor, Collection)
+ */
+public class PushResult extends OperationResult {
+	private Map<String, RemoteRefUpdate> remoteUpdates = Collections.emptyMap();
+
+	/**
+	 * Get status of remote refs updates. Together with
+	 * {@link #getAdvertisedRefs()} it provides full description/status of each
+	 * ref update.
+	 * <p>
+	 * Returned collection is not sorted in any order.
+	 * </p>
+	 * 
+	 * @return collection of remote refs updates
+	 */
+	public Collection<RemoteRefUpdate> getRemoteUpdates() {
+		return Collections.unmodifiableCollection(remoteUpdates.values());
+	}
+
+	/**
+	 * Get status of specific remote ref update by remote ref name. Together
+	 * with {@link #getAdvertisedRef(String)} it provide full description/status
+	 * of this ref update.
+	 * 
+	 * @param refName
+	 *            remote ref name
+	 * @return status of remote ref update
+	 */
+	public RemoteRefUpdate getRemoteUpdate(final String refName) {
+		return remoteUpdates.get(refName);
+	}
+
+	protected void setRemoteUpdates(
+			final Map<String, RemoteRefUpdate> remoteUpdates) {
+		this.remoteUpdates = remoteUpdates;
+	}
+}
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 08/23] Support for fetchThin and pushThin options in Transport
  2008-06-27 22:06             ` [EGIT PATCH 07/23] Add PushResult class Marek Zawirski
@ 2008-06-27 22:06               ` Marek Zawirski
  2008-06-27 22:06                 ` [EGIT PATCH 09/23] Big refactor: *Connection hierarchy Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

This option determines whether we should use thin pack when possible
during fetching from or pushing to a remote repo.

For fetching the default is to produce a thin pack when remote side
supports it, while for pushing the default setting is to not produce a
thin pack.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../jgit/transport/PackFetchConnection.java        |    4 +-
 .../src/org/spearce/jgit/transport/Transport.java  |   63 ++++++++++++++++++++
 2 files changed, 66 insertions(+), 1 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PackFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PackFetchConnection.java
index 5f15a8d..6209030 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PackFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PackFetchConnection.java
@@ -150,6 +150,7 @@ abstract class PackFetchConnection extends FetchConnection {
 		local = packTransport.local;
 		uri = packTransport.uri;
 		includeTags = packTransport.getTagOpt() != TagOpt.NO_TAGS;
+		thinPack = packTransport.isFetchThin();
 
 		walk = new RevWalk(local);
 		reachableCommits = new RevCommitList<RevCommit>();
@@ -363,7 +364,8 @@ abstract class PackFetchConnection extends FetchConnection {
 			includeTags = wantCapability(line, OPTION_INCLUDE_TAG);
 		wantCapability(line, OPTION_OFS_DELTA);
 		multiAck = wantCapability(line, OPTION_MULTI_ACK);
-		thinPack = wantCapability(line, OPTION_THIN_PACK);
+		if (thinPack)
+			thinPack = wantCapability(line, OPTION_THIN_PACK);
 		if (wantCapability(line, OPTION_SIDE_BAND_64K))
 			sideband = true;
 		else if (wantCapability(line, OPTION_SIDE_BAND))
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
index 6cc38ec..c4b71eb 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
@@ -142,6 +142,16 @@ public abstract class Transport {
 		throw new NotSupportedException("URI not supported: " + remote);
 	}
 
+	/**
+	 * Default setting for {@link #fetchThin} option.
+	 */
+	public static final boolean DEFAULT_FETCH_THIN = true;
+
+	/**
+	 * Default setting for {@link #pushThin} option.
+	 */
+	public static final boolean DEFAULT_PUSH_THIN = false;
+
 	/** The repository this transport fetches into, or pushes out of. */
 	protected final Repository local;
 
@@ -165,6 +175,12 @@ public abstract class Transport {
 	 */
 	private TagOpt tagopt = TagOpt.NO_TAGS;
 
+	/** Should fetch request thin-pack if remote repository can produce it. */
+	private boolean fetchThin = DEFAULT_FETCH_THIN;
+
+	/** Should push produce thin-pack when sending objects to remote repository. */
+	private boolean pushThin = DEFAULT_PUSH_THIN;
+
 	/**
 	 * Create a new transport instance.
 	 * 
@@ -234,6 +250,53 @@ public abstract class Transport {
 	}
 
 	/**
+	 * Default setting is: {@link #DEFAULT_FETCH_THIN}
+	 * 
+	 * @return true if fetch should request thin-pack when possible; false
+	 *         otherwise
+	 * @see PackTransport
+	 */
+	public boolean isFetchThin() {
+		return fetchThin;
+	}
+
+	/**
+	 * Set the thin-pack preference for fetch operation. Default setting is:
+	 * {@link #DEFAULT_FETCH_THIN}
+	 * 
+	 * @param fetchThin
+	 *            true when fetch should request thin-pack when possible; false
+	 *            when it shouldn't
+	 * @see PackTransport
+	 */
+	public void setFetchThin(final boolean fetchThin) {
+		this.fetchThin = fetchThin;
+	}
+
+	/**
+	 * Default setting is: {@value #DEFAULT_PUSH_THIN}
+	 * 
+	 * @return true if push should produce thin-pack in pack transports
+	 * @see PackTransport
+	 */
+	public boolean isPushThin() {
+		return pushThin;
+	}
+
+	/**
+	 * Set thin-pack preference for push operation. Default setting is:
+	 * {@value #DEFAULT_PUSH_THIN}
+	 * 
+	 * @param pushThin
+	 *            true when push should produce thin-pack in pack transports;
+	 *            false when it shouldn't
+	 * @see PackTransport
+	 */
+	public void setPushThin(final boolean pushThin) {
+		this.pushThin = pushThin;
+	}
+
+	/**
 	 * Fetch objects and refs from the remote repository to the local one.
 	 * <p>
 	 * This is a utility function providing standard fetch behavior. Local
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 09/23] Big refactor: *Connection hierarchy
  2008-06-27 22:06               ` [EGIT PATCH 08/23] Support for fetchThin and pushThin options in Transport Marek Zawirski
@ 2008-06-27 22:06                 ` Marek Zawirski
  2008-06-27 22:06                   ` [EGIT PATCH 10/23] Add ignoreMissingUninteresting option to PackWriter Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

New interfaces and base classes are introduced to cope with lack of
multiple inheritance and allow code reuse between fetch and push
operations implementations. Some renames (adding Base prefix) are also
performed to distinct between interfaces and base implementations.

Some generalizations/cleanings in interfaces and implementations
(particularly in Base* classes) were introduced. readAdvertisedRefs() in
BasePackConnection now support both push and fetch advertisements.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../org/spearce/jgit/transport/BaseConnection.java |  103 +++++++++
 .../jgit/transport/BaseFetchConnection.java        |   86 ++++++++
 .../spearce/jgit/transport/BasePackConnection.java |  217 ++++++++++++++++++++
 ...onnection.java => BasePackFetchConnection.java} |  165 ++--------------
 .../src/org/spearce/jgit/transport/Connection.java |  104 ++++++++++
 .../spearce/jgit/transport/FetchConnection.java    |  127 ++----------
 .../org/spearce/jgit/transport/FetchProcess.java   |    2 +-
 .../org/spearce/jgit/transport/PackTransport.java  |    3 +-
 .../org/spearce/jgit/transport/PushConnection.java |   56 +++++-
 .../spearce/jgit/transport/TransportBundle.java    |    2 +-
 .../spearce/jgit/transport/TransportGitAnon.java   |    2 +-
 .../spearce/jgit/transport/TransportGitSsh.java    |    2 +-
 .../org/spearce/jgit/transport/TransportLocal.java |    2 +-
 .../jgit/transport/WalkFetchConnection.java        |    2 +-
 14 files changed, 602 insertions(+), 271 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/BaseConnection.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/BaseFetchConnection.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
 rename org.spearce.jgit/src/org/spearce/jgit/transport/{PackFetchConnection.java => BasePackFetchConnection.java} (76%)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/Connection.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BaseConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BaseConnection.java
new file mode 100644
index 0000000..9a6b7df
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BaseConnection.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.spearce.jgit.errors.TransportException;
+import org.spearce.jgit.lib.Ref;
+
+/**
+ * Base helper class for implementing operations connections.
+ * 
+ * @see BasePackConnection
+ * @see BaseFetchConnection
+ */
+abstract class BaseConnection implements Connection {
+
+	private Map<String, Ref> advertisedRefs = Collections.emptyMap();
+
+	private boolean startedOperation;
+
+	public Map<String, Ref> getRefsMap() {
+		return advertisedRefs;
+	}
+
+	public final Collection<Ref> getRefs() {
+		return advertisedRefs.values();
+	}
+
+	public final Ref getRef(final String name) {
+		return advertisedRefs.get(name);
+	}
+
+	public abstract void close();
+
+	/**
+	 * Denote the list of refs available on the remote repository.
+	 * <p>
+	 * Implementors should invoke this method once they have obtained the refs
+	 * that are available from the remote repository.
+	 * 
+	 * @param all
+	 *            the complete list of refs the remote has to offer. This map
+	 *            will be wrapped in an unmodifiable way to protect it, but it
+	 *            does not get copied.
+	 */
+	protected void available(final Map<String, Ref> all) {
+		advertisedRefs = Collections.unmodifiableMap(all);
+	}
+
+	/**
+	 * Helper method for ensuring one-operation per connection. Check whether
+	 * operation was already marked as started, and mark it as started.
+	 * 
+	 * @throws TransportException
+	 *             if operation was already marked as started.
+	 */
+	protected void markStartedOperation() throws TransportException {
+		if (startedOperation)
+			throw new TransportException(
+					"Only one operation call per connection is supported.");
+		startedOperation = true;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BaseFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BaseFetchConnection.java
new file mode 100644
index 0000000..7fb13bc
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BaseFetchConnection.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.util.Collection;
+
+import org.spearce.jgit.errors.TransportException;
+import org.spearce.jgit.lib.ProgressMonitor;
+import org.spearce.jgit.lib.Ref;
+
+/**
+ * Base helper class for fetch connection implementations. Provides some common
+ * typical structures and methods used during fetch connection.
+ * <p>
+ * Implementors of fetch over pack-based protocols should consider using
+ * {@link BasePackFetchConnection} instead.
+ * </p>
+ */
+abstract class BaseFetchConnection extends BaseConnection implements
+		FetchConnection {
+	public final void fetch(final ProgressMonitor monitor,
+			final Collection<Ref> want) throws TransportException {
+		markStartedOperation();
+		doFetch(monitor, want);
+	}
+
+	/**
+	 * Default implementation of {@link FetchConnection#didFetchIncludeTags()} -
+	 * returning false.
+	 */
+	public boolean didFetchIncludeTags() {
+		return false;
+	}
+
+	/**
+	 * Implementation of {@link #fetch(ProgressMonitor, Collection)} without
+	 * checking for multiple fetch.
+	 * 
+	 * @param monitor
+	 *            as in {@link #fetch(ProgressMonitor, Collection)}
+	 * @param want
+	 *            as in {@link #fetch(ProgressMonitor, Collection)}
+	 * @throws TransportException
+	 *             as in {@link #fetch(ProgressMonitor, Collection)}, but
+	 *             implementation doesn't have to care about multiple
+	 *             {@link #fetch(ProgressMonitor, Collection)} calls, as it is
+	 *             checked in this class.
+	 */
+	protected abstract void doFetch(final ProgressMonitor monitor,
+			final Collection<Ref> want) throws TransportException;
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
new file mode 100644
index 0000000..d119672
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Set;
+
+import org.spearce.jgit.errors.PackProtocolException;
+import org.spearce.jgit.errors.TransportException;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.lib.Repository;
+
+/**
+ * Base helper class for pack-based operations implementations. Provides partial
+ * implementation of pack-protocol - refs advertising and capabilities support,
+ * and some other helper methods.
+ * 
+ * @see BasePackFetchConnection
+ * @see BasePackPushConnection
+ */
+abstract class BasePackConnection extends BaseConnection {
+
+	/** The repository this transport fetches into, or pushes out of. */
+	protected final Repository local;
+
+	/** Remote repository location. */
+	protected final URIish uri;
+
+	/** Buffered input stream reading from the remote. */
+	protected InputStream in;
+
+	/** Buffered output stream sending to the remote. */
+	protected OutputStream out;
+
+	/** Packet line decoder around {@link #in}. */
+	protected PacketLineIn pckIn;
+
+	/** Packet line encoder around {@link #out}. */
+	protected PacketLineOut pckOut;
+
+	/** Capability tokens advertised by the remote side. */
+	private final Set<String> remoteCapablities = new HashSet<String>();
+
+	BasePackConnection(final PackTransport packTransport) {
+		local = packTransport.local;
+		uri = packTransport.uri;
+	}
+
+	protected void init(final InputStream myIn, final OutputStream myOut) {
+		in = myIn instanceof BufferedInputStream ? myIn
+				: new BufferedInputStream(myIn);
+		out = myOut instanceof BufferedOutputStream ? myOut
+				: new BufferedOutputStream(myOut);
+
+		pckIn = new PacketLineIn(in);
+		pckOut = new PacketLineOut(out);
+	}
+
+	protected void readAdvertisedRefs() throws TransportException {
+		try {
+			readAdvertisedRefsImpl();
+		} catch (TransportException err) {
+			close();
+			throw err;
+		} catch (IOException err) {
+			close();
+			throw new TransportException(err.getMessage(), err);
+		} catch (RuntimeException err) {
+			close();
+			throw new TransportException(err.getMessage(), err);
+		}
+	}
+
+	private void readAdvertisedRefsImpl() throws IOException {
+		final LinkedHashMap<String, Ref> avail = new LinkedHashMap<String, Ref>();
+		for (;;) {
+			String line;
+
+			try {
+				line = pckIn.readString();
+			} catch (EOFException eof) {
+				if (avail.isEmpty())
+					throw new TransportException(uri + " not found.");
+				throw eof;
+			}
+
+			if (avail.isEmpty()) {
+				final int nul = line.indexOf('\0');
+				if (nul >= 0) {
+					// The first line (if any) may contain "hidden"
+					// capability values after a NUL byte.
+					for (String c : line.substring(nul + 1).split(" "))
+						remoteCapablities.add(c);
+					line = line.substring(0, nul);
+				}
+
+				if (line.equals("capabilties^{}")) {
+					// special line from git-receive-pack to show
+					// capabilities when there are no refs to advertise
+					continue;
+				}
+			}
+
+			if (line.length() == 0)
+				break;
+
+			String name = line.substring(41, line.length());
+			final ObjectId id = ObjectId.fromString(line.substring(0, 40));
+			if (name.endsWith("^{}")) {
+				name = name.substring(0, name.length() - 3);
+				final Ref prior = avail.get(name);
+				if (prior == null)
+					throw new PackProtocolException(uri + ": advertisement of "
+							+ name + "^{} came before " + name);
+
+				if (prior.getPeeledObjectId() != null)
+					throw duplicateAdvertisement(name + "^{}");
+
+				avail.put(name, new Ref(name, prior.getObjectId(), id));
+			} else {
+				final Ref prior = avail.put(name, new Ref(name, id));
+				if (prior != null)
+					throw duplicateAdvertisement(name);
+			}
+		}
+		available(avail);
+	}
+
+	protected boolean isCapableOf(final String option) {
+		return remoteCapablities.contains(option);
+	}
+
+	protected boolean wantCapability(final StringBuilder b, final String option) {
+		if (!isCapableOf(option))
+			return false;
+		if (b.length() > 0)
+			b.append(' ');
+		b.append(option);
+		return true;
+	}
+
+	private PackProtocolException duplicateAdvertisement(final String name) {
+		return new PackProtocolException(uri + ": duplicate advertisements of "
+				+ name);
+	}
+
+	@Override
+	public void close() {
+		if (out != null) {
+			try {
+				pckOut.end();
+				out.close();
+			} catch (IOException err) {
+				// Ignore any close errors.
+			} finally {
+				out = null;
+				pckOut = null;
+			}
+		}
+
+		if (in != null) {
+			try {
+				in.close();
+			} catch (IOException err) {
+				// Ignore any close errors.
+			} finally {
+				in = null;
+				pckIn = null;
+			}
+		}
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PackFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
similarity index 76%
rename from org.spearce.jgit/src/org/spearce/jgit/transport/PackFetchConnection.java
rename to org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
index 6209030..04a91bf 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PackFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
@@ -38,26 +38,15 @@
 
 package org.spearce.jgit.transport;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.EOFException;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.Collection;
 import java.util.Date;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.Set;
 
-import org.spearce.jgit.errors.PackProtocolException;
 import org.spearce.jgit.errors.TransportException;
 import org.spearce.jgit.lib.AnyObjectId;
 import org.spearce.jgit.lib.MutableObjectId;
-import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.ProgressMonitor;
 import org.spearce.jgit.lib.Ref;
-import org.spearce.jgit.lib.Repository;
 import org.spearce.jgit.revwalk.RevCommit;
 import org.spearce.jgit.revwalk.RevCommitList;
 import org.spearce.jgit.revwalk.RevFlag;
@@ -78,8 +67,14 @@ import org.spearce.jgit.revwalk.filter.RevFilter;
  * This connection requires only a bi-directional pipe or socket, and thus is
  * easily wrapped up into a local process pipe, anonymous TCP socket, or a
  * command executed through an SSH tunnel.
+ * <p>
+ * Concrete implementations should just call
+ * {@link #init(java.io.InputStream, java.io.OutputStream)} and
+ * {@link #readAdvertisedRefs()} methods in constructor or before any use. They
+ * should also handle resources releasing in {@link #close()} method if needed.
  */
-abstract class PackFetchConnection extends FetchConnection {
+abstract class BasePackFetchConnection extends BasePackConnection implements
+		FetchConnection {
 	/**
 	 * Maximum number of 'have' lines to send before giving up.
 	 * <p>
@@ -103,27 +98,6 @@ abstract class PackFetchConnection extends FetchConnection {
 
 	static final String OPTION_SHALLOW = "shallow";
 
-	/** The repository this transport fetches into, or pushes out of. */
-	protected final Repository local;
-
-	/** Remote repository location. */
-	protected final URIish uri;
-
-	/** Capability tokens advertised by the remote side. */
-	protected final Set<String> remoteCapablities = new HashSet<String>();
-
-	/** Buffered input stream reading from the remote. */
-	protected InputStream in;
-
-	/** Buffered output stream sending to the remote. */
-	protected OutputStream out;
-
-	/** Packet line decoder around {@link #in}. */
-	protected PacketLineIn pckIn;
-
-	/** Packet line encoder around {@link #out}. */
-	protected PacketLineOut pckOut;
-
 	private final RevWalk walk;
 
 	/** All commits that are immediately reachable by a local ref. */
@@ -146,9 +120,8 @@ abstract class PackFetchConnection extends FetchConnection {
 
 	private boolean includeTags;
 
-	PackFetchConnection(final PackTransport packTransport) {
-		local = packTransport.local;
-		uri = packTransport.uri;
+	BasePackFetchConnection(final PackTransport packTransport) {
+		super(packTransport);
 		includeTags = packTransport.getTagOpt() != TagOpt.NO_TAGS;
 		thinPack = packTransport.isFetchThin();
 
@@ -163,91 +136,16 @@ abstract class PackFetchConnection extends FetchConnection {
 		walk.carry(ADVERTISED);
 	}
 
-	protected void init(final InputStream myIn, final OutputStream myOut) {
-		in = myIn instanceof BufferedInputStream ? myIn
-				: new BufferedInputStream(myIn);
-		out = myOut instanceof BufferedOutputStream ? myOut
-				: new BufferedOutputStream(myOut);
-
-		pckIn = new PacketLineIn(in);
-		pckOut = new PacketLineOut(out);
+	public final void fetch(final ProgressMonitor monitor,
+			final Collection<Ref> want) throws TransportException {
+		markStartedOperation();
+		doFetch(monitor, want);
 	}
 
-	@Override
 	public boolean didFetchIncludeTags() {
-		return includeTags;
-	}
-
-	protected void readAdvertisedRefs() throws TransportException {
-		try {
-			readAdvertisedRefsImpl();
-		} catch (TransportException err) {
-			close();
-			throw err;
-		} catch (IOException err) {
-			close();
-			throw new TransportException(err.getMessage(), err);
-		} catch (RuntimeException err) {
-			close();
-			throw new TransportException(err.getMessage(), err);
-		}
-	}
-
-	private void readAdvertisedRefsImpl() throws IOException {
-		final LinkedHashMap<String, Ref> avail = new LinkedHashMap<String, Ref>();
-		for (;;) {
-			String line;
-
-			try {
-				line = pckIn.readString();
-			} catch (EOFException eof) {
-				if (avail.isEmpty())
-					throw new TransportException(uri + " not found.");
-				throw eof;
-			}
-
-			if (avail.isEmpty()) {
-				// The first line (if any) may contain "hidden"
-				// capability values after a NUL byte.
-				//
-				final int nul = line.indexOf('\0');
-				if (nul >= 0) {
-					for (String c : line.substring(nul + 1).split(" "))
-						remoteCapablities.add(c);
-					line = line.substring(0, nul);
-				}
-			}
-
-			if (line.length() == 0)
-				break;
-
-			String name = line.substring(41, line.length());
-			final ObjectId id = ObjectId.fromString(line.substring(0, 40));
-			if (name.endsWith("^{}")) {
-				name = name.substring(0, name.length() - 3);
-				final Ref prior = avail.get(name);
-				if (prior == null)
-					throw new PackProtocolException("advertisement of " + name
-							+ "^{} came before " + name);
-
-				if (prior.getPeeledObjectId() != null)
-					throw duplicateAdvertisement(name + "^{}");
-
-				avail.put(name, new Ref(name, prior.getObjectId(), id));
-			} else {
-				final Ref prior = avail.put(name, new Ref(name, id));
-				if (prior != null)
-					throw duplicateAdvertisement(name);
-			}
-		}
-		available(avail);
+		return false;
 	}
 
-	private PackProtocolException duplicateAdvertisement(final String name) {
-		return new PackProtocolException("duplicate advertisements of " + name);
-	}
-
-	@Override
 	protected void doFetch(final ProgressMonitor monitor,
 			final Collection<Ref> want) throws TransportException {
 		try {
@@ -373,15 +271,6 @@ abstract class PackFetchConnection extends FetchConnection {
 		return line.toString();
 	}
 
-	private boolean wantCapability(final StringBuilder b, final String option) {
-		if (!remoteCapablities.contains(option))
-			return false;
-		if (b.length() > 0)
-			b.append(' ');
-		b.append(option);
-		return true;
-	}
-
 	private void negotiate(final ProgressMonitor monitor) throws IOException,
 			CancelledException {
 		final MutableObjectId ackId = new MutableObjectId();
@@ -568,32 +457,6 @@ abstract class PackFetchConnection extends FetchConnection {
 		ip.renameAndOpenPack();
 	}
 
-	@Override
-	public void close() {
-		if (out != null) {
-			try {
-				pckOut.end();
-				out.close();
-			} catch (IOException err) {
-				// Ignore any close errors.
-			} finally {
-				out = null;
-				pckOut = null;
-			}
-		}
-
-		if (in != null) {
-			try {
-				in.close();
-			} catch (IOException err) {
-				// Ignore any close errors.
-			} finally {
-				in = null;
-				pckIn = null;
-			}
-		}
-	}
-
 	private static class CancelledException extends Exception {
 		private static final long serialVersionUID = 1L;
 	}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Connection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Connection.java
new file mode 100644
index 0000000..5a91e9b
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Connection.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.spearce.jgit.lib.Ref;
+
+/**
+ * Represent connection for operation on a remote repository.
+ * <p>
+ * Currently all operations on remote repository (fetch and push) provide
+ * information about remote refs. Every connection is able to be closed and
+ * should be closed - this is a connection client responsibility.
+ * 
+ * @see Transport
+ */
+public interface Connection {
+
+	/**
+	 * Get the complete map of refs advertised as available for fetching or
+	 * pushing.
+	 * 
+	 * @return available/advertised refs: map of refname to ref. Never null. Not
+	 *         modifiable. The collection can be empty if the remote side has no
+	 *         refs (it is an empty/newly created repository).
+	 */
+	public Map<String, Ref> getRefsMap();
+
+	/**
+	 * Get the complete list of refs advertised as available for fetching or
+	 * pushing.
+	 * <p>
+	 * The returned refs may appear in any order. If the caller needs these to
+	 * be sorted, they should be copied into a new array or List and then sorted
+	 * by the caller as necessary.
+	 * 
+	 * @return available/advertised refs. Never null. Not modifiable. The
+	 *         collection can be empty if the remote side has no refs (it is an
+	 *         empty/newly created repository).
+	 */
+	public Collection<Ref> getRefs();
+
+	/**
+	 * Get a single advertised ref by name.
+	 * <p>
+	 * The name supplied should be valid ref name. To get a peeled value for a
+	 * ref (aka <code>refs/tags/v1.0^{}</code>) use the base name (without
+	 * the <code>^{}</code> suffix) and look at the peeled object id.
+	 * 
+	 * @param name
+	 *            name of the ref to obtain.
+	 * @return the requested ref; null if the remote did not advertise this ref.
+	 */
+	public Ref getRef(final String name);
+
+	/**
+	 * Close any resources used by this connection.
+	 * <p>
+	 * If the remote repository is contacted by a network socket this method
+	 * must close that network socket, disconnecting the two peers. If the
+	 * remote repository is actually local (same system) this method must close
+	 * any open file handles used to read the "remote" repository.
+	 */
+	public void close();
+
+}
\ No newline at end of file
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchConnection.java
index 8e7641c..9d25b0d 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchConnection.java
@@ -38,8 +38,6 @@
 package org.spearce.jgit.transport;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
 
 import org.spearce.jgit.errors.TransportException;
 import org.spearce.jgit.lib.ProgressMonitor;
@@ -62,84 +60,13 @@ import org.spearce.jgit.lib.Ref;
  * 
  * @see Transport
  */
-public abstract class FetchConnection {
-	private Map<String, Ref> cachedRefs = Collections.<String, Ref> emptyMap();
-
-	/** Have we started {@link #fetch(ProgressMonitor, Collection)} yet? */
-	private boolean startedFetch;
-
-	Map<String, Ref> getCachedRefs() {
-		return cachedRefs;
-	}
-
-	/**
-	 * Denote the list of refs available on the remote repository.
-	 * <p>
-	 * Implementors should invoke this method once they have obtained the refs
-	 * that are available from the remote repository.s
-	 * 
-	 * @param all
-	 *            the complete list of refs the remote has to offer. This map
-	 *            will be wrapped in an unmodifiable way to protect it, but it
-	 *            does not get copied.
-	 */
-	protected void available(final Map<String, Ref> all) {
-		cachedRefs = Collections.unmodifiableMap(all);
-	}
-
-	/**
-	 * Get the complete list of refs advertised as available for fetching.
-	 * <p>
-	 * The returned refs may appear in any order. If the caller needs these to
-	 * be sorted, they should be copied into a new array or List and then sorted
-	 * by the caller as necessary.
-	 * 
-	 * @return available/advertised refs. Never null. Not modifiable. The
-	 *         collection can be empty if the remote side has no refs (it is an
-	 *         empty/newly created repository).
-	 */
-	public final Collection<Ref> getRefs() {
-		return cachedRefs.values();
-	}
-
-	/**
-	 * Get a single advertised ref by name.
-	 * <p>
-	 * The name supplied should be valid ref name. To get a peeled value for a
-	 * ref (aka <code>refs/tags/v1.0^{}</code>) use the base name (without
-	 * the <code>^{}</code> suffix) and look at the peeled object id.
-	 * 
-	 * @param name
-	 *            name of the ref to obtain.
-	 * @return the requested ref; null if the remote did not advertise this ref.
-	 */
-	public final Ref getRef(final String name) {
-		return cachedRefs.get(name);
-	}
-
+public interface FetchConnection extends Connection {
 	/**
 	 * Fetch objects we don't have but that are reachable from advertised refs.
-	 * 
-	 * @param monitor
-	 *            progress monitor to update the end-user about the amount of
-	 *            work completed, or to indicate cancellation.
-	 * @param want
-	 *            one or more refs advertised by this connection that the caller
-	 *            wants to store locally.
-	 * @throws TransportException
-	 *             objects could not be copied due to a network failure,
-	 *             protocol error, or error on remote side.
-	 */
-	public final void fetch(final ProgressMonitor monitor,
-			final Collection<Ref> want) throws TransportException {
-		if (startedFetch)
-			throw new TransportException("Only one fetch call supported.");
-		startedFetch = true;
-		doFetch(monitor, want);
-	}
-
-	/**
-	 * Fetch objects this repository does not yet contain.
+	 * <p>
+	 * Only one call per connection is allowed. Subsequent calls will result in
+	 * {@link TransportException}.
+	 * </p>
 	 * <p>
 	 * Implementations are free to use network connections as necessary to
 	 * efficiently (for both client and server) transfer objects from the remote
@@ -147,22 +74,24 @@ public abstract class FetchConnection {
 	 * avoid replacing/overwriting/duplicating an object already available in
 	 * the local destination repository. Locally available objects and packs
 	 * should always be preferred over remotely available objects and packs.
+	 * {@link Transport#isFetchThin()} should be honored if applicable.
+	 * </p>
 	 * 
 	 * @param monitor
-	 *            progress feedback to inform the end-user about the status of
-	 *            the object transfer. Implementors should poll the monitor at
-	 *            regular intervals to look for cancellation requests from the
-	 *            user.
+	 *            progress monitor to inform the end-user about the amount of
+	 *            work completed, or to indicate cancellation. Implementations
+	 *            should poll the monitor at regular intervals to look for
+	 *            cancellation requests from the user.
 	 * @param want
-	 *            one or more refs that were previously passed to
-	 *            {@link #available(Map)} by the implementation. These refs
-	 *            indicate the objects the caller wants copied.
+	 *            one or more refs advertised by this connection that the caller
+	 *            wants to store locally.
 	 * @throws TransportException
 	 *             objects could not be copied due to a network failure,
-	 *             protocol error, or error on remote side.
+	 *             protocol error, or error on remote side, or connection was
+	 *             already used for fetch.
 	 */
-	protected abstract void doFetch(ProgressMonitor monitor,
-			Collection<Ref> want) throws TransportException;
+	public void fetch(final ProgressMonitor monitor, final Collection<Ref> want)
+			throws TransportException;
 
 	/**
 	 * Did the last {@link #fetch(ProgressMonitor, Collection)} get tags?
@@ -170,9 +99,9 @@ public abstract class FetchConnection {
 	 * Some Git aware transports are able to implicitly grab an annotated tag if
 	 * {@link TagOpt#AUTO_FOLLOW} or {@link TagOpt#FETCH_TAGS} was selected and
 	 * the object the tag peels to (references) was transferred as part of the
-	 * last {@link #doFetch(ProgressMonitor, Collection)} call. If it is
-	 * possible for such tags to have been included in the transfer this method
-	 * returns true, allowing the caller to attempt tag discovery.
+	 * last {@link #fetch(ProgressMonitor, Collection)} call. If it is possible
+	 * for such tags to have been included in the transfer this method returns
+	 * true, allowing the caller to attempt tag discovery.
 	 * <p>
 	 * By returning only true/false (and not the actual list of tags obtained)
 	 * the transport itself does not need to be aware of whether or not tags
@@ -181,17 +110,5 @@ public abstract class FetchConnection {
 	 * @return true if the last fetch call implicitly included tag objects;
 	 *         false if tags were not implicitly obtained.
 	 */
-	public boolean didFetchIncludeTags() {
-		return false;
-	}
-
-	/**
-	 * Close any resources used by this connection.
-	 * <p>
-	 * If the remote repository is contacted by a network socket this method
-	 * must close that network socket, disconnecting the two peers. If the
-	 * remote repository is actually local (same system) this method must close
-	 * any open file handles used to read the "remote" repository.
-	 */
-	public abstract void close();
-}
+	public boolean didFetchIncludeTags();
+}
\ No newline at end of file
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
index e33b35b..c765c12 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
@@ -97,7 +97,7 @@ class FetchProcess {
 
 		conn = transport.openFetch();
 		try {
-			result.setAdvertisedRefs(conn.getCachedRefs());
+			result.setAdvertisedRefs(conn.getRefsMap());
 			final Set<Ref> matched = new HashSet<Ref>();
 			for (final RefSpec spec : toFetch) {
 				if (spec.isWildcard())
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PackTransport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PackTransport.java
index 177e065..50708d3 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PackTransport.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PackTransport.java
@@ -46,7 +46,8 @@ import org.spearce.jgit.lib.Repository;
  * forth by creating pack files on the source side and indexing them on the
  * receiving side.
  * 
- * @see PackFetchConnection
+ * @see BasePackFetchConnection
+ * @see BasePackPushConnection
  */
 abstract class PackTransport extends Transport {
 	PackTransport(final Repository local, final URIish u) {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PushConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PushConnection.java
index 316bb95..835b15c 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PushConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PushConnection.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- *
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * 
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or
@@ -37,6 +38,12 @@
 
 package org.spearce.jgit.transport;
 
+import java.util.Map;
+
+import org.spearce.jgit.errors.TransportException;
+import org.spearce.jgit.lib.ProgressMonitor;
+import org.spearce.jgit.transport.RemoteRefUpdate.Status;
+
 /**
  * Lists known refs from the remote and sends objects to the remote.
  * <p>
@@ -55,14 +62,47 @@ package org.spearce.jgit.transport;
  * 
  * @see Transport
  */
-public abstract class PushConnection {
+public interface PushConnection extends Connection {
+
 	/**
-	 * Close any resources used by this connection.
+	 * Pushes to the remote repository basing on provided specification. This
+	 * possibly result in update/creation/deletion of refs on remote repository
+	 * and sending objects that remote repository need to have a consistent
+	 * objects graph from new refs.
+	 * <p>
+	 * <p>
+	 * Only one call per connection is allowed. Subsequent calls will result in
+	 * {@link TransportException}.
+	 * </p>
 	 * <p>
-	 * If the remote repository is contacted by a network socket this method
-	 * must close that network socket, disconnecting the two peers. If the
-	 * remote repository is actually local (same system) this method must close
-	 * any open file handles used to read the "remote" repository.
+	 * Implementation may use local repository to send a minimum set of objects
+	 * needed by remote repository in efficient way.
+	 * {@link Transport#isPushThin()} should be honored if applicable.
+	 * refUpdates should be filled with information about status of each update.
+	 * </p>
+	 * 
+	 * @param monitor
+	 *            progress monitor to update the end-user about the amount of
+	 *            work completed, or to indicate cancellation. Implementors
+	 *            should poll the monitor at regular intervals to look for
+	 *            cancellation requests from the user.
+	 * @param refUpdates
+	 *            map of remote refnames to remote refs update
+	 *            specifications/statuses. Can't be empty. This indicate what
+	 *            refs caller want to update on remote side. Only refs updates
+	 *            with {@link Status#NOT_ATTEMPTED} should passed.
+	 *            Implementation must ensure that and appropriate status with
+	 *            optional message should be set during call. No refUpdate with
+	 *            {@link Status#AWAITING_REPORT} or {@link Status#NOT_ATTEMPTED}
+	 *            can be leaved by implementation after return from this call.
+	 * @throws TransportException
+	 *             objects could not be copied due to a network failure,
+	 *             critical protocol error, or error on remote side, or
+	 *             connection was already used for push - new connection must be
+	 *             created. Non-critical errors concerning only isolated refs
+	 *             should be placed in refUpdates.
 	 */
-	public abstract void close();
+	public void push(final ProgressMonitor monitor,
+			final Map<String, RemoteRefUpdate> refUpdates)
+			throws TransportException;
 }
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
index 2c173fd..48120a8 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
@@ -95,7 +95,7 @@ class TransportBundle extends PackTransport {
 		return new BundleFetchConnection();
 	}
 
-	class BundleFetchConnection extends FetchConnection {
+	class BundleFetchConnection extends BaseFetchConnection {
 		FileInputStream in;
 
 		RewindBufferedInputStream bin;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
index e37757a..a7a419e 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
@@ -99,7 +99,7 @@ class TransportGitAnon extends PackTransport {
 		pckOut.flush();
 	}
 
-	class TcpFetchConnection extends PackFetchConnection {
+	class TcpFetchConnection extends BasePackFetchConnection {
 		private Socket sock;
 
 		TcpFetchConnection() throws TransportException {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java
index 8944df7..f6e456a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java
@@ -188,7 +188,7 @@ class TransportGitSsh extends PackTransport {
 		}
 	}
 
-	class SshFetchConnection extends PackFetchConnection {
+	class SshFetchConnection extends BasePackFetchConnection {
 		private Session session;
 
 		private ChannelExec channel;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
index cde648d..e109cf4 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
@@ -83,7 +83,7 @@ class TransportLocal extends PackTransport {
 		return new LocalFetchConnection();
 	}
 
-	class LocalFetchConnection extends PackFetchConnection {
+	class LocalFetchConnection extends BasePackFetchConnection {
 		private Process uploadPack;
 
 		LocalFetchConnection() throws TransportException {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
index 45c2ded..78116b2 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
@@ -95,7 +95,7 @@ import org.spearce.jgit.treewalk.TreeWalk;
  * 
  * @see WalkRemoteObjectDatabase
  */
-class WalkFetchConnection extends FetchConnection {
+class WalkFetchConnection extends BaseFetchConnection {
 	/** The repository this transport fetches into, or pushes out of. */
 	private final Repository local;
 
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 10/23] Add ignoreMissingUninteresting option to PackWriter
  2008-06-27 22:06                 ` [EGIT PATCH 09/23] Big refactor: *Connection hierarchy Marek Zawirski
@ 2008-06-27 22:06                   ` Marek Zawirski
  2008-06-27 22:06                     ` [EGIT PATCH 11/23] Add BasePackPushConnection implementing git-send-pack protocol Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

This option is useful when caller cares only about locally existing
uninteresting objects.

Test cases created.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../tst/org/spearce/jgit/lib/PackWriterTest.java   |   45 +++++++++++++++++---
 .../src/org/spearce/jgit/lib/PackWriter.java       |   37 +++++++++++-----
 2 files changed, 65 insertions(+), 17 deletions(-)

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/PackWriterTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/PackWriterTest.java
index 9572342..f94eb72 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/PackWriterTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/PackWriterTest.java
@@ -120,7 +120,7 @@ public class PackWriterTest extends RepositoryTestCase {
 	 * @throws IOException
 	 */
 	public void testWriteEmptyPack1() throws IOException {
-		createVerifyOpenPack(EMPTY_LIST_OBJECT, EMPTY_LIST_OBJECT, false);
+		createVerifyOpenPack(EMPTY_LIST_OBJECT, EMPTY_LIST_OBJECT, false, false);
 
 		assertEquals(0, writer.getObjectsNumber());
 		assertEquals(0, pack.getObjectCount());
@@ -142,6 +142,37 @@ public class PackWriterTest extends RepositoryTestCase {
 	}
 
 	/**
+	 * Try to pass non-existing object as uninteresting, with non-ignoring
+	 * setting.
+	 * 
+	 * @throws IOException
+	 */
+	public void testNotIgnoreNonExistingObjects() throws IOException {
+		final ObjectId nonExisting = ObjectId
+				.fromString("0000000000000000000000000000000000000001");
+		try {
+			createVerifyOpenPack(EMPTY_LIST_OBJECT, Collections.nCopies(1,
+					nonExisting), false, false);
+			fail("Should have thrown MissingObjectException");
+		} catch (MissingObjectException x) {
+			// expected
+		}
+	}
+
+	/**
+	 * Try to pass non-existing object as uninteresting, with ignoring setting.
+	 * 
+	 * @throws IOException
+	 */
+	public void testIgnoreNonExistingObjects() throws IOException {
+		final ObjectId nonExisting = ObjectId
+				.fromString("0000000000000000000000000000000000000001");
+		createVerifyOpenPack(EMPTY_LIST_OBJECT, Collections.nCopies(1,
+				nonExisting), false, true);
+		// shouldn't throw anything
+	}
+
+	/**
 	 * Create pack basing on only interesting objects, then precisely verify
 	 * content. No delta reuse here.
 	 * 
@@ -326,7 +357,7 @@ public class PackWriterTest extends RepositoryTestCase {
 		final LinkedList<ObjectId> interestings = new LinkedList<ObjectId>();
 		interestings.add(ObjectId
 				.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
-		createVerifyOpenPack(interestings, EMPTY_LIST_OBJECT, false);
+		createVerifyOpenPack(interestings, EMPTY_LIST_OBJECT, false, false);
 
 		final ObjectId expectedOrder[] = new ObjectId[] {
 				ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
@@ -352,7 +383,7 @@ public class PackWriterTest extends RepositoryTestCase {
 		final LinkedList<ObjectId> uninterestings = new LinkedList<ObjectId>();
 		uninterestings.add(ObjectId
 				.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"));
-		createVerifyOpenPack(interestings, uninterestings, false);
+		createVerifyOpenPack(interestings, uninterestings, false, false);
 
 		final ObjectId expectedOrder[] = new ObjectId[] {
 				ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
@@ -380,7 +411,7 @@ public class PackWriterTest extends RepositoryTestCase {
 		final LinkedList<ObjectId> uninterestings = new LinkedList<ObjectId>();
 		uninterestings.add(ObjectId
 				.fromString("c59759f143fb1fe21c197981df75a7ee00290799"));
-		createVerifyOpenPack(interestings, uninterestings, thin);
+		createVerifyOpenPack(interestings, uninterestings, thin, false);
 
 		final ObjectId writtenObjects[] = new ObjectId[] {
 				ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
@@ -404,9 +435,11 @@ public class PackWriterTest extends RepositoryTestCase {
 	}
 
 	private void createVerifyOpenPack(final Collection<ObjectId> interestings,
-			final Collection<ObjectId> uninterestings, final boolean thin)
+			final Collection<ObjectId> uninterestings, final boolean thin,
+			final boolean ignoreMissingUninteresting)
 			throws MissingObjectException, IOException {
-		writer.writePack(interestings, uninterestings, thin);
+		writer.writePack(interestings, uninterestings, thin,
+				ignoreMissingUninteresting);
 		verifyOpenPack(thin);
 	}
 
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java
index ba43da5..a331237 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java
@@ -77,7 +77,7 @@ import org.spearce.jgit.util.NB;
  * Typical usage consists of creating instance intended for some pack,
  * configuring options through accessors methods and finally call
  * {@link #writePack(Iterator)} or
- * {@link #writePack(Collection, Collection, boolean)} with objects
+ * {@link #writePack(Collection, Collection, boolean, boolean)} with objects
  * specification, to generate a pack stream.
  * </p>
  * <p>
@@ -98,7 +98,7 @@ public class PackWriter {
 	 * Title of {@link ProgressMonitor} task used during counting objects to
 	 * pack.
 	 * 
-	 * @see #writePack(Collection, Collection, boolean)
+	 * @see #writePack(Collection, Collection, boolean, boolean)
 	 */
 	public static final String COUNTING_OBJECTS_PROGRESS = "Counting objects to pack";
 
@@ -107,7 +107,7 @@ public class PackWriter {
 	 * reuse or delta reuse.
 	 * 
 	 * @see #writePack(Iterator)
-	 * @see #writePack(Collection, Collection, boolean)
+	 * @see #writePack(Collection, Collection, boolean, boolean)
 	 */
 	public static final String SEARCHING_REUSE_PROGRESS = "Searching for delta and object reuse";
 
@@ -116,7 +116,7 @@ public class PackWriter {
 	 * (objects)
 	 * 
 	 * @see #writePack(Iterator)
-	 * @see #writePack(Collection, Collection, boolean)
+	 * @see #writePack(Collection, Collection, boolean, boolean)
 	 */
 	public static final String WRITING_OBJECTS_PROGRESS = "Writing objects";
 
@@ -193,7 +193,7 @@ public class PackWriter {
 	 * Create writer for specified repository, that will write a pack to
 	 * provided output stream. Objects for packing are specified in
 	 * {@link #writePack(Iterator)} or
-	 * {@link #writePack(Collection, Collection, boolean)}.
+	 * {@link #writePack(Collection, Collection, boolean, boolean)}.
 	 * 
 	 * @param repo
 	 *            repository where objects are stored.
@@ -203,7 +203,7 @@ public class PackWriter {
 	 * @param monitor
 	 *            operations progress monitor, used within
 	 *            {@link #writePack(Iterator)} or
-	 *            {@link #writePack(Collection, Collection, boolean)}.
+	 *            {@link #writePack(Collection, Collection, boolean, boolean)}.
 	 */
 	public PackWriter(final Repository repo, final OutputStream out,
 			final ProgressMonitor monitor) {
@@ -233,7 +233,8 @@ public class PackWriter {
 	 * writer will search for delta representation of object in repository and
 	 * use it if possible. Normally, only deltas with base to another object
 	 * existing in set of objects to pack will be used. Exception is however
-	 * thin-pack (see {@link #writePack(Collection, Collection, boolean)} and
+	 * thin-pack (see
+	 * {@link #writePack(Collection, Collection, boolean, boolean)} and
 	 * {@link #writePack(Iterator)}) where base object must exist on other side
 	 * machine.
 	 * <p>
@@ -442,15 +443,21 @@ public class PackWriter {
 	 *            belonging to party repository (uninteresting/boundary) as
 	 *            determined by set; this kind of pack is used only for
 	 *            transport; true - to produce thin pack, false - otherwise.
+	 * @param ignoreMissingUninteresting
+	 *            true if writer should ignore non existing uninteresting
+	 *            objects during construction set of objects to pack; false
+	 *            otherwise - non existing uninteresting objects may cause
+	 *            {@link MissingObjectException}
 	 * @throws IOException
 	 *             when some I/O problem occur during reading objects for pack
 	 *             or writing pack stream.
 	 */
 	public void writePack(final Collection<ObjectId> interestingObjects,
-			final Collection<ObjectId> uninterestingObjects, boolean thin)
+			final Collection<ObjectId> uninterestingObjects,
+			final boolean thin, final boolean ignoreMissingUninteresting)
 			throws IOException {
 		ObjectWalk walker = setUpWalker(interestingObjects,
-				uninterestingObjects, thin);
+				uninterestingObjects, thin, ignoreMissingUninteresting);
 		findObjectsToPack(walker);
 		writePackInternal();
 	}
@@ -682,7 +689,8 @@ public class PackWriter {
 
 	private ObjectWalk setUpWalker(
 			final Collection<ObjectId> interestingObjects,
-			final Collection<ObjectId> uninterestingObjects, boolean thin)
+			final Collection<ObjectId> uninterestingObjects,
+			final boolean thin, final boolean ignoreMissingUninteresting)
 			throws MissingObjectException, IOException,
 			IncorrectObjectTypeException {
 		final ObjectWalk walker = new ObjectWalk(db);
@@ -696,7 +704,14 @@ public class PackWriter {
 			walker.markStart(o);
 		}
 		for (ObjectId id : uninterestingObjects) {
-			RevObject o = walker.parseAny(id);
+			final RevObject o;
+			try {
+				o = walker.parseAny(id);
+			} catch (MissingObjectException x) {
+				if (ignoreMissingUninteresting)
+					continue;
+				throw x;
+			}
 			walker.markUninteresting(o);
 		}
 		return walker;
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 11/23] Add BasePackPushConnection implementing git-send-pack protocol
  2008-06-27 22:06                   ` [EGIT PATCH 10/23] Add ignoreMissingUninteresting option to PackWriter Marek Zawirski
@ 2008-06-27 22:06                     ` Marek Zawirski
  2008-06-27 22:06                       ` [EGIT PATCH 12/23] Fix: let RevWalk reset correctly before isMergedInto() Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Implementation realies extensively on RemoteRefUpdate as input.

It supports report-status capability, and honors delete-refs one.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../jgit/transport/BasePackPushConnection.java     |  226 ++++++++++++++++++++
 1 files changed, 226 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
new file mode 100644
index 0000000..159e331
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * 
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import org.spearce.jgit.errors.PackProtocolException;
+import org.spearce.jgit.errors.TransportException;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.PackWriter;
+import org.spearce.jgit.lib.ProgressMonitor;
+import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.transport.RemoteRefUpdate.Status;
+
+/**
+ * Push implementation using the native Git pack transfer service.
+ * <p>
+ * This is the canonical implementation for transferring objects to the remote
+ * repository from the local repository by talking to the 'git-receive-pack'
+ * service. Objects are packed on the local side into a pack file and then sent
+ * to the remote repository.
+ * <p>
+ * This connection requires only a bi-directional pipe or socket, and thus is
+ * easily wrapped up into a local process pipe, anonymous TCP socket, or a
+ * command executed through an SSH tunnel.
+ * <p>
+ * This implementation honors {@link Transport#isPushThin()} option.
+ * <p>
+ * Concrete implementations should just call
+ * {@link #init(java.io.InputStream, java.io.OutputStream)} and
+ * {@link #readAdvertisedRefs()} methods in constructor or before any use. They
+ * should also handle resources releasing in {@link #close()} method if needed.
+ */
+class BasePackPushConnection extends BasePackConnection implements
+		PushConnection {
+	static final String CAPABILITY_REPORT_STATUS = "report-status";
+
+	static final String CAPABILITY_DELETE_REFS = "delete-refs";
+
+	private final boolean thinPack;
+
+	private boolean capableDeleteRefs;
+
+	private boolean capableReport;
+
+	private boolean sentCommand;
+
+	private boolean writePack;
+
+	BasePackPushConnection(final PackTransport transport) {
+		super(transport);
+		thinPack = transport.isPushThin();
+	}
+
+	public void push(final ProgressMonitor monitor,
+			final Map<String, RemoteRefUpdate> refUpdates)
+			throws TransportException {
+		markStartedOperation();
+		doPush(monitor, refUpdates);
+	}
+
+	protected void doPush(final ProgressMonitor monitor,
+			final Map<String, RemoteRefUpdate> refUpdates)
+			throws TransportException {
+		try {
+			writeCommands(refUpdates.values(), monitor);
+			if (writePack)
+				writePack(refUpdates, monitor);
+			if (sentCommand && capableReport)
+				readStatusReport(refUpdates);
+		} catch (TransportException e) {
+			throw e;
+		} catch (Exception e) {
+			throw new TransportException(uri + ": " + e.getMessage(), e);
+		} finally {
+			close();
+		}
+	}
+
+	private void writeCommands(final Collection<RemoteRefUpdate> refUpdates,
+			final ProgressMonitor monitor) throws IOException {
+		final String capabilties = enableCapabilties();
+		for (final RemoteRefUpdate rru : refUpdates) {
+			if (!capableDeleteRefs && rru.isDelete()) {
+				rru.setStatus(Status.REJECTED_NODELETE);
+				continue;
+			}
+
+			final StringBuilder sb = new StringBuilder();
+			final Ref advertisedRef = getRef(rru.getRemoteName());
+			final ObjectId oldId = (advertisedRef == null ? ObjectId.zeroId()
+					: advertisedRef.getObjectId());
+			sb.append(oldId);
+			sb.append(' ');
+			sb.append(rru.getNewObjectId());
+			sb.append(' ');
+			sb.append(rru.getRemoteName());
+			if (!sentCommand) {
+				sentCommand = true;
+				sb.append(capabilties);
+			}
+
+			pckOut.writeString(sb.toString());
+			rru.setStatus(sentCommand ? Status.AWAITING_REPORT : Status.OK);
+			if (!rru.isDelete())
+				writePack = true;
+		}
+
+		if (monitor.isCancelled())
+			throw new TransportException(uri + ": push cancelled");
+		pckOut.end();
+	}
+
+	private String enableCapabilties() {
+		final StringBuilder line = new StringBuilder();
+		capableReport = wantCapability(line, CAPABILITY_REPORT_STATUS);
+		capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);
+		if (line.length() > 0)
+			line.insert(0, '\0');
+		return line.toString();
+	}
+
+	private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
+			final ProgressMonitor monitor) throws IOException {
+		final PackWriter writer = new PackWriter(local, out, monitor);
+		final ArrayList<ObjectId> remoteObjects = new ArrayList<ObjectId>(
+				getRefs().size());
+		final ArrayList<ObjectId> newObjects = new ArrayList<ObjectId>(
+				refUpdates.size());
+
+		for (final Ref r : getRefs())
+			remoteObjects.add(r.getObjectId());
+		for (final RemoteRefUpdate r : refUpdates.values())
+			newObjects.add(r.getNewObjectId());
+
+		writer.writePack(newObjects, remoteObjects, thinPack, true);
+	}
+
+	private void readStatusReport(final Map<String, RemoteRefUpdate> refUpdates)
+			throws IOException {
+		final String unpackLine = pckIn.readString();
+		if (!unpackLine.startsWith("unpack "))
+			throw new PackProtocolException(uri + ": unexpected report line: "
+					+ unpackLine);
+		final String unpackStatus = unpackLine.substring("unpack ".length());
+		if (!unpackStatus.equals("ok"))
+			throw new TransportException(uri
+					+ ": error occurred during unpacking on the remote end: "
+					+ unpackStatus);
+
+		String refLine;
+		while ((refLine = pckIn.readString()).length() > 0) {
+			boolean ok = false;
+			int refNameEnd = -1;
+			if (refLine.startsWith("ok ")) {
+				ok = true;
+				refNameEnd = refLine.length();
+			} else if (refLine.startsWith("ng ")) {
+				ok = false;
+				refNameEnd = refLine.indexOf(" ", 3);
+			}
+			if (refNameEnd == -1)
+				throw new PackProtocolException(uri
+						+ ": unexpected report line: " + refLine);
+			final String refName = refLine.substring(3, refNameEnd);
+			final String message = (ok ? null : refLine
+					.substring(refNameEnd + 1));
+
+			final RemoteRefUpdate rru = refUpdates.get(refName);
+			if (rru == null)
+				throw new PackProtocolException(uri
+						+ ": unexpected ref report: " + refName);
+			if (ok) {
+				rru.setStatus(Status.OK);
+			} else {
+				rru.setStatus(Status.REJECTED_OTHER_REASON);
+				rru.setMessage(message);
+			}
+		}
+		for (final RemoteRefUpdate rru : refUpdates.values()) {
+			if (rru.getStatus() == Status.AWAITING_REPORT)
+				throw new PackProtocolException(uri
+						+ ": expected report for ref " + rru.getRemoteName()
+						+ " not received");
+		}
+	}
+}
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 12/23] Fix: let RevWalk reset correctly before isMergedInto()
  2008-06-27 22:06                     ` [EGIT PATCH 11/23] Add BasePackPushConnection implementing git-send-pack protocol Marek Zawirski
@ 2008-06-27 22:06                       ` Marek Zawirski
  2008-06-27 22:06                         ` [EGIT PATCH 13/23] Add PushProcess class implementing git-send-pack logic Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Retained flags was wrongly computed for reset() call inside
isMergedInto().

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../src/org/spearce/jgit/revwalk/RevWalk.java      |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/revwalk/RevWalk.java b/org.spearce.jgit/src/org/spearce/jgit/revwalk/RevWalk.java
index fc757a5..7976d75 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/revwalk/RevWalk.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/revwalk/RevWalk.java
@@ -347,7 +347,7 @@ public class RevWalk implements Iterable<RevCommit> {
 		final TreeFilter oldTF = treeFilter;
 		try {
 			finishDelayedFreeFlags();
-			reset(~freeFlags & ~RESERVED_FLAGS);
+			reset(~freeFlags & APP_FLAGS);
 			filter = RevFilter.MERGE_BASE;
 			treeFilter = TreeFilter.ALL;
 			markStart(tip);
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 13/23] Add PushProcess class implementing git-send-pack logic
  2008-06-27 22:06                       ` [EGIT PATCH 12/23] Fix: let RevWalk reset correctly before isMergedInto() Marek Zawirski
@ 2008-06-27 22:06                         ` Marek Zawirski
  2008-06-27 22:06                           ` [EGIT PATCH 14/23] Clarify Repository#resolve() documentation Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

This class perform analogous operations as FetchProcess. It processes
refs advertised by connection, updates RemoteRefUpdates and
local tracking branches - TrackingRefUpdates.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../org/spearce/jgit/transport/PushProcess.java    |  224 ++++++++++++++++++++
 1 files changed, 224 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java
new file mode 100644
index 0000000..f742949
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * 
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.spearce.jgit.errors.MissingObjectException;
+import org.spearce.jgit.errors.NotSupportedException;
+import org.spearce.jgit.errors.TransportException;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.ProgressMonitor;
+import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.revwalk.RevCommit;
+import org.spearce.jgit.revwalk.RevObject;
+import org.spearce.jgit.revwalk.RevWalk;
+import org.spearce.jgit.transport.RemoteRefUpdate.Status;
+
+/**
+ * Class performing push operation on remote repository.
+ * 
+ * @see Transport#push(ProgressMonitor, Collection)
+ */
+class PushProcess {
+	/** Task name for {@link ProgressMonitor} used during opening connection. */
+	static final String PROGRESS_OPENING_CONNECTION = "Opening connection";
+
+	/** Transport used to perform this operation. */
+	private final Transport transport;
+
+	/** Push operation connection created to perform this operation */
+	private PushConnection connection;
+
+	/** Refs to update on remote side. */
+	private final Map<String, RemoteRefUpdate> toPush;
+
+	/** Revision walker for checking some updates properties. */
+	private final RevWalk walker;
+
+	/**
+	 * Create process for specified transport and refs updates specification.
+	 * 
+	 * @param transport
+	 *            transport between remote and local repository, used to create
+	 *            connection.
+	 * @param toPush
+	 *            specification of refs updates (and local tracking branches).
+	 * @throws TransportException
+	 */
+	PushProcess(final Transport transport,
+			final Collection<RemoteRefUpdate> toPush) throws TransportException {
+		this.walker = new RevWalk(transport.local);
+		this.transport = transport;
+		this.toPush = new HashMap<String, RemoteRefUpdate>();
+		for (final RemoteRefUpdate rru : toPush) {
+			if (this.toPush.put(rru.getRemoteName(), rru) != null)
+				throw new TransportException(
+						"Duplicate remote ref update is illegal. Affected remote name: "
+								+ rru.getRemoteName());
+		}
+	}
+
+	/**
+	 * Perform push operation between local and remote repository - set remote
+	 * refs appropriately, send needed objects and update local tracking refs.
+	 * 
+	 * @param monitor
+	 *            progress monitor used for feedback about operation.
+	 * @return result of push operation with complete status description.
+	 * @throws NotSupportedException
+	 *             when push operation is not supported by provided transport.
+	 * @throws TransportException
+	 *             when some error occurred during operation, like I/O, protocol
+	 *             error, or local database consistency error.
+	 */
+	PushResult execute(final ProgressMonitor monitor)
+			throws NotSupportedException, TransportException {
+		monitor.beginTask(PROGRESS_OPENING_CONNECTION, ProgressMonitor.UNKNOWN);
+		connection = transport.openPush();
+		try {
+			monitor.endTask();
+
+			final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates();
+			if (!preprocessed.isEmpty())
+				connection.push(monitor, preprocessed);
+		} finally {
+			connection.close();
+		}
+		updateTrackingRefs();
+		return prepareOperationResult();
+	}
+
+	private Map<String, RemoteRefUpdate> prepareRemoteUpdates()
+			throws TransportException {
+		final Map<String, RemoteRefUpdate> result = new HashMap<String, RemoteRefUpdate>();
+		for (final RemoteRefUpdate rru : toPush.values()) {
+			final Ref advertisedRef = connection.getRef(rru.getRemoteName());
+			final ObjectId advertisedOld = (advertisedRef == null ? ObjectId
+					.zeroId() : advertisedRef.getObjectId());
+
+			if (rru.getNewObjectId().equals(advertisedOld)) {
+				if (rru.isDelete()) {
+					// ref does exist neither locally nor remotely
+					rru.setStatus(Status.NON_EXISTING);
+				} else {
+					// same object - nothing to do
+					rru.setStatus(Status.UP_TO_DATE);
+				}
+				continue;
+			}
+
+			// caller has explicitly specified expected old object id, while it
+			// has been changed in the mean time - reject
+			if (rru.isExpectingOldObjectId()
+					&& !rru.getExpectedOldObjectId().equals(advertisedOld)) {
+				rru.setStatus(Status.REJECTED_REMOTE_CHANGED);
+				continue;
+			}
+
+			// create ref (hasn't existed on remote side) and delete ref
+			// are always fast-forward commands, feasible at this level
+			if (advertisedOld.equals(ObjectId.zeroId()) || rru.isDelete()) {
+				rru.setFastForward(true);
+				result.put(rru.getRemoteName(), rru);
+				continue;
+			}
+
+			// check for fast-forward:
+			// - both old and new ref must point to commits, AND
+			// - both of them must be known for us, exist in repository, AND
+			// - old commit must be ancestor of new commit
+			boolean fastForward = true;
+			try {
+				RevObject oldRev = walker.parseAny(advertisedOld);
+				final RevObject newRev = walker.parseAny(rru.getNewObjectId());
+				if (!(oldRev instanceof RevCommit)
+						|| !(newRev instanceof RevCommit)
+						|| !walker.isMergedInto((RevCommit) oldRev,
+								(RevCommit) newRev))
+					fastForward = false;
+			} catch (MissingObjectException x) {
+				fastForward = false;
+			} catch (Exception x) {
+				throw new TransportException(transport.getURI()
+						+ ": reading objects from local repository failed: "
+						+ x.getMessage(), x);
+			}
+			rru.setFastForward(fastForward);
+			if (!fastForward && !rru.isForceUpdate())
+				rru.setStatus(Status.REJECTED_NONFASTFORWARD);
+			else
+				result.put(rru.getRemoteName(), rru);
+		}
+		return result;
+	}
+
+	private void updateTrackingRefs() {
+		for (final RemoteRefUpdate rru : toPush.values()) {
+			final Status status = rru.getStatus();
+			if (rru.hasTrackingRefUpdate()
+					&& (status == Status.UP_TO_DATE || status == Status.OK)) {
+				// update local tracking branch only when there is a chance that
+				// it has changed; this is possible for:
+				// -updated (OK) status,
+				// -up to date (UP_TO_DATE) status
+				try {
+					rru.updateTrackingRef(walker);
+				} catch (IOException e) {
+					// ignore as RefUpdate has stored I/O error status
+				}
+			}
+		}
+	}
+
+	private PushResult prepareOperationResult() {
+		final PushResult result = new PushResult();
+		result.setAdvertisedRefs(connection.getRefsMap());
+		result.setRemoteUpdates(toPush);
+
+		for (final RemoteRefUpdate rru : toPush.values()) {
+			final TrackingRefUpdate tru = rru.getTrackingRefUpdate();
+			if (tru != null)
+				result.add(tru);
+		}
+		return result;
+	}
+}
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 14/23] Clarify Repository#resolve() documentation
  2008-06-27 22:06                         ` [EGIT PATCH 13/23] Add PushProcess class implementing git-send-pack logic Marek Zawirski
@ 2008-06-27 22:06                           ` Marek Zawirski
  2008-06-27 22:06                             ` [EGIT PATCH 15/23] Add String versions of methods in RefSpec Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Say explicitly that this method returns null when revstr can't be
resolved.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../src/org/spearce/jgit/lib/Repository.java       |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
index 64f93ff..2b43b2c 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
@@ -524,7 +524,7 @@ public class Repository {
 	 * </ul>
 	 *
 	 * @param revstr A git object references expression
-	 * @return an ObjectId
+	 * @return an ObjectId or null if revstr can't be resolved to any ObjectId
 	 * @throws IOException on serious errors
 	 */
 	public ObjectId resolve(final String revstr) throws IOException {
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 15/23] Add String versions of methods in RefSpec
  2008-06-27 22:06                           ` [EGIT PATCH 14/23] Clarify Repository#resolve() documentation Marek Zawirski
@ 2008-06-27 22:06                             ` Marek Zawirski
  2008-06-27 22:06                               ` [EGIT PATCH 16/23] Transport* - general support for push() and implementations Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

matchDestination(), matchSource(), expandFromSourec() can take String
as parameter now, not only Ref (we just need a ref name).

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../src/org/spearce/jgit/transport/RefSpec.java    |   46 +++++++++++++++++--
 1 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java
index 38489be..1589e19 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java
@@ -281,6 +281,17 @@ public class RefSpec {
 	}
 
 	/**
+	 * Does this specification's source description match the ref name?
+	 * 
+	 * @param r
+	 *            ref name that should be tested.
+	 * @return true if the names match; false otherwise.
+	 */
+	public boolean matchSource(final String r) {
+		return match(r, getSource());
+	}
+
+	/**
 	 * Does this specification's source description match the ref?
 	 * 
 	 * @param r
@@ -288,7 +299,18 @@ public class RefSpec {
 	 * @return true if the names match; false otherwise.
 	 */
 	public boolean matchSource(final Ref r) {
-		return match(r, getSource());
+		return match(r.getName(), getSource());
+	}
+
+	/**
+	 * Does this specification's destination description match the ref name?
+	 * 
+	 * @param r
+	 *            ref name that should be tested.
+	 * @return true if the names match; false otherwise.
+	 */
+	public boolean matchDestination(final String r) {
+		return match(r, getDestination());
 	}
 
 	/**
@@ -299,7 +321,21 @@ public class RefSpec {
 	 * @return true if the names match; false otherwise.
 	 */
 	public boolean matchDestination(final Ref r) {
-		return match(r, getDestination());
+		return match(r.getName(), getDestination());
+	}
+
+	/**
+	 * Expand this specification to exactly match a ref name.
+	 * <p>
+	 * Callers must first verify the passed ref name matches this specification,
+	 * otherwise expansion results may be unpredictable.
+	 * 
+	 * @param r
+	 *            a ref name that matched our source specification.
+	 * @return a new specification that is not a wildcard.
+	 */
+	public RefSpec expandFromSource(final String r) {
+		return isWildcard() ? new RefSpec(this, r) : this;
 	}
 
 	/**
@@ -316,12 +352,12 @@ public class RefSpec {
 		return isWildcard() ? new RefSpec(this, r.getName()) : this;
 	}
 
-	private boolean match(final Ref r, final String s) {
+	private boolean match(final String refName, final String s) {
 		if (s == null)
 			return false;
 		if (isWildcard())
-			return r.getName().startsWith(s.substring(0, s.length() - 1));
-		return r.getName().equals(s);
+			return refName.startsWith(s.substring(0, s.length() - 1));
+		return refName.equals(s);
 	}
 
 	public int hashCode() {
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 16/23] Transport* - general support for push() and implementations
  2008-06-27 22:06                             ` [EGIT PATCH 15/23] Add String versions of methods in RefSpec Marek Zawirski
@ 2008-06-27 22:06                               ` Marek Zawirski
  2008-06-27 22:06                                 ` [EGIT PATCH 17/23] Test cases for PushProcess Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Implementation of push() at abstract Transport class level and
implementations of concrete protocols: SSH, local, git-daemon.

Some Transport* implementations required refactoring to share code
between pack and fetch connections.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../src/org/spearce/jgit/transport/Transport.java  |  184 +++++++++++++++++++-
 .../spearce/jgit/transport/TransportBundle.java    |    6 +
 .../spearce/jgit/transport/TransportGitAnon.java   |   39 ++++
 .../spearce/jgit/transport/TransportGitSsh.java    |   51 ++++++
 .../org/spearce/jgit/transport/TransportLocal.java |  113 ++++++++----
 .../org/spearce/jgit/transport/WalkTransport.java  |    7 +
 6 files changed, 361 insertions(+), 39 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
index c4b71eb..da5b41e 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  *
  * All rights reserved.
  *
@@ -42,12 +43,16 @@ import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import org.spearce.jgit.errors.NotSupportedException;
 import org.spearce.jgit.errors.TransportException;
 import org.spearce.jgit.lib.NullProgressMonitor;
 import org.spearce.jgit.lib.ProgressMonitor;
+import org.spearce.jgit.lib.Ref;
 import org.spearce.jgit.lib.Repository;
 
 /**
@@ -105,6 +110,8 @@ public abstract class Transport {
 		tn.setOptionUploadPack(cfg.getUploadPack());
 		tn.fetch = cfg.getFetchRefSpecs();
 		tn.tagopt = cfg.getTagOpt();
+		tn.setOptionReceivePack(cfg.getReceivePack());
+		tn.push = cfg.getPushRefSpecs();
 		return tn;
 	}
 
@@ -152,6 +159,20 @@ public abstract class Transport {
 	 */
 	public static final boolean DEFAULT_PUSH_THIN = false;
 
+	/**
+	 * Specification for fetch or push operations, to fetch or push all tags.
+	 * Acts as --tags.
+	 */
+	public static final RefSpec REFSPEC_TAGS = new RefSpec(
+			"refs/tags/*:refs/tags/*");
+
+	/**
+	 * Specification for push operation, to push all refs under refs/heads. Acts
+	 * as --all.
+	 */
+	public static final RefSpec REFSPEC_PUSH_ALL = new RefSpec(
+			"refs/heads/*:refs/heads/*");
+
 	/** The repository this transport fetches into, or pushes out of. */
 	protected final Repository local;
 
@@ -162,7 +183,7 @@ public abstract class Transport {
 	private String optionUploadPack = RemoteConfig.DEFAULT_UPLOAD_PACK;
 
 	/** Specifications to apply during fetch. */
-	private List<RefSpec> fetch = Collections.<RefSpec> emptyList();
+	private List<RefSpec> fetch = Collections.emptyList();
 
 	/**
 	 * How {@link #fetch(ProgressMonitor, Collection)} should handle tags.
@@ -178,6 +199,12 @@ public abstract class Transport {
 	/** Should fetch request thin-pack if remote repository can produce it. */
 	private boolean fetchThin = DEFAULT_FETCH_THIN;
 
+	/** Name of the receive pack program, if it must be executed. */
+	private String optionReceivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
+
+	/** Specifications to apply during push. */
+	private List<RefSpec> push = Collections.emptyList();
+
 	/** Should push produce thin-pack when sending objects to remote repository. */
 	private boolean pushThin = DEFAULT_PUSH_THIN;
 
@@ -274,6 +301,31 @@ public abstract class Transport {
 	}
 
 	/**
+	 * Default setting is: {@value RemoteConfig#DEFAULT_RECEIVE_PACK}
+	 * 
+	 * @return remote executable providing receive-pack service for pack
+	 *         transports.
+	 * @see PackTransport
+	 */
+	public String getOptionReceivePack() {
+		return optionReceivePack;
+	}
+
+	/**
+	 * Set remote executable providing receive-pack service for pack transports.
+	 * Default setting is: {@value RemoteConfig#DEFAULT_RECEIVE_PACK}
+	 * 
+	 * @param optionReceivePack
+	 *            remote executable, if null or empty default one is set;
+	 */
+	public void setOptionReceivePack(String optionReceivePack) {
+		if (optionReceivePack != null && optionReceivePack.length() > 0)
+			this.optionReceivePack = optionReceivePack;
+		else
+			this.optionReceivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
+	}
+
+	/**
 	 * Default setting is: {@value #DEFAULT_PUSH_THIN}
 	 * 
 	 * @return true if push should produce thin-pack in pack transports
@@ -356,6 +408,98 @@ public abstract class Transport {
 	}
 
 	/**
+	 * Push objects and refs from the local repository to the remote one.
+	 * <p>
+	 * This is a utility function providing standard push behavior. It updates
+	 * remote refs and send there necessary objects according to remote ref
+	 * update specification. After successful remote ref update, associated
+	 * locally stored tracking branch is updated if set up accordingly. Detailed
+	 * operation result is provided after execution.
+	 * <p>
+	 * For setting up remote ref update specification from ref spec, see helper
+	 * method {@link #findRemoteRefUpdatesFor(Collection)}, predefined refspecs ({@link #REFSPEC_TAGS},
+	 * {@link #REFSPEC_PUSH_ALL}) or consider using directly
+	 * {@link RemoteRefUpdate} for more possibilities.
+	 * 
+	 * @see RemoteRefUpdate
+	 * 
+	 * @param monitor
+	 *            progress monitor to inform the user about our processing
+	 *            activity. Must not be null. Use {@link NullProgressMonitor} if
+	 *            progress updates are not interesting or necessary.
+	 * @param toPush
+	 *            specification of refs to push. May be null or the empty
+	 *            collection to use the specifications from the RemoteConfig
+	 *            converted by {@link #findRemoteRefUpdatesFor(Collection)}. No
+	 *            more than 1 RemoteRefUpdate with the same remoteName is
+	 *            allowed.
+	 * @return information about results of remote refs updates, tracking refs
+	 *         updates and refs advertised by remote repository.
+	 * @throws NotSupportedException
+	 *             this transport implementation does not support pusing
+	 *             objects.
+	 * @throws TransportException
+	 *             the remote connection could not be established or object
+	 *             copying (if necessary) failed at I/O or protocol level or
+	 *             update specification was incorrect.
+	 */
+	public PushResult push(final ProgressMonitor monitor,
+			Collection<RemoteRefUpdate> toPush) throws NotSupportedException,
+			TransportException {
+		if (toPush == null || toPush.isEmpty()) {
+			// If the caller did not ask for anything use the defaults.
+			toPush = findRemoteRefUpdatesFor(push);
+			if (toPush.isEmpty())
+				throw new TransportException("Nothing to push.");
+		}
+		final PushProcess pushProcess = new PushProcess(this, toPush);
+		return pushProcess.execute(monitor);
+	}
+
+	/**
+	 * Convert push remote refs update specification from {@link RefSpec} form
+	 * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching
+	 * source part to local refs. expectedOldObjectId in RemoteRefUpdate is
+	 * always set as null. Tracking branch is configured if RefSpec destination
+	 * matches source of any fetch ref spec for this transport remote
+	 * configuration.
+	 * 
+	 * @param specs
+	 *            collection of RefSpec to convert.
+	 * @return collection of set up {@link RemoteRefUpdate}.
+	 * @throws TransportException
+	 *             when problem occurred during conversion or specification set
+	 *             up: most probably, missing objects or refs.
+	 */
+	public Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(
+			final Collection<RefSpec> specs) throws TransportException {
+		final List<RemoteRefUpdate> result = new LinkedList<RemoteRefUpdate>();
+		final Collection<RefSpec> procRefs = expandPushWildcardsFor(specs);
+
+		for (final RefSpec spec : procRefs) {
+			try {
+				final String srcRef = spec.getSource();
+				// null destination (no-colon in ref-spec) is a special case
+				final String remoteName = (spec.getDestination() == null ? spec
+						.getSource() : spec.getDestination());
+				final boolean forceUpdate = spec.isForceUpdate();
+				final String localName = findTrackingRefName(remoteName);
+
+				final RemoteRefUpdate rru = new RemoteRefUpdate(local, srcRef,
+						remoteName, forceUpdate, localName, null);
+				result.add(rru);
+			} catch (TransportException x) {
+				throw x;
+			} catch (Exception x) {
+				throw new TransportException(
+						"Problem with resolving push ref spec \"" + spec
+								+ "\" locally: " + x.getMessage(), x);
+			}
+		}
+		return result;
+	}
+
+	/**
 	 * Begins a new connection for fetching from the remote repository.
 	 * 
 	 * @return a fresh connection to fetch from the remote repository.
@@ -373,9 +517,41 @@ public abstract class Transport {
 	 * @return a fresh connection to push into the remote repository.
 	 * @throws NotSupportedException
 	 *             the implementation does not support pushing.
+	 * @throws TransportException
+	 *             the remote connection could not be established
 	 */
-	public final PushConnection openPush() throws NotSupportedException
-	/* TransportException */{
-		throw new NotSupportedException("No push support.");
+	public abstract PushConnection openPush() throws NotSupportedException,
+			TransportException;
+
+	private Collection<RefSpec> expandPushWildcardsFor(
+			final Collection<RefSpec> specs) {
+		final Map<String, Ref> localRefs = local.getAllRefs();
+		final Collection<RefSpec> procRefs = new HashSet<RefSpec>();
+
+		for (final RefSpec spec : specs) {
+			if (spec.isWildcard()) {
+				for (final Ref localRef : localRefs.values()) {
+					if (spec.matchSource(localRef))
+						procRefs.add(spec.expandFromSource(localRef));
+				}
+			} else {
+				procRefs.add(spec);
+			}
+		}
+		return procRefs;
+	}
+
+	private String findTrackingRefName(final String remoteName) {
+		// try to find matching tracking refs
+		for (final RefSpec fetchSpec : fetch) {
+			if (fetchSpec.matchSource(remoteName)) {
+				if (fetchSpec.isWildcard())
+					return fetchSpec.expandFromSource(remoteName)
+							.getDestination();
+				else
+					return fetchSpec.getDestination();
+			}
+		}
+		return null;
 	}
 }
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
index 48120a8..1bf081a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
@@ -95,6 +95,12 @@ class TransportBundle extends PackTransport {
 		return new BundleFetchConnection();
 	}
 
+	@Override
+	public PushConnection openPush() throws NotSupportedException {
+		throw new NotSupportedException(
+				"Push is not supported for bundle transport");
+	}
+
 	class BundleFetchConnection extends BaseFetchConnection {
 		FileInputStream in;
 
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
index a7a419e..6e49083 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  *
  * All rights reserved.
  *
@@ -71,6 +72,11 @@ class TransportGitAnon extends PackTransport {
 		return new TcpFetchConnection();
 	}
 
+	@Override
+	public PushConnection openPush() throws TransportException {
+		return new TcpPushConnection();
+	}
+
 	Socket openConnection() throws TransportException {
 		final int port = uri.getPort() > 0 ? uri.getPort() : GIT_PORT;
 		try {
@@ -131,4 +137,37 @@ class TransportGitAnon extends PackTransport {
 			}
 		}
 	}
+
+	class TcpPushConnection extends BasePackPushConnection {
+		private Socket sock;
+
+		TcpPushConnection() throws TransportException {
+			super(TransportGitAnon.this);
+			sock = openConnection();
+			try {
+				init(sock.getInputStream(), sock.getOutputStream());
+				service("git-receive-pack", pckOut);
+			} catch (IOException err) {
+				close();
+				throw new TransportException(uri.toString()
+						+ ": remote hung up unexpectedly", err);
+			}
+			readAdvertisedRefs();
+		}
+
+		@Override
+		public void close() {
+			super.close();
+
+			if (sock != null) {
+				try {
+					sock.close();
+				} catch (IOException err) {
+					// Ignore errors during close.
+				} finally {
+					sock = null;
+				}
+			}
+		}
+	}
 }
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java
index f6e456a..55be4f6 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  *
  * All rights reserved.
  *
@@ -88,6 +89,11 @@ class TransportGitSsh extends PackTransport {
 		return new SshFetchConnection();
 	}
 
+	@Override
+	public PushConnection openPush() throws TransportException {
+		return new SshPushConnection();
+	}
+
 	private static void sqMinimal(final StringBuilder cmd, final String val) {
 		if (val.matches("^[a-zA-Z0-9._/-]*$")) {
 			// If the string matches only generally safe characters
@@ -232,4 +238,49 @@ class TransportGitSsh extends PackTransport {
 			}
 		}
 	}
+
+	class SshPushConnection extends BasePackPushConnection {
+		private Session session;
+
+		private ChannelExec channel;
+
+		SshPushConnection() throws TransportException {
+			super(TransportGitSsh.this);
+			try {
+				session = openSession();
+				channel = exec(session, getOptionReceivePack());
+				init(channel.getInputStream(), channel.getOutputStream());
+			} catch (TransportException err) {
+				close();
+				throw err;
+			} catch (IOException err) {
+				close();
+				throw new TransportException(uri.toString()
+						+ ": remote hung up unexpectedly", err);
+			}
+			readAdvertisedRefs();
+		}
+
+		@Override
+		public void close() {
+			super.close();
+
+			if (channel != null) {
+				try {
+					if (channel.isConnected())
+						channel.disconnect();
+				} finally {
+					channel = null;
+				}
+			}
+
+			if (session != null) {
+				try {
+					sch.releaseSession(session);
+				} finally {
+					session = null;
+				}
+			}
+		}
+	}
 }
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
index e109cf4..f48dc6d 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
@@ -2,6 +2,7 @@
  * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  *
  * All rights reserved.
  *
@@ -43,6 +44,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.spearce.jgit.errors.NotSupportedException;
 import org.spearce.jgit.errors.TransportException;
 import org.spearce.jgit.lib.Repository;
 import org.spearce.jgit.util.FS;
@@ -83,50 +85,35 @@ class TransportLocal extends PackTransport {
 		return new LocalFetchConnection();
 	}
 
+	@Override
+	public PushConnection openPush() throws NotSupportedException,
+			TransportException {
+		return new LocalPushConnection();
+	}
+
+	protected Process startProcessWithErrStream(final String cmd)
+			throws TransportException {
+		try {
+			final Process proc = Runtime.getRuntime().exec(
+					new String[] { cmd, "." }, null, remoteGitDir);
+			new StreamRewritingThread(proc.getErrorStream()).start();
+			return proc;
+		} catch (IOException err) {
+			throw new TransportException(uri.toString() + ": "
+					+ err.getMessage(), err);
+		}
+	}
+
 	class LocalFetchConnection extends BasePackFetchConnection {
 		private Process uploadPack;
 
 		LocalFetchConnection() throws TransportException {
 			super(TransportLocal.this);
-			try {
-				uploadPack = Runtime.getRuntime().exec(
-						new String[] { getOptionUploadPack(), "." }, null,
-						remoteGitDir);
-			} catch (IOException err) {
-				throw new TransportException(uri.toString() + ": "
-						+ err.getMessage(), err);
-			}
-			startErrorThread();
+			uploadPack = startProcessWithErrStream(getOptionReceivePack());
 			init(uploadPack.getInputStream(), uploadPack.getOutputStream());
 			readAdvertisedRefs();
 		}
 
-		private void startErrorThread() {
-			final InputStream errorStream = uploadPack.getErrorStream();
-			new Thread("JGit " + getOptionUploadPack() + " Errors") {
-				public void run() {
-					final byte[] tmp = new byte[512];
-					try {
-						for (;;) {
-							final int n = errorStream.read(tmp);
-							if (n < 0)
-								break;
-							System.err.write(tmp, 0, n);
-							System.err.flush();
-						}
-					} catch (IOException err) {
-						// Ignore errors reading errors.
-					} finally {
-						try {
-							errorStream.close();
-						} catch (IOException err2) {
-							// Ignore errors closing the pipe.
-						}
-					}
-				}
-			}.start();
-		}
-
 		@Override
 		public void close() {
 			super.close();
@@ -142,4 +129,60 @@ class TransportLocal extends PackTransport {
 			}
 		}
 	}
+
+	class LocalPushConnection extends BasePackPushConnection {
+		private Process receivePack;
+
+		LocalPushConnection() throws TransportException {
+			super(TransportLocal.this);
+			receivePack = startProcessWithErrStream(getOptionReceivePack());
+			init(receivePack.getInputStream(), receivePack.getOutputStream());
+			readAdvertisedRefs();
+		}
+
+		@Override
+		public void close() {
+			super.close();
+
+			if (receivePack != null) {
+				try {
+					receivePack.waitFor();
+				} catch (InterruptedException ie) {
+					// Stop waiting and return anyway.
+				} finally {
+					receivePack = null;
+				}
+			}
+		}
+	}
+
+	class StreamRewritingThread extends Thread {
+		private final InputStream in;
+
+		StreamRewritingThread(final InputStream in) {
+			super("JGit " + getOptionUploadPack() + " Errors");
+			this.in = in;
+		}
+
+		public void run() {
+			final byte[] tmp = new byte[512];
+			try {
+				for (;;) {
+					final int n = in.read(tmp);
+					if (n < 0)
+						break;
+					System.err.write(tmp, 0, n);
+					System.err.flush();
+				}
+			} catch (IOException err) {
+				// Ignore errors reading errors.
+			} finally {
+				try {
+					in.close();
+				} catch (IOException err2) {
+					// Ignore errors closing the pipe.
+				}
+			}
+		}
+	}
 }
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkTransport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkTransport.java
index ae51d6d..29dd661 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkTransport.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkTransport.java
@@ -37,6 +37,7 @@
 
 package org.spearce.jgit.transport;
 
+import org.spearce.jgit.errors.NotSupportedException;
 import org.spearce.jgit.lib.Repository;
 
 /**
@@ -55,4 +56,10 @@ abstract class WalkTransport extends Transport {
 	WalkTransport(final Repository local, final URIish u) {
 		super(local, u);
 	}
+
+	@Override
+	public PushConnection openPush() throws NotSupportedException {
+		throw new NotSupportedException(
+				"Push is not supported by object walking transports");
+	}
 }
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 17/23] Test cases for PushProcess
  2008-06-27 22:06                               ` [EGIT PATCH 16/23] Transport* - general support for push() and implementations Marek Zawirski
@ 2008-06-27 22:06                                 ` Marek Zawirski
  2008-06-27 22:06                                   ` [EGIT PATCH 18/23] Test cases for RefSpec to RemoteRefUpdate conversions Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Tests for push process with highly differentiated remote update cases,
that are possible in practice.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../spearce/jgit/transport/PushProcessTest.java    |  407 ++++++++++++++++++++
 1 files changed, 407 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/transport/PushProcessTest.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/PushProcessTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/PushProcessTest.java
new file mode 100644
index 0000000..f0cfe98
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/PushProcessTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.spearce.jgit.errors.NotSupportedException;
+import org.spearce.jgit.errors.TransportException;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.ProgressMonitor;
+import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.lib.RepositoryTestCase;
+import org.spearce.jgit.lib.TextProgressMonitor;
+import org.spearce.jgit.lib.RefUpdate.Result;
+import org.spearce.jgit.transport.RemoteRefUpdate.Status;
+
+public class PushProcessTest extends RepositoryTestCase {
+	private PushProcess process;
+
+	private MockTransport transport;
+
+	private HashSet<RemoteRefUpdate> refUpdates;
+
+	private HashSet<Ref> advertisedRefs;
+
+	private Status connectionUpdateStatus;
+
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+		transport = new MockTransport(db, new URIish());
+		refUpdates = new HashSet<RemoteRefUpdate>();
+		advertisedRefs = new HashSet<Ref>();
+		connectionUpdateStatus = Status.OK;
+	}
+
+	/**
+	 * Test for fast-forward remote update.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateFastForward() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", false, null, null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
+		testOneUpdateStatus(rru, ref, Status.OK, true);
+	}
+
+	/**
+	 * Test for non fast-forward remote update, when remote object is not known
+	 * to local repository.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateNonFastForwardUnknownObject() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", false, null, null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("0000000000000000000000000000000000000001"));
+		testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
+	}
+
+	/**
+	 * Test for non fast-forward remote update, when remote object is known to
+	 * local repository, but it is not an ancestor of new object.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateNonFastForward() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
+				"refs/heads/master", false, null, null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
+		testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
+	}
+
+	/**
+	 * Test for non fast-forward remote update, when force update flag is set.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateNonFastForwardForced() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
+				"refs/heads/master", true, null, null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
+		testOneUpdateStatus(rru, ref, Status.OK, false);
+	}
+
+	/**
+	 * Test for remote ref creation.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateCreateRef() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
+				"refs/heads/master", false, null, null);
+		testOneUpdateStatus(rru, null, Status.OK, true);
+	}
+
+	/**
+	 * Test for remote ref deletion.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateDelete() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db, null,
+				"refs/heads/master", false, null, null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
+		testOneUpdateStatus(rru, ref, Status.OK, true);
+	}
+
+	/**
+	 * Test for remote ref deletion (try), when that ref doesn't exist on remote
+	 * repo.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateDeleteNonExisting() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db, null,
+				"refs/heads/master", false, null, null);
+		testOneUpdateStatus(rru, null, Status.NON_EXISTING, null);
+	}
+
+	/**
+	 * Test for remote ref update, when it is already up to date.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateUpToDate() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", false, null, null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
+		testOneUpdateStatus(rru, ref, Status.UP_TO_DATE, null);
+	}
+
+	/**
+	 * Test for remote ref update with expected remote object.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateExpectedRemote() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", false, null, ObjectId
+						.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
+		testOneUpdateStatus(rru, ref, Status.OK, true);
+	}
+
+	/**
+	 * Test for remote ref update with expected old object set, when old object
+	 * is not that expected one.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateUnexpectedRemote() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", false, null, ObjectId
+						.fromString("0000000000000000000000000000000000000001"));
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
+		testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
+	}
+
+	/**
+	 * Test for remote ref update with expected old object set, when old object
+	 * is not that expected one and force update flag is set (which should have
+	 * lower priority) - shouldn't change behavior.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateUnexpectedRemoteVsForce() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", true, null, ObjectId
+						.fromString("0000000000000000000000000000000000000001"));
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
+		testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
+	}
+
+	/**
+	 * Test for remote ref udpate, when connection rejects update.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateRejectedByConnection() throws IOException {
+		connectionUpdateStatus = Status.REJECTED_OTHER_REASON;
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", false, null, null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
+		testOneUpdateStatus(rru, ref, Status.REJECTED_OTHER_REASON, null);
+	}
+
+	/**
+	 * Test for remote refs updates with mixed cases that shouldn't depend on
+	 * each other.
+	 * 
+	 * @throws IOException
+	 */
+	public void testUpdateMixedCases() throws IOException {
+		final RemoteRefUpdate rruOk = new RemoteRefUpdate(db, null,
+				"refs/heads/master", false, null, null);
+		final Ref refToChange = new Ref("refs/heads/master", ObjectId
+				.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
+		final RemoteRefUpdate rruReject = new RemoteRefUpdate(db, null,
+				"refs/heads/nonexisting", false, null, null);
+		refUpdates.add(rruOk);
+		refUpdates.add(rruReject);
+		advertisedRefs.add(refToChange);
+		executePush();
+		assertEquals(Status.OK, rruOk.getStatus());
+		assertEquals(true, rruOk.isFastForward());
+		assertEquals(Status.NON_EXISTING, rruReject.getStatus());
+	}
+
+	/**
+	 * Test for local tracking ref update.
+	 * 
+	 * @throws IOException
+	 */
+	public void testTrackingRefUpdateEnabled() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", false, "refs/remotes/test/master", null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
+		refUpdates.add(rru);
+		advertisedRefs.add(ref);
+		final PushResult result = executePush();
+		final TrackingRefUpdate tru = result
+				.getTrackingRefUpdate("refs/remotes/test/master");
+		assertNotNull(tru);
+		assertEquals("refs/remotes/test/master", tru.getLocalName());
+		assertEquals(Result.NEW, tru.getResult());
+	}
+
+	/**
+	 * Test for local tracking ref update disabled.
+	 * 
+	 * @throws IOException
+	 */
+	public void testTrackingRefUpdateDisabled() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", false, null, null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
+		refUpdates.add(rru);
+		advertisedRefs.add(ref);
+		final PushResult result = executePush();
+		assertTrue(result.getTrackingRefUpdates().isEmpty());
+	}
+
+	/**
+	 * Test for local tracking ref update when remote update has failed.
+	 * 
+	 * @throws IOException
+	 */
+	public void testTrackingRefUpdateOnReject() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
+				"refs/heads/master", false, null, null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
+		final PushResult result = testOneUpdateStatus(rru, ref,
+				Status.REJECTED_NONFASTFORWARD, null);
+		assertTrue(result.getTrackingRefUpdates().isEmpty());
+	}
+
+	/**
+	 * Test for push operation result - that contains expected elements.
+	 * 
+	 * @throws IOException
+	 */
+	public void testPushResult() throws IOException {
+		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
+				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
+				"refs/heads/master", false, "refs/remotes/test/master", null);
+		final Ref ref = new Ref("refs/heads/master", ObjectId
+				.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
+		refUpdates.add(rru);
+		advertisedRefs.add(ref);
+		final PushResult result = executePush();
+		assertEquals(1, result.getTrackingRefUpdates().size());
+		assertEquals(1, result.getAdvertisedRefs().size());
+		assertEquals(1, result.getRemoteUpdates().size());
+		assertNotNull(result.getTrackingRefUpdate("refs/remotes/test/master"));
+		assertNotNull(result.getAdvertisedRef("refs/heads/master"));
+		assertNotNull(result.getRemoteUpdate("refs/heads/master"));
+	}
+
+	private PushResult testOneUpdateStatus(final RemoteRefUpdate rru,
+			final Ref advertisedRef, final Status expectedStatus,
+			Boolean fastForward) throws NotSupportedException,
+			TransportException {
+		refUpdates.add(rru);
+		if (advertisedRef != null)
+			advertisedRefs.add(advertisedRef);
+		final PushResult result = executePush();
+		assertEquals(expectedStatus, rru.getStatus());
+		if (fastForward != null)
+			assertEquals(fastForward.booleanValue(), rru.isFastForward());
+		return result;
+	}
+
+	private PushResult executePush() throws NotSupportedException,
+			TransportException {
+		process = new PushProcess(transport, refUpdates);
+		return process.execute(new TextProgressMonitor());
+	}
+
+	private class MockTransport extends Transport {
+		MockTransport(Repository local, URIish uri) {
+			super(local, uri);
+		}
+
+		@Override
+		public FetchConnection openFetch() throws NotSupportedException,
+				TransportException {
+			throw new NotSupportedException("mock");
+		}
+
+		@Override
+		public PushConnection openPush() throws NotSupportedException,
+				TransportException {
+			return new MockPushConnection();
+		}
+	}
+
+	private class MockPushConnection extends BaseConnection implements
+			PushConnection {
+		MockPushConnection() {
+			final Map<String, Ref> refsMap = new HashMap<String, Ref>();
+			for (final Ref r : advertisedRefs)
+				refsMap.put(r.getName(), r);
+			available(refsMap);
+		}
+
+		@Override
+		public void close() {
+			// nothing here
+		}
+
+		public void push(ProgressMonitor monitor,
+				Map<String, RemoteRefUpdate> refUpdates)
+				throws TransportException {
+			for (final RemoteRefUpdate rru : refUpdates.values()) {
+				assertEquals(Status.NOT_ATTEMPTED, rru.getStatus());
+				rru.setStatus(connectionUpdateStatus);
+			}
+		}
+	}
+}
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 18/23] Test cases for RefSpec to RemoteRefUpdate conversions
  2008-06-27 22:06                                 ` [EGIT PATCH 17/23] Test cases for PushProcess Marek Zawirski
@ 2008-06-27 22:06                                   ` Marek Zawirski
  2008-06-27 22:06                                     ` [EGIT PATCH 19/23] Repository search for command line tools Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Tests for method Transport#findRemoteRefUpdatesFor().

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../org/spearce/jgit/transport/TransportTest.java  |  181 ++++++++++++++++++++
 1 files changed, 181 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java
new file mode 100644
index 0000000..47d738b
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.spearce.jgit.lib.RepositoryConfig;
+import org.spearce.jgit.lib.RepositoryTestCase;
+
+public class TransportTest extends RepositoryTestCase {
+	private Transport transport;
+
+	private RemoteConfig remoteConfig;
+
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+		final RepositoryConfig config = db.getConfig();
+		remoteConfig = new RemoteConfig(config, "test");
+		remoteConfig.addURI(new URIish("http://everyones.loves.git/u/2"));
+	}
+
+	/**
+	 * Test RefSpec to RemoteRefUpdate conversion with simple RefSpec - no
+	 * wildcard, no tracking ref in repo configuration.
+	 * 
+	 * @throws IOException
+	 */
+	public void testFindRemoteRefUpdatesNoWildcardNoTracking()
+			throws IOException {
+		transport = Transport.open(db, remoteConfig);
+		final Collection<RemoteRefUpdate> result = transport
+				.findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(
+						"refs/heads/master:refs/heads/x")));
+
+		assertEquals(1, result.size());
+		final RemoteRefUpdate rru = result.iterator().next();
+		assertNull(rru.getExpectedOldObjectId());
+		assertFalse(rru.isForceUpdate());
+		assertEquals("refs/heads/master", rru.getSrcRef());
+		assertEquals(db.resolve("refs/heads/master"), rru.getNewObjectId());
+		assertEquals("refs/heads/x", rru.getRemoteName());
+	}
+
+	/**
+	 * Test RefSpec to RemoteRefUpdate conversion with no-destination RefSpec
+	 * (destination should be set up for the same name as source).
+	 * 
+	 * @throws IOException
+	 */
+	public void testFindRemoteRefUpdatesNoWildcardNoDestination()
+			throws IOException {
+		transport = Transport.open(db, remoteConfig);
+		final Collection<RemoteRefUpdate> result = transport
+				.findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(
+						"+refs/heads/master")));
+
+		assertEquals(1, result.size());
+		final RemoteRefUpdate rru = result.iterator().next();
+		assertNull(rru.getExpectedOldObjectId());
+		assertTrue(rru.isForceUpdate());
+		assertEquals("refs/heads/master", rru.getSrcRef());
+		assertEquals(db.resolve("refs/heads/master"), rru.getNewObjectId());
+		assertEquals("refs/heads/master", rru.getRemoteName());
+	}
+
+	/**
+	 * Test RefSpec to RemoteRefUpdate conversion with wildcard RefSpec.
+	 * 
+	 * @throws IOException
+	 */
+	public void testFindRemoteRefUpdatesWildcardNoTracking() throws IOException {
+		transport = Transport.open(db, remoteConfig);
+		final Collection<RemoteRefUpdate> result = transport
+				.findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(
+						"+refs/heads/*:refs/heads/test/*")));
+
+		assertEquals(7, result.size());
+		boolean foundA = false;
+		boolean foundB = false;
+		for (final RemoteRefUpdate rru : result) {
+			if ("refs/heads/a".equals(rru.getSrcRef())
+					&& "refs/heads/test/a".equals(rru.getRemoteName()))
+				foundA = true;
+			if ("refs/heads/b".equals(rru.getSrcRef())
+					&& "refs/heads/test/b".equals(rru.getRemoteName()))
+				foundB = true;
+		}
+		assertTrue(foundA);
+		assertTrue(foundB);
+	}
+
+	/**
+	 * Test RefSpec to RemoteRefUpdate conversion for more than one RefSpecs
+	 * handling.
+	 * 
+	 * @throws IOException
+	 */
+	public void testFindRemoteRefUpdatesTwoRefSpecs() throws IOException {
+		transport = Transport.open(db, remoteConfig);
+		final RefSpec specA = new RefSpec("+refs/heads/a:refs/heads/b");
+		final RefSpec specC = new RefSpec("+refs/heads/c:refs/heads/d");
+		final Collection<RefSpec> specs = Arrays.asList(specA, specC);
+		final Collection<RemoteRefUpdate> result = transport
+				.findRemoteRefUpdatesFor(specs);
+
+		assertEquals(2, result.size());
+		boolean foundA = false;
+		boolean foundC = false;
+		for (final RemoteRefUpdate rru : result) {
+			if ("refs/heads/a".equals(rru.getSrcRef())
+					&& "refs/heads/b".equals(rru.getRemoteName()))
+				foundA = true;
+			if ("refs/heads/c".equals(rru.getSrcRef())
+					&& "refs/heads/d".equals(rru.getRemoteName()))
+				foundC = true;
+		}
+		assertTrue(foundA);
+		assertTrue(foundC);
+	}
+
+	/**
+	 * Test RefSpec to RemoteRefUpdate conversion for tracking ref search.
+	 * 
+	 * @throws IOException
+	 */
+	public void testFindRemoteRefUpdatesTrackingRef() throws IOException {
+		remoteConfig.addFetchRefSpec(new RefSpec(
+				"refs/heads/*:refs/remotes/test/*"));
+		transport = Transport.open(db, remoteConfig);
+		final Collection<RemoteRefUpdate> result = transport
+				.findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(
+						"+refs/heads/a:refs/heads/a")));
+
+		assertEquals(1, result.size());
+		final TrackingRefUpdate tru = result.iterator().next()
+				.getTrackingRefUpdate();
+		assertEquals("refs/remotes/test/a", tru.getLocalName());
+		assertEquals("refs/heads/a", tru.getRemoteName());
+		assertEquals(db.resolve("refs/heads/a"), tru.getNewObjectId());
+		assertNull(tru.getOldObjectId());
+	}
+}
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 19/23] Repository search for command line tools
  2008-06-27 22:06                                   ` [EGIT PATCH 18/23] Test cases for RefSpec to RemoteRefUpdate conversions Marek Zawirski
@ 2008-06-27 22:06                                     ` Marek Zawirski
  2008-06-27 22:06                                       ` [EGIT PATCH 20/23] Push command line utility Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Introducing some simple search for git repository in parent directories
of current directory.

Previous version was annoying: we had to be in directory that contains
.git/ or specify it explicitly by --git-dir.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../src/org/spearce/jgit/pgm/Main.java             |   24 +++++++++++++++++--
 1 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/Main.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/Main.java
index 44f8a42..8afd0d7 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/pgm/Main.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/Main.java
@@ -85,12 +85,12 @@ public class Main {
 
 	private static void execute(final String[] argv) throws Exception {
 		int argi = 0;
-		String gitdir = ".git";
 
+		File gitdir = null;
 		for (; argi < argv.length; argi++) {
 			final String arg = argv[argi];
 			if (arg.startsWith("--git-dir="))
-				gitdir = arg.substring("--git-dir=".length());
+				gitdir = new File(arg.substring("--git-dir=".length()));
 			else if (arg.equals("--show-stack-trace"))
 				showStackTrace = true;
 			else if (arg.startsWith("--"))
@@ -101,8 +101,15 @@ public class Main {
 
 		if (argi == argv.length)
 			usage();
+		if (gitdir == null)
+			gitdir = findGitDir();
+		if (gitdir == null || !gitdir.isDirectory()) {
+			System.err.println("error: can't find git directory");
+			System.exit(1);
+		}
+
 		final TextBuiltin cmd = createCommand(argv[argi++]);
-		cmd.db = new Repository(new File(gitdir));
+		cmd.db = new Repository(gitdir);
 		try {
 			cmd.execute(subarray(argv, argi));
 		} finally {
@@ -111,6 +118,17 @@ public class Main {
 		}
 	}
 
+	private static File findGitDir() {
+		File current = new File(".").getAbsoluteFile();
+		while (current != null) {
+			final File gitDir = new File(current, ".git");
+			if (gitDir.isDirectory())
+				return gitDir;
+			current = current.getParentFile();
+		}
+		return null;
+	}
+
 	private static String[] subarray(final String[] argv, final int i) {
 		return Arrays.asList(argv).subList(i, argv.length).toArray(
 				new String[0]);
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 20/23] Push command line utility
  2008-06-27 22:06                                     ` [EGIT PATCH 19/23] Repository search for command line tools Marek Zawirski
@ 2008-06-27 22:06                                       ` Marek Zawirski
  2008-06-27 22:06                                         ` [EGIT PATCH 21/23] Don't accept RefSpec with null source for fetch Marek Zawirski
  2008-06-28 12:36                                         ` [EGIT PATCH 20/23] Push command line utility Robin Rosenberg
  0 siblings, 2 replies; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

pgm.Push class providing command line push utility, similar to C Git
one.

Some shared abbreviating methods for both Fetch and Push are moved to
TextBuiltin.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../src/org/spearce/jgit/pgm/Fetch.java            |   36 +---
 .../src/org/spearce/jgit/pgm/Push.java             |  235 ++++++++++++++++++++
 .../src/org/spearce/jgit/pgm/TextBuiltin.java      |   21 ++
 3 files changed, 262 insertions(+), 30 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java
index 3a81575..c9c997e 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java
@@ -40,8 +40,6 @@ package org.spearce.jgit.pgm;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.spearce.jgit.lib.Constants;
-import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.RefUpdate;
 import org.spearce.jgit.lib.TextProgressMonitor;
 import org.spearce.jgit.transport.FetchResult;
@@ -50,12 +48,6 @@ import org.spearce.jgit.transport.TrackingRefUpdate;
 import org.spearce.jgit.transport.Transport;
 
 class Fetch extends TextBuiltin {
-	private static final String REFS_HEADS = Constants.HEADS_PREFIX + "/";
-
-	private static final String REFS_REMOTES = Constants.REMOTES_PREFIX + "/";
-
-	private static final String REFS_TAGS = Constants.TAGS_PREFIX + "/";
-
 	@Override
 	void execute(String[] args) throws Exception {
 		int argi = 0;
@@ -84,20 +76,8 @@ class Fetch extends TextBuiltin {
 		for (final TrackingRefUpdate u : r.getTrackingRefUpdates()) {
 			final char type = shortTypeOf(u.getResult());
 			final String longType = longTypeOf(u);
-
-			String src = u.getRemoteName();
-			if (src.startsWith(REFS_HEADS))
-				src = src.substring(REFS_HEADS.length());
-			else if (src.startsWith(REFS_TAGS))
-				src = src.substring(REFS_TAGS.length());
-
-			String dst = u.getLocalName();
-			if (dst.startsWith(REFS_HEADS))
-				dst = dst.substring(REFS_HEADS.length());
-			else if (dst.startsWith(REFS_TAGS))
-				dst = dst.substring(REFS_TAGS.length());
-			else if (dst.startsWith(REFS_REMOTES))
-				dst = dst.substring(REFS_REMOTES.length());
+			final String src = abbreviateRef(u.getRemoteName(), false);
+			final String dst = abbreviateRef(u.getLocalName(), true);
 
 			out.format(" %c %-17s %-10s -> %s", type, longType, src, dst);
 			out.println();
@@ -121,14 +101,14 @@ class Fetch extends TextBuiltin {
 		}
 
 		if (r == RefUpdate.Result.FORCED) {
-			final String aOld = abbreviate(u.getOldObjectId());
-			final String aNew = abbreviate(u.getNewObjectId());
+			final String aOld = abbreviateObject(u.getOldObjectId());
+			final String aNew = abbreviateObject(u.getNewObjectId());
 			return aOld + "..." + aNew;
 		}
 
 		if (r == RefUpdate.Result.FAST_FORWARD) {
-			final String aOld = abbreviate(u.getOldObjectId());
-			final String aNew = abbreviate(u.getNewObjectId());
+			final String aOld = abbreviateObject(u.getOldObjectId());
+			final String aNew = abbreviateObject(u.getNewObjectId());
 			return aOld + ".." + aNew;
 		}
 
@@ -139,10 +119,6 @@ class Fetch extends TextBuiltin {
 		return "[" + r.name() + "]";
 	}
 
-	private static String abbreviate(final ObjectId id) {
-		return id.toString().substring(0, 7);
-	}
-
 	private static char shortTypeOf(final RefUpdate.Result r) {
 		if (r == RefUpdate.Result.LOCK_FAILURE)
 			return '!';
diff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java
new file mode 100644
index 0000000..4130bc9
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * 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 Git Development Community 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.spearce.jgit.pgm;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.lib.TextProgressMonitor;
+import org.spearce.jgit.transport.PushResult;
+import org.spearce.jgit.transport.RefSpec;
+import org.spearce.jgit.transport.RemoteRefUpdate;
+import org.spearce.jgit.transport.Transport;
+import org.spearce.jgit.transport.RemoteRefUpdate.Status;
+
+class Push extends TextBuiltin {
+
+	private boolean verbose = false;
+
+	private Transport transport;
+
+	private boolean first = true;
+
+	@Override
+	void execute(String[] args) throws Exception {
+		final LinkedList<RefSpec> refSpecs = new LinkedList<RefSpec>();
+		Boolean thin = null;
+		String exec = null;
+		boolean forceAll = false;
+
+		int argi = 0;
+		for (; argi < args.length; argi++) {
+			final String a = args[argi];
+			if ("--thin".equals(a))
+				thin = true;
+			else if ("--no-thin".equals(a))
+				thin = false;
+			else if ("-f".equals(a) || "--force".equals(a))
+				forceAll = true;
+			else if (a.startsWith("--exec="))
+				exec = a.substring("--exec=".length());
+			else if (a.startsWith("--receive-pack="))
+				exec = a.substring("--receive-pack=".length());
+			else if ("--tags".equals(a))
+				refSpecs.add(Transport.REFSPEC_TAGS);
+			else if ("--all".equals(a))
+				refSpecs.add(Transport.REFSPEC_PUSH_ALL);
+			else if ("-v".equals(a))
+				verbose = true;
+			else if ("--".equals(a)) {
+				argi++;
+				break;
+			} else if (a.startsWith("-"))
+				die("usage: push [--all] [--tags] [--force] [--thin]\n"
+						+ "[--receive-pack=<git-receive-pack>] [<repository> [<refspec>]...]");
+			else
+				break;
+		}
+
+		final String repository;
+		if (argi == args.length)
+			repository = "origin";
+		else
+			repository = args[argi++];
+		transport = Transport.open(db, repository);
+		if (thin != null)
+			transport.setPushThin(thin);
+		if (exec != null)
+			transport.setOptionReceivePack(exec);
+
+		for (; argi < args.length; argi++) {
+			final RefSpec spec = new RefSpec(args[argi]);
+			if (forceAll)
+				spec.setForceUpdate(true);
+			refSpecs.add(spec);
+		}
+		final Collection<RemoteRefUpdate> toPush = transport
+				.findRemoteRefUpdatesFor(refSpecs);
+
+		final PushResult result = transport.push(new TextProgressMonitor(),
+				toPush);
+		printPushResult(result);
+	}
+
+	private void printPushResult(final PushResult result) {
+		boolean everythingUpToDate = true;
+		// at first, print up-to-date ones...
+		for (final RemoteRefUpdate rru : result.getRemoteUpdates()) {
+			if (rru.getStatus() == Status.UP_TO_DATE) {
+				if (verbose)
+					printRefUpdateResult(result, rru);
+			} else
+				everythingUpToDate = false;
+		}
+
+		for (final RemoteRefUpdate rru : result.getRemoteUpdates()) {
+			// ...then successful updates...
+			if (rru.getStatus() == Status.OK)
+				printRefUpdateResult(result, rru);
+		}
+
+		for (final RemoteRefUpdate rru : result.getRemoteUpdates()) {
+			// ...finally, others (problematic)
+			if (rru.getStatus() != Status.OK
+					&& rru.getStatus() != Status.UP_TO_DATE)
+				printRefUpdateResult(result, rru);
+		}
+
+		if (everythingUpToDate)
+			out.println("Everything up-to-date");
+	}
+
+	private void printRefUpdateResult(final PushResult result,
+			final RemoteRefUpdate rru) {
+		if (first) {
+			first = false;
+			out.format("To %s\n", transport.getURI());
+		}
+
+		final String remoteName = rru.getRemoteName();
+		final String srcRef = rru.isDelete() ? null : rru.getSrcRef();
+
+		switch (rru.getStatus()) {
+		case OK:
+			if (rru.isDelete())
+				printUpdateLine('-', "[deleted]", null, remoteName, null);
+			else {
+				final Ref oldRef = result.getAdvertisedRef(remoteName);
+				if (oldRef == null) {
+					final String summary;
+					if (remoteName.startsWith(REFS_TAGS))
+						summary = "[new tag]";
+					else
+						summary = "[new branch]";
+					printUpdateLine('*', summary, srcRef, remoteName, null);
+				} else {
+					boolean fastForward = rru.isFastForward();
+					final char flag = fastForward ? ' ' : '+';
+					final String summary = abbreviateObject(oldRef
+							.getObjectId())
+							+ (fastForward ? ".." : "...")
+							+ abbreviateObject(rru.getNewObjectId());
+					final String message = fastForward ? null : "forced update";
+					printUpdateLine(flag, summary, srcRef, remoteName, message);
+				}
+			}
+			break;
+
+		case NON_EXISTING:
+			printUpdateLine('X', "[no match]", null, remoteName, null);
+			break;
+
+		case REJECTED_NODELETE:
+			printUpdateLine('!', "[rejected]", null, remoteName,
+					"remote side does not support deleting refs");
+			break;
+
+		case REJECTED_NONFASTFORWARD:
+			printUpdateLine('!', "[rejected]", srcRef, remoteName,
+					"non-fast forward");
+			break;
+
+		case REJECTED_REMOTE_CHANGED:
+			final String message = "remote ref object changed - is not expected one "
+					+ abbreviateObject(rru.getExpectedOldObjectId());
+			printUpdateLine('!', "[rejected]", srcRef, remoteName, message);
+			break;
+
+		case REJECTED_OTHER_REASON:
+			printUpdateLine('!', "[remote rejected]", srcRef, remoteName, rru
+					.getMessage());
+			break;
+
+		case UP_TO_DATE:
+			if (verbose)
+				printUpdateLine('=', "[up to date]", srcRef, remoteName, null);
+			break;
+
+		case NOT_ATTEMPTED:
+		case AWAITING_REPORT:
+			printUpdateLine('?', "[unexpected push-process behavior]", srcRef,
+					remoteName, rru.getMessage());
+			break;
+		}
+	}
+
+	private void printUpdateLine(final char flag, final String summary,
+			final String srcRef, final String destRef, final String message) {
+		out.format(" %c %-17s", flag, summary);
+
+		if (srcRef != null)
+			out.format(" %s ->", abbreviateRef(srcRef, true));
+		out.format(" %s", abbreviateRef(destRef, true));
+
+		if (message != null)
+			out.format(" (%s)", message);
+
+		out.println();
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/TextBuiltin.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/TextBuiltin.java
index 163f795..b3d8f39 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/pgm/TextBuiltin.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/TextBuiltin.java
@@ -43,10 +43,17 @@ import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 
+import org.spearce.jgit.lib.Constants;
 import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.Repository;
 
 abstract class TextBuiltin {
+	protected static final String REFS_HEADS = Constants.HEADS_PREFIX + "/";
+
+	protected static final String REFS_REMOTES = Constants.REMOTES_PREFIX + "/";
+
+	protected static final String REFS_TAGS = Constants.TAGS_PREFIX + "/";
+
 	protected PrintWriter out;
 
 	protected Repository db;
@@ -72,4 +79,18 @@ abstract class TextBuiltin {
 	protected static Die die(final String why) {
 		return new Die(why);
 	}
+
+	protected static String abbreviateObject(final ObjectId id) {
+		return id.toString().substring(0, 7);
+	}
+
+	protected String abbreviateRef(String dst, boolean abbreviateRemote) {
+		if (dst.startsWith(REFS_HEADS))
+			dst = dst.substring(REFS_HEADS.length());
+		else if (dst.startsWith(REFS_TAGS))
+			dst = dst.substring(REFS_TAGS.length());
+		else if (abbreviateRemote && dst.startsWith(REFS_REMOTES))
+			dst = dst.substring(REFS_REMOTES.length());
+		return dst;
+	}
 }
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 21/23] Don't accept RefSpec with null source for fetch
  2008-06-27 22:06                                       ` [EGIT PATCH 20/23] Push command line utility Marek Zawirski
@ 2008-06-27 22:06                                         ` Marek Zawirski
  2008-06-27 22:06                                           ` [EGIT PATCH 22/23] Add new handy constructors to TransportException, PackProtocolException Marek Zawirski
  2008-06-28 12:36                                         ` [EGIT PATCH 20/23] Push command line utility Robin Rosenberg
  1 sibling, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

RefSpec with null source has no sense, so let's inform about it.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../org/spearce/jgit/transport/FetchProcess.java   |    4 ++++
 .../src/org/spearce/jgit/transport/Transport.java  |    5 +++--
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
index c765c12..f9c2266 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
@@ -100,6 +100,10 @@ class FetchProcess {
 			result.setAdvertisedRefs(conn.getRefsMap());
 			final Set<Ref> matched = new HashSet<Ref>();
 			for (final RefSpec spec : toFetch) {
+				if (spec.getSource() == null)
+					throw new TransportException(
+							"Source ref not specified for refspec: " + spec);
+
 				if (spec.isWildcard())
 					expandWildcard(spec, matched);
 				else
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
index da5b41e..8260da6 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
@@ -363,14 +363,15 @@ public abstract class Transport {
 	 * @param toFetch
 	 *            specification of refs to fetch locally. May be null or the
 	 *            empty collection to use the specifications from the
-	 *            RemoteConfig.
+	 *            RemoteConfig. Source for each RefSpec can't be null.
 	 * @return information describing the tracking refs updated.
 	 * @throws NotSupportedException
 	 *             this transport implementation does not support fetching
 	 *             objects.
 	 * @throws TransportException
 	 *             the remote connection could not be established or object
-	 *             copying (if necessary) failed.
+	 *             copying (if necessary) failed or update specification was
+	 *             incorrect.
 	 */
 	public FetchResult fetch(final ProgressMonitor monitor,
 			Collection<RefSpec> toFetch) throws NotSupportedException,
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 22/23] Add new handy constructors to TransportException, PackProtocolException
  2008-06-27 22:06                                         ` [EGIT PATCH 21/23] Don't accept RefSpec with null source for fetch Marek Zawirski
@ 2008-06-27 22:06                                           ` Marek Zawirski
  2008-06-27 22:06                                             ` [EGIT PATCH 23/23] Use new TransportException constructors Marek Zawirski
  0 siblings, 1 reply; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Constructor takes additionally URI argument, to add prefix: uri + ": "
before real message. Now we don't have to remember about ugly (uri + ":
") prefix in each thrown message;

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../spearce/jgit/errors/PackProtocolException.java |   30 +++++++++++++++++++
 .../spearce/jgit/errors/TransportException.java    |   31 ++++++++++++++++++++
 2 files changed, 61 insertions(+), 0 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/PackProtocolException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/PackProtocolException.java
index 525496a..7b5b4f6 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/errors/PackProtocolException.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/errors/PackProtocolException.java
@@ -38,6 +38,7 @@
 
 package org.spearce.jgit.errors;
 
+import org.spearce.jgit.transport.URIish;
 
 /**
  * Indicates a protocol error has occurred while fetching/pushing objects.
@@ -46,6 +47,35 @@ public class PackProtocolException extends TransportException {
 	private static final long serialVersionUID = 1L;
 
 	/**
+	 * Constructs an PackProtocolException with the specified detail message
+	 * prefixed with provided URI.
+	 * 
+	 * @param uri
+	 *            URI used for transport
+	 * @param s
+	 *            message
+	 */
+	public PackProtocolException(final URIish uri, final String s) {
+		super(uri + ": " + s);
+	}
+
+	/**
+	 * Constructs an PackProtocolException with the specified detail message
+	 * prefixed with provided URI.
+	 * 
+	 * @param uri
+	 *            URI used for transport
+	 * @param s
+	 *            message
+	 * @param cause
+	 *            root cause exception
+	 */
+	public PackProtocolException(final URIish uri, final String s,
+			final Throwable cause) {
+		this(uri + ": " + s, cause);
+	}
+
+	/**
 	 * Constructs an PackProtocolException with the specified detail message.
 	 * 
 	 * @param s
diff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/TransportException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/TransportException.java
index 7b378db..13c7a28 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/errors/TransportException.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/errors/TransportException.java
@@ -40,6 +40,8 @@ package org.spearce.jgit.errors;
 
 import java.io.IOException;
 
+import org.spearce.jgit.transport.URIish;
+
 /**
  * Indicates a protocol error has occurred while fetching/pushing objects.
  */
@@ -47,6 +49,35 @@ public class TransportException extends IOException {
 	private static final long serialVersionUID = 1L;
 
 	/**
+	 * Constructs an TransportException with the specified detail message
+	 * prefixed with provided URI.
+	 * 
+	 * @param uri
+	 *            URI used for transport
+	 * @param s
+	 *            message
+	 */
+	public TransportException(final URIish uri, final String s) {
+		super(uri + ": " + s);
+	}
+
+	/**
+	 * Constructs an TransportException with the specified detail message
+	 * prefixed with provided URI.
+	 * 
+	 * @param uri
+	 *            URI used for transport
+	 * @param s
+	 *            message
+	 * @param cause
+	 *            root cause exception
+	 */
+	public TransportException(final URIish uri, final String s,
+			final Throwable cause) {
+		this(uri + ": " + s, cause);
+	}
+
+	/**
 	 * Constructs an TransportException with the specified detail message.
 	 * 
 	 * @param s
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [EGIT PATCH 23/23] Use new TransportException constructors
  2008-06-27 22:06                                           ` [EGIT PATCH 22/23] Add new handy constructors to TransportException, PackProtocolException Marek Zawirski
@ 2008-06-27 22:06                                             ` Marek Zawirski
  0 siblings, 0 replies; 26+ messages in thread
From: Marek Zawirski @ 2008-06-27 22:06 UTC (permalink / raw
  To: robin.rosenberg, spearce; +Cc: git, Marek Zawirski

Modify existing TransportException constructor calls to new ones with
URI as argument.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../spearce/jgit/transport/BasePackConnection.java |    6 +++---
 .../jgit/transport/BasePackPushConnection.java     |   12 ++++++------
 .../org/spearce/jgit/transport/PushProcess.java    |    6 +++---
 .../spearce/jgit/transport/TransportGitAnon.java   |   15 +++++++--------
 .../spearce/jgit/transport/TransportGitSsh.java    |   18 ++++++++----------
 .../org/spearce/jgit/transport/TransportLocal.java |    3 +--
 .../org/spearce/jgit/transport/TransportSftp.java  |   12 +++++-------
 7 files changed, 33 insertions(+), 39 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
index d119672..9b39ebc 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
@@ -125,7 +125,7 @@ abstract class BasePackConnection extends BaseConnection {
 				line = pckIn.readString();
 			} catch (EOFException eof) {
 				if (avail.isEmpty())
-					throw new TransportException(uri + " not found.");
+					throw new TransportException(uri, "not found.");
 				throw eof;
 			}
 
@@ -155,7 +155,7 @@ abstract class BasePackConnection extends BaseConnection {
 				name = name.substring(0, name.length() - 3);
 				final Ref prior = avail.get(name);
 				if (prior == null)
-					throw new PackProtocolException(uri + ": advertisement of "
+					throw new PackProtocolException(uri, "advertisement of "
 							+ name + "^{} came before " + name);
 
 				if (prior.getPeeledObjectId() != null)
@@ -185,7 +185,7 @@ abstract class BasePackConnection extends BaseConnection {
 	}
 
 	private PackProtocolException duplicateAdvertisement(final String name) {
-		return new PackProtocolException(uri + ": duplicate advertisements of "
+		return new PackProtocolException(uri, "duplicate advertisements of "
 				+ name);
 	}
 
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
index 159e331..c56605a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
@@ -110,7 +110,7 @@ class BasePackPushConnection extends BasePackConnection implements
 		} catch (TransportException e) {
 			throw e;
 		} catch (Exception e) {
-			throw new TransportException(uri + ": " + e.getMessage(), e);
+			throw new TransportException(uri, e.getMessage(), e);
 		} finally {
 			close();
 		}
@@ -146,7 +146,7 @@ class BasePackPushConnection extends BasePackConnection implements
 		}
 
 		if (monitor.isCancelled())
-			throw new TransportException(uri + ": push cancelled");
+			throw new TransportException(uri, "push cancelled");
 		pckOut.end();
 	}
 
@@ -179,13 +179,13 @@ class BasePackPushConnection extends BasePackConnection implements
 			throws IOException {
 		final String unpackLine = pckIn.readString();
 		if (!unpackLine.startsWith("unpack "))
-			throw new PackProtocolException(uri + ": unexpected report line: "
+			throw new PackProtocolException(uri, "unexpected report line: "
 					+ unpackLine);
 		final String unpackStatus = unpackLine.substring("unpack ".length());
 		if (!unpackStatus.equals("ok"))
-			throw new TransportException(uri
-					+ ": error occurred during unpacking on the remote end: "
-					+ unpackStatus);
+			throw new TransportException(uri,
+					"error occurred during unpacking on the remote end: "
+							+ unpackStatus);
 
 		String refLine;
 		while ((refLine = pckIn.readString()).length() > 0) {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java
index f742949..1b5f9c6 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java
@@ -178,9 +178,9 @@ class PushProcess {
 			} catch (MissingObjectException x) {
 				fastForward = false;
 			} catch (Exception x) {
-				throw new TransportException(transport.getURI()
-						+ ": reading objects from local repository failed: "
-						+ x.getMessage(), x);
+				throw new TransportException(transport.getURI(),
+						"reading objects from local repository failed: "
+								+ x.getMessage(), x);
 			}
 			rru.setFastForward(fastForward);
 			if (!fastForward && !rru.isForceUpdate())
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
index 6e49083..8a78099 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
@@ -82,12 +82,11 @@ class TransportGitAnon extends PackTransport {
 		try {
 			return new Socket(InetAddress.getByName(uri.getHost()), port);
 		} catch (IOException c) {
-			final String us = uri.toString();
 			if (c instanceof UnknownHostException)
-				throw new TransportException(us + ": Unknown host");
+				throw new TransportException(uri, "unknown host");
 			if (c instanceof ConnectException)
-				throw new TransportException(us + ": " + c.getMessage());
-			throw new TransportException(us + ": " + c.getMessage(), c);
+				throw new TransportException(uri, c.getMessage());
+			throw new TransportException(uri, c.getMessage(), c);
 		}
 	}
 
@@ -116,8 +115,8 @@ class TransportGitAnon extends PackTransport {
 				service("git-upload-pack", pckOut);
 			} catch (IOException err) {
 				close();
-				throw new TransportException(uri.toString()
-						+ ": remote hung up unexpectedly", err);
+				throw new TransportException(uri,
+						"remote hung up unexpectedly", err);
 			}
 			readAdvertisedRefs();
 		}
@@ -149,8 +148,8 @@ class TransportGitAnon extends PackTransport {
 				service("git-receive-pack", pckOut);
 			} catch (IOException err) {
 				close();
-				throw new TransportException(uri.toString()
-						+ ": remote hung up unexpectedly", err);
+				throw new TransportException(uri,
+						"remote hung up unexpectedly", err);
 			}
 			readAdvertisedRefs();
 		}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java
index 55be4f6..46c60c3 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java
@@ -162,13 +162,12 @@ class TransportGitSsh extends PackTransport {
 				session.connect();
 			return session;
 		} catch (JSchException je) {
-			final String us = uri.toString();
 			final Throwable c = je.getCause();
 			if (c instanceof UnknownHostException)
-				throw new TransportException(us + ": Unknown host");
+				throw new TransportException(uri, "unknown host");
 			if (c instanceof ConnectException)
-				throw new TransportException(us + ": " + c.getMessage());
-			throw new TransportException(us + ": " + je.getMessage(), je);
+				throw new TransportException(uri, c.getMessage());
+			throw new TransportException(uri, je.getMessage(), je);
 		}
 	}
 
@@ -189,8 +188,7 @@ class TransportGitSsh extends PackTransport {
 			channel.connect();
 			return channel;
 		} catch (JSchException je) {
-			throw new TransportException(uri.toString() + ": "
-					+ je.getMessage(), je);
+			throw new TransportException(uri, je.getMessage(), je);
 		}
 	}
 
@@ -210,8 +208,8 @@ class TransportGitSsh extends PackTransport {
 				throw err;
 			} catch (IOException err) {
 				close();
-				throw new TransportException(uri.toString()
-						+ ": remote hung up unexpectedly", err);
+				throw new TransportException(uri,
+						"remote hung up unexpectedly", err);
 			}
 			readAdvertisedRefs();
 		}
@@ -255,8 +253,8 @@ class TransportGitSsh extends PackTransport {
 				throw err;
 			} catch (IOException err) {
 				close();
-				throw new TransportException(uri.toString()
-						+ ": remote hung up unexpectedly", err);
+				throw new TransportException(uri,
+						"remote hung up unexpectedly", err);
 			}
 			readAdvertisedRefs();
 		}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
index f48dc6d..761d1b8 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
@@ -99,8 +99,7 @@ class TransportLocal extends PackTransport {
 			new StreamRewritingThread(proc.getErrorStream()).start();
 			return proc;
 		} catch (IOException err) {
-			throw new TransportException(uri.toString() + ": "
-					+ err.getMessage(), err);
+			throw new TransportException(uri, err.getMessage(), err);
 		}
 	}
 
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java
index cfe22c1..21657ef 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java
@@ -111,13 +111,12 @@ class TransportSftp extends WalkTransport {
 				session.connect();
 			return session;
 		} catch (JSchException je) {
-			final String us = uri.toString();
 			final Throwable c = je.getCause();
 			if (c instanceof UnknownHostException)
-				throw new TransportException(us + ": Unknown host");
+				throw new TransportException(uri, "unknown host");
 			if (c instanceof ConnectException)
-				throw new TransportException(us + ": " + c.getMessage());
-			throw new TransportException(us + ": " + je.getMessage(), je);
+				throw new TransportException(uri, c.getMessage());
+			throw new TransportException(uri, je.getMessage(), je);
 		}
 	}
 
@@ -127,8 +126,7 @@ class TransportSftp extends WalkTransport {
 			channel.connect();
 			return (ChannelSftp) channel;
 		} catch (JSchException je) {
-			throw new TransportException(uri.toString() + ": "
-					+ je.getMessage(), je);
+			throw new TransportException(uri, je.getMessage(), je);
 		}
 	}
 
@@ -259,7 +257,7 @@ class TransportSftp extends WalkTransport {
 			} catch (FileNotFoundException notPacked) {
 				// Perhaps it wasn't worthwhile, or is just an older repository.
 			} catch (IOException e) {
-				throw new TransportException(uri + ": error in packed-refs", e);
+				throw new TransportException(uri, "error in packed-refs", e);
 			}
 			readRef(avail, "../HEAD", "HEAD");
 			readLooseRefs(avail, "../refs", "refs/");
-- 
1.5.5.3

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [EGIT PATCH 00/23] Push implementation
  2008-06-27 22:06 [EGIT PATCH 00/23] Push implementation Marek Zawirski
  2008-06-27 22:06 ` [EGIT PATCH 01/23] Fix: let FetchProcess use fetch() instead of doFetch() Marek Zawirski
@ 2008-06-27 23:25 ` Robin Rosenberg
  1 sibling, 0 replies; 26+ messages in thread
From: Robin Rosenberg @ 2008-06-27 23:25 UTC (permalink / raw
  To: Marek Zawirski; +Cc: spearce, git

lördagen den 28 juni 2008 00.06.24 skrev Marek Zawirski:
> Finally, series with push implementation. It has taken a "while" to
> squash all bugs this time and polish the series, uhh.

This will make fine weekend reading :)

-- robin

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [EGIT PATCH 20/23] Push command line utility
  2008-06-27 22:06                                       ` [EGIT PATCH 20/23] Push command line utility Marek Zawirski
  2008-06-27 22:06                                         ` [EGIT PATCH 21/23] Don't accept RefSpec with null source for fetch Marek Zawirski
@ 2008-06-28 12:36                                         ` Robin Rosenberg
  1 sibling, 0 replies; 26+ messages in thread
From: Robin Rosenberg @ 2008-06-28 12:36 UTC (permalink / raw
  To: Marek Zawirski; +Cc: spearce, git

Marek, 

RefSpecs are unmutable classes. I Can squeeze this into the patchset.  

Other than that, impressive!

-- robin

diff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java
index 4130bc9..cbdf465 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java
@@ -104,9 +104,9 @@ class Push extends TextBuiltin {
                        transport.setOptionReceivePack(exec);

                for (; argi < args.length; argi++) {
-                       final RefSpec spec = new RefSpec(args[argi]);
+                       RefSpec spec = new RefSpec(args[argi]);
                        if (forceAll)
-                               spec.setForceUpdate(true);
+                               spec = spec.setForceUpdate(true);
                        refSpecs.add(spec);
                }
                final Collection<RemoteRefUpdate> toPush = transport

^ permalink raw reply related	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2008-06-28 12:41 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-27 22:06 [EGIT PATCH 00/23] Push implementation Marek Zawirski
2008-06-27 22:06 ` [EGIT PATCH 01/23] Fix: let FetchProcess use fetch() instead of doFetch() Marek Zawirski
2008-06-27 22:06   ` [EGIT PATCH 02/23] RefUpdate: new possible result Result.IO_FAILURE Marek Zawirski
2008-06-27 22:06     ` [EGIT PATCH 03/23] Refactor TrackingRefUpdate to not hold RefSpec Marek Zawirski
2008-06-27 22:06       ` [EGIT PATCH 04/23] New constructor without RefSpec for TrackingRefUpdate Marek Zawirski
2008-06-27 22:06         ` [EGIT PATCH 05/23] Add RemoteRefUpdate class Marek Zawirski
2008-06-27 22:06           ` [EGIT PATCH 06/23] Refactor: extract superclass OperationResult from FetchResult Marek Zawirski
2008-06-27 22:06             ` [EGIT PATCH 07/23] Add PushResult class Marek Zawirski
2008-06-27 22:06               ` [EGIT PATCH 08/23] Support for fetchThin and pushThin options in Transport Marek Zawirski
2008-06-27 22:06                 ` [EGIT PATCH 09/23] Big refactor: *Connection hierarchy Marek Zawirski
2008-06-27 22:06                   ` [EGIT PATCH 10/23] Add ignoreMissingUninteresting option to PackWriter Marek Zawirski
2008-06-27 22:06                     ` [EGIT PATCH 11/23] Add BasePackPushConnection implementing git-send-pack protocol Marek Zawirski
2008-06-27 22:06                       ` [EGIT PATCH 12/23] Fix: let RevWalk reset correctly before isMergedInto() Marek Zawirski
2008-06-27 22:06                         ` [EGIT PATCH 13/23] Add PushProcess class implementing git-send-pack logic Marek Zawirski
2008-06-27 22:06                           ` [EGIT PATCH 14/23] Clarify Repository#resolve() documentation Marek Zawirski
2008-06-27 22:06                             ` [EGIT PATCH 15/23] Add String versions of methods in RefSpec Marek Zawirski
2008-06-27 22:06                               ` [EGIT PATCH 16/23] Transport* - general support for push() and implementations Marek Zawirski
2008-06-27 22:06                                 ` [EGIT PATCH 17/23] Test cases for PushProcess Marek Zawirski
2008-06-27 22:06                                   ` [EGIT PATCH 18/23] Test cases for RefSpec to RemoteRefUpdate conversions Marek Zawirski
2008-06-27 22:06                                     ` [EGIT PATCH 19/23] Repository search for command line tools Marek Zawirski
2008-06-27 22:06                                       ` [EGIT PATCH 20/23] Push command line utility Marek Zawirski
2008-06-27 22:06                                         ` [EGIT PATCH 21/23] Don't accept RefSpec with null source for fetch Marek Zawirski
2008-06-27 22:06                                           ` [EGIT PATCH 22/23] Add new handy constructors to TransportException, PackProtocolException Marek Zawirski
2008-06-27 22:06                                             ` [EGIT PATCH 23/23] Use new TransportException constructors Marek Zawirski
2008-06-28 12:36                                         ` [EGIT PATCH 20/23] Push command line utility Robin Rosenberg
2008-06-27 23:25 ` [EGIT PATCH 00/23] Push implementation Robin Rosenberg

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).