git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [JGIT PATCH 0/4] Misc. push transport fixes
@ 2008-07-09  4:15 Shawn O. Pearce
  2008-07-09  4:15 ` [JGIT PATCH 1/4] Avoid deadlock while fetching from local repository Shawn O. Pearce
  0 siblings, 1 reply; 5+ messages in thread
From: Shawn O. Pearce @ 2008-07-09  4:15 UTC (permalink / raw)
  To: Robin Rosenberg, Marek Zawirski; +Cc: git

Few minor bugs related to our push implementation.  A deadlock
is fixed in the local fetch case and pack generation to include
annotated tags is also fixed.

We also now do essentially "git init" on the remote repository
if we are creating it over a dumb transport during the first push
request made to it.


Shawn O. Pearce (4):
  Avoid deadlock while fetching from local repository
  Fix pushing of annotated tags to actually include the tag object
  Automatically initialize a new dumb repository during push
  Use a singleton for the NullProgressMonitor implementation

 .../org/spearce/jgit/lib/NullProgressMonitor.java  |    3 +
 .../src/org/spearce/jgit/revwalk/ObjectWalk.java   |   13 ++++-
 .../spearce/jgit/transport/BasePackConnection.java |    7 ++-
 .../jgit/transport/BasePackFetchConnection.java    |    1 +
 .../jgit/transport/BasePackPushConnection.java     |    1 +
 .../spearce/jgit/transport/WalkPushConnection.java |   54 ++++++++++++++++++++
 .../src/org/spearce/jgit/util/TemporaryBuffer.java |    2 +-
 7 files changed, 77 insertions(+), 4 deletions(-)

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

* [JGIT PATCH 1/4] Avoid deadlock while fetching from local repository
  2008-07-09  4:15 [JGIT PATCH 0/4] Misc. push transport fixes Shawn O. Pearce
@ 2008-07-09  4:15 ` Shawn O. Pearce
  2008-07-09  4:15   ` [JGIT PATCH 2/4] Fix pushing of annotated tags to actually include the tag object Shawn O. Pearce
  0 siblings, 1 reply; 5+ messages in thread
From: Shawn O. Pearce @ 2008-07-09  4:15 UTC (permalink / raw)
  To: Robin Rosenberg, Marek Zawirski; +Cc: git

We cannot send a packet line end command to git-upload-pack after we
have sent our want list and obtained a pack back from it.  Once the
want list has ended git-upload-pack wants no further data sent to
it, and attempting to write more may cause us to deadlock as the
pipe won't accept the data.

Not sending the packet line end if we don't send a want list is a
(minor) protocol error.  To avoid these protocol errors (which
may display on stderr from git-upload-pack) we still send the end
during close if we have not yet sent a want list.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../spearce/jgit/transport/BasePackConnection.java |    7 ++++++-
 .../jgit/transport/BasePackFetchConnection.java    |    1 +
 .../jgit/transport/BasePackPushConnection.java     |    1 +
 3 files changed, 8 insertions(+), 1 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 a878f01..7dc4620 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
@@ -83,6 +83,9 @@ abstract class BasePackConnection extends BaseConnection {
 	/** Packet line encoder around {@link #out}. */
 	protected PacketLineOut pckOut;
 
+	/** Send {@link PacketLineOut#end()} before closing {@link #out}? */
+	protected boolean outNeedsEnd;
+
 	/** Capability tokens advertised by the remote side. */
 	private final Set<String> remoteCapablities = new HashSet<String>();
 
@@ -99,6 +102,7 @@ abstract class BasePackConnection extends BaseConnection {
 
 		pckIn = new PacketLineIn(in);
 		pckOut = new PacketLineOut(out);
+		outNeedsEnd = true;
 	}
 
 	protected void readAdvertisedRefs() throws TransportException {
@@ -195,7 +199,8 @@ abstract class BasePackConnection extends BaseConnection {
 	public void close() {
 		if (out != null) {
 			try {
-				pckOut.end();
+				if (outNeedsEnd)
+					pckOut.end();
 				out.close();
 			} catch (IOException err) {
 				// Ignore any close errors.
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
index 04a91bf..12b36f2 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
@@ -253,6 +253,7 @@ abstract class BasePackFetchConnection extends BasePackConnection implements
 			pckOut.writeString(line.toString());
 		}
 		pckOut.end();
+		outNeedsEnd = false;
 		return !first;
 	}
 
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 784a578..6d95eaf 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
@@ -148,6 +148,7 @@ class BasePackPushConnection extends BasePackConnection implements
 		if (monitor.isCancelled())
 			throw new TransportException(uri, "push cancelled");
 		pckOut.end();
+		outNeedsEnd = false;
 	}
 
 	private String enableCapabilties() {
-- 
1.5.6.74.g8a5e

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

* [JGIT PATCH 2/4] Fix pushing of annotated tags to actually include the tag object
  2008-07-09  4:15 ` [JGIT PATCH 1/4] Avoid deadlock while fetching from local repository Shawn O. Pearce
@ 2008-07-09  4:15   ` Shawn O. Pearce
  2008-07-09  4:15     ` [JGIT PATCH 3/4] Automatically initialize a new dumb repository during push Shawn O. Pearce
  0 siblings, 1 reply; 5+ messages in thread
From: Shawn O. Pearce @ 2008-07-09  4:15 UTC (permalink / raw)
  To: Robin Rosenberg, Marek Zawirski; +Cc: git

When we pushed an annotated tag to a remote repository jgit was
failing to include the annotated tag object itself as ObjectWalk
nextObject() failed to pop the object out of the internal pending
queue and return it for inclusion in the generated pack file.

We need to use two different flags for SEEN and IN_PENDING as a blob
or tree object can be SEEN during a TreeWalk, yet may also have been
inserted into the pending object list.  SEEN means we have output
the object to the caller, IN_PENDING means we have placed it into
the pending object list.  Together these prevent duplicates in the
queue and duplicate return values.

Noticed by Robin while trying to push an annotated tag:

  $ git tag -m foo X
  $ git rev-parse X
  49aa0e93621...

  $ jgit push somerepo refs/tags/X
  error: unpack should have generated 49aa0e93621...
  To somerepo.git
   ! [remote rejected] X -> X (bad pack)

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/revwalk/ObjectWalk.java   |   13 +++++++++++--
 1 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/revwalk/ObjectWalk.java b/org.spearce.jgit/src/org/spearce/jgit/revwalk/ObjectWalk.java
index 6a5b857..1ba21eb 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/revwalk/ObjectWalk.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/revwalk/ObjectWalk.java
@@ -66,6 +66,15 @@ import org.spearce.jgit.treewalk.TreeWalk;
  * commits that are returned first.
  */
 public class ObjectWalk extends RevWalk {
+	/**
+	 * Indicates a non-RevCommit is in {@link #pendingObjects}.
+	 * <p>
+	 * We can safely reuse {@link RevWalk#REWRITE} here for the same value as it
+	 * is only set on RevCommit and {@link #pendingObjects} never has RevCommit
+	 * instances inserted into it.
+	 */
+	private static final int IN_PENDING = RevWalk.REWRITE;
+
 	private final TreeWalk treeWalk;
 
 	private BlockObjQueue pendingObjects;
@@ -361,8 +370,8 @@ public class ObjectWalk extends RevWalk {
 	}
 
 	private void addObject(final RevObject o) {
-		if ((o.flags & SEEN) == 0) {
-			o.flags |= SEEN;
+		if ((o.flags & IN_PENDING) == 0) {
+			o.flags |= IN_PENDING;
 			pendingObjects.add(o);
 		}
 	}
-- 
1.5.6.74.g8a5e

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

* [JGIT PATCH 3/4] Automatically initialize a new dumb repository during push
  2008-07-09  4:15   ` [JGIT PATCH 2/4] Fix pushing of annotated tags to actually include the tag object Shawn O. Pearce
@ 2008-07-09  4:15     ` Shawn O. Pearce
  2008-07-09  4:15       ` [JGIT PATCH 4/4] Use a singleton for the NullProgressMonitor implementation Shawn O. Pearce
  0 siblings, 1 reply; 5+ messages in thread
From: Shawn O. Pearce @ 2008-07-09  4:15 UTC (permalink / raw)
  To: Robin Rosenberg, Marek Zawirski; +Cc: git

If we are pushing into a repository which has no refs and no packs
we can reasonably assume the repository is brand new.  In such
cases we must ensure there is a valid HEAD and config file in the
repository, otherwise Git clients reading the repository may not
be able to recognize its contents correctly.

By default we try to create HEAD from refs/heads/master, if that
is among the branches being pushed by the user.  This mirrors what
git-init would have initialized HEAD to be.  However if master was
not among the branches pushed then another branch is selected as a
reasonable default.

We initialize .git/config to a very bare configuration, listing
only the repository version.  We avoid setting up core.filemode as
we cannot genrally detect if the remote side understands executable
mode bits.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../spearce/jgit/transport/WalkPushConnection.java |   54 ++++++++++++++++++++
 1 files changed, 54 insertions(+), 0 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java
index e11b85a..bb5a653 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java
@@ -48,6 +48,7 @@ import java.util.TreeMap;
 
 import org.spearce.jgit.errors.TransportException;
 import org.spearce.jgit.lib.AnyObjectId;
+import org.spearce.jgit.lib.Constants;
 import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.PackWriter;
 import org.spearce.jgit.lib.ProgressMonitor;
@@ -151,6 +152,12 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
 		for (final RemoteRefUpdate u : updates)
 			updateCommand(u);
 
+		// Is this a new repository? If so we should create additional
+		// metadata files so it is properly initialized during the push.
+		//
+		if (!updates.isEmpty() && isNewRepository())
+			createNewRepository(updates);
+
 		if (!packedRefUpdates.isEmpty()) {
 			try {
 				dest.writePackedRefs(newRefs.values());
@@ -307,4 +314,51 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
 			u.setMessage(e.getMessage());
 		}
 	}
+
+	private boolean isNewRepository() {
+		return getRefsMap().isEmpty() && packNames != null
+				&& packNames.isEmpty();
+	}
+
+	private void createNewRepository(final List<RemoteRefUpdate> updates)
+			throws TransportException {
+		try {
+			final String ref = "ref: " + pickHEAD(updates) + "\n";
+			final byte[] bytes = ref.getBytes(Constants.CHARACTER_ENCODING);
+			dest.writeFile("../HEAD", bytes);
+		} catch (IOException e) {
+			throw new TransportException(uri, "cannot create HEAD", e);
+		}
+
+		try {
+			final String config = "[core]\n"
+					+ "\trepositoryformatversion = 0\n";
+			final byte[] bytes = config.getBytes(Constants.CHARACTER_ENCODING);
+			dest.writeFile("../config", bytes);
+		} catch (IOException e) {
+			throw new TransportException(uri, "cannot create config", e);
+		}
+	}
+
+	private static String pickHEAD(final List<RemoteRefUpdate> updates) {
+		// Try to use master if the user is pushing that, it is the
+		// default branch and is likely what they want to remain as
+		// the default on the new remote.
+		//
+		for (final RemoteRefUpdate u : updates) {
+			final String n = u.getRemoteName();
+			if (n.equals(Constants.HEADS_PREFIX + "/" + Constants.MASTER))
+				return n;
+		}
+
+		// Pick any branch, under the assumption the user pushed only
+		// one to the remote side.
+		//
+		for (final RemoteRefUpdate u : updates) {
+			final String n = u.getRemoteName();
+			if (n.startsWith(Constants.HEADS_PREFIX + "/"))
+				return n;
+		}
+		return updates.get(0).getRemoteName();
+	}
 }
-- 
1.5.6.74.g8a5e

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

* [JGIT PATCH 4/4] Use a singleton for the NullProgressMonitor implementation
  2008-07-09  4:15     ` [JGIT PATCH 3/4] Automatically initialize a new dumb repository during push Shawn O. Pearce
@ 2008-07-09  4:15       ` Shawn O. Pearce
  0 siblings, 0 replies; 5+ messages in thread
From: Shawn O. Pearce @ 2008-07-09  4:15 UTC (permalink / raw)
  To: Robin Rosenberg, Marek Zawirski; +Cc: git

No reason to create a new instance every time we need to shove
a null monitor into a variable because someone passed us null.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../org/spearce/jgit/lib/NullProgressMonitor.java  |    3 +++
 .../src/org/spearce/jgit/util/TemporaryBuffer.java |    2 +-
 2 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/NullProgressMonitor.java b/org.spearce.jgit/src/org/spearce/jgit/lib/NullProgressMonitor.java
index de75b90..191dc00 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/NullProgressMonitor.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/NullProgressMonitor.java
@@ -42,6 +42,9 @@ package org.spearce.jgit.lib;
  * A NullProgressMonitor does not report progress anywhere.
  */
 public class NullProgressMonitor implements ProgressMonitor {
+	/** Immutable instance of a null progress monitor. */
+	public static final NullProgressMonitor INSTANCE = new NullProgressMonitor();
+
 	public void start(int totalTasks) {
 		// Do not report.
 	}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/util/TemporaryBuffer.java b/org.spearce.jgit/src/org/spearce/jgit/util/TemporaryBuffer.java
index 20db580..d597c38 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/util/TemporaryBuffer.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/util/TemporaryBuffer.java
@@ -200,7 +200,7 @@ public class TemporaryBuffer extends OutputStream {
 	public void writeTo(final OutputStream os, ProgressMonitor pm)
 			throws IOException {
 		if (pm == null)
-			pm = new NullProgressMonitor();
+			pm = NullProgressMonitor.INSTANCE;
 		if (blocks != null) {
 			// Everything is in core so we can stream directly to the output.
 			//
-- 
1.5.6.74.g8a5e

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

end of thread, other threads:[~2008-07-09  4:16 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-09  4:15 [JGIT PATCH 0/4] Misc. push transport fixes Shawn O. Pearce
2008-07-09  4:15 ` [JGIT PATCH 1/4] Avoid deadlock while fetching from local repository Shawn O. Pearce
2008-07-09  4:15   ` [JGIT PATCH 2/4] Fix pushing of annotated tags to actually include the tag object Shawn O. Pearce
2008-07-09  4:15     ` [JGIT PATCH 3/4] Automatically initialize a new dumb repository during push Shawn O. Pearce
2008-07-09  4:15       ` [JGIT PATCH 4/4] Use a singleton for the NullProgressMonitor implementation Shawn O. Pearce

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 NNTP newsgroup(s).