* [JGIT PATCH 00/11] Repository instance caching
@ 2009-07-11 20:19 Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 01/11] Change Daemon to use concurrent collections for exported repositories Shawn O. Pearce
0 siblings, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
This series lays the groundwork for caching Repository instances
within the JRE, such as a server daemon might want when it is
accessing multiple repositories, like a git:// daemon needs to do.
We also modify the way alternates are handled, checking to see if
an alternate is actually an objects directory for a Repository,
and if so, using the Repository instance from the cache in the
alternate chain. This permits better reuse of pack files windows
when repositories are using alternates. Based on this support we can
also now implement the ".have" logic in the ReceivePack server code.
I plan to follow onto this series with work to support submodules in
a server setting, e.g. processing the .gitmodules directly from the
object database and being able to examine the submodule Repository.
Shawn O. Pearce (11):
Change Daemon to use concurrent collections for exported repositories
Make Daemon's exportAll check not require synchronization
Don't retry ".git" suffix in daemon if already tried
Refactor ref advertisement code from server implementations
Don't advertise HEAD from ReceivePack
Add a use reference counter to Repository
Introduce RepositoryCache to cache handles of Repository objects
Change Daemon to use RepositoryCache
Expose the Repository's ObjectDatabase object
Use cached Repository instances when resolving alternates
Send .have lines in ReceivePack for alternate repositories
.../org/spearce/jgit/lib/RepositoryCacheTest.java | 126 +++++++
.../org/spearce/jgit/lib/RepositoryTestCase.java | 16 +-
.../jgit/errors/RepositoryNotFoundException.java | 65 ++++
.../jgit/lib/AlternateRepositoryDatabase.java | 127 +++++++
.../src/org/spearce/jgit/lib/ObjectDatabase.java | 16 +-
.../src/org/spearce/jgit/lib/ObjectDirectory.java | 18 +-
.../src/org/spearce/jgit/lib/Repository.java | 18 +-
.../src/org/spearce/jgit/lib/RepositoryCache.java | 386 ++++++++++++++++++++
.../src/org/spearce/jgit/transport/Daemon.java | 76 ++---
.../org/spearce/jgit/transport/DaemonService.java | 14 +-
.../org/spearce/jgit/transport/ReceivePack.java | 67 +---
.../org/spearce/jgit/transport/RefAdvertiser.java | 189 ++++++++++
.../src/org/spearce/jgit/transport/UploadPack.java | 93 +-----
13 files changed, 1019 insertions(+), 192 deletions(-)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryCacheTest.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/RepositoryNotFoundException.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/AlternateRepositoryDatabase.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryCache.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
^ permalink raw reply [flat|nested] 14+ messages in thread
* [JGIT PATCH 01/11] Change Daemon to use concurrent collections for exported repositories
2009-07-11 20:19 [JGIT PATCH 00/11] Repository instance caching Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 02/11] Make Daemon's exportAll check not require synchronization Shawn O. Pearce
0 siblings, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
A ConcurrentHashMap can be read without taking a lock, which makes it
more efficient when serving the concurrent client connections. This
is also true of a CopyOnWriteArrayList, where again the readers far
outweigh the writers. Copying on write is much more efficient than
copying on read.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/transport/Daemon.java | 36 +++++++------------
1 files changed, 13 insertions(+), 23 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
index 3101d6f..d3f7b2e 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
@@ -46,10 +46,10 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
import org.spearce.jgit.lib.PersonIdent;
import org.spearce.jgit.lib.Repository;
@@ -93,8 +93,8 @@ public Daemon() {
*/
public Daemon(final InetSocketAddress addr) {
myAddress = addr;
- exports = new HashMap<String, Repository>();
- exportBase = new ArrayList<File>();
+ exports = new ConcurrentHashMap<String, Repository>();
+ exportBase = new CopyOnWriteArrayList<File>();
processors = new ThreadGroup("Git-Daemon");
services = new DaemonService[] {
@@ -196,9 +196,7 @@ public synchronized void setExportAll(final boolean export) {
* the repository instance.
*/
public void exportRepository(final String name, final Repository db) {
- synchronized (exports) {
- exports.put(name, db);
- }
+ exports.put(name, db);
}
/**
@@ -210,9 +208,7 @@ public void exportRepository(final String name, final Repository db) {
* named <code>git-daemon-export-ok</code> will be published.
*/
public void exportDirectory(final File dir) {
- synchronized (exportBase) {
- exportBase.add(dir);
- }
+ exportBase.add(dir);
}
/** @return timeout (in seconds) before aborting an IO operation. */
@@ -351,21 +347,15 @@ Repository openRepository(String name) {
name = name.substring(1);
Repository db;
- synchronized (exports) {
- db = exports.get(name);
- if (db != null)
- return db;
+ db = exports.get(name);
+ if (db != null)
+ return db;
- db = exports.get(name + ".git");
- if (db != null)
- return db;
- }
+ db = exports.get(name + ".git");
+ if (db != null)
+ return db;
- final File[] search;
- synchronized (exportBase) {
- search = exportBase.toArray(new File[exportBase.size()]);
- }
- for (final File f : search) {
+ for (final File f : exportBase) {
db = openRepository(new File(f, name));
if (db != null)
return db;
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 02/11] Make Daemon's exportAll check not require synchronization
2009-07-11 20:19 ` [JGIT PATCH 01/11] Change Daemon to use concurrent collections for exported repositories Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 03/11] Don't retry ".git" suffix in daemon if already tried Shawn O. Pearce
0 siblings, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
A volatile boolean can be read more efficiently than using a
synchronized monitor on "this", as it only requires that the
load of the boolean be examining the most recent copy from the
memory bus. Since the value of the boolean has no further
impact on the object state, we don't need to worry about more
than ensuring we see the current value of it.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/transport/Daemon.java | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
index d3f7b2e..7b4c138 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
@@ -67,7 +67,7 @@
private final ThreadGroup processors;
- private boolean exportAll;
+ private volatile boolean exportAll;
private Map<String, Repository> exports;
@@ -164,7 +164,7 @@ public synchronized DaemonService getService(String name) {
* ignored.
* @see #setExportAll(boolean)
*/
- public synchronized boolean isExportAll() {
+ public boolean isExportAll() {
return exportAll;
}
@@ -180,7 +180,7 @@ public synchronized boolean isExportAll() {
*
* @param export
*/
- public synchronized void setExportAll(final boolean export) {
+ public void setExportAll(final boolean export) {
exportAll = export;
}
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 03/11] Don't retry ".git" suffix in daemon if already tried
2009-07-11 20:19 ` [JGIT PATCH 02/11] Make Daemon's exportAll check not require synchronization Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 04/11] Refactor ref advertisement code from server implementations Shawn O. Pearce
0 siblings, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
If the name we already tried ended in ".git", don't append ".git"
(creating "foo.git.git") and search the map again. Instead the
names are normalized to always end with ".git", and ".git" is put
on the end if it is not present in the user request.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/transport/Daemon.java | 10 ++++------
1 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
index 7b4c138..f55e049 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
@@ -195,7 +195,9 @@ public void setExportAll(final boolean export) {
* @param db
* the repository instance.
*/
- public void exportRepository(final String name, final Repository db) {
+ public void exportRepository(String name, final Repository db) {
+ if (!name.endsWith(".git"))
+ name = name + ".git";
exports.put(name, db);
}
@@ -347,11 +349,7 @@ Repository openRepository(String name) {
name = name.substring(1);
Repository db;
- db = exports.get(name);
- if (db != null)
- return db;
-
- db = exports.get(name + ".git");
+ db = exports.get(name.endsWith(".git") ? name : name + ".git");
if (db != null)
return db;
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 04/11] Refactor ref advertisement code from server implementations
2009-07-11 20:19 ` [JGIT PATCH 03/11] Don't retry ".git" suffix in daemon if already tried Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack Shawn O. Pearce
0 siblings, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
The RefAdvertiser implements the common logic from both UploadPack
and ReceivePack, as the code here was really very similar. By using
a support class we can also cleanup the nasty passing of parameters
for temporary data, and instead rely on the instance members of the
support class to handle it for us.
A minor change in functionality exists in this change, we now make
a temporary RevFlag and tag all advertised objects in ReceivePack.
This is a pointless operation that currently wastes a very small
amount of CPU time during setup of the advertisement, but is so
minor that its just not worth optimizing out. The code reuse is
much more important for maintenance.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../org/spearce/jgit/transport/ReceivePack.java | 61 ++------
.../org/spearce/jgit/transport/RefAdvertiser.java | 155 ++++++++++++++++++++
.../src/org/spearce/jgit/transport/UploadPack.java | 93 ++-----------
3 files changed, 177 insertions(+), 132 deletions(-)
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
index 16b0c57..fd8aa86 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
@@ -46,8 +46,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -60,12 +60,12 @@
import org.spearce.jgit.lib.PackLock;
import org.spearce.jgit.lib.PersonIdent;
import org.spearce.jgit.lib.Ref;
-import org.spearce.jgit.lib.RefComparator;
import org.spearce.jgit.lib.RefUpdate;
import org.spearce.jgit.lib.Repository;
import org.spearce.jgit.lib.RepositoryConfig;
import org.spearce.jgit.revwalk.ObjectWalk;
import org.spearce.jgit.revwalk.RevCommit;
+import org.spearce.jgit.revwalk.RevFlag;
import org.spearce.jgit.revwalk.RevObject;
import org.spearce.jgit.revwalk.RevWalk;
import org.spearce.jgit.transport.ReceiveCommand.Result;
@@ -503,55 +503,18 @@ private void unlockPack() {
}
private void sendAdvertisedRefs() throws IOException {
+ final RevFlag advertised = walk.newFlag("ADVERTISED");
+ final RefAdvertiser adv = new RefAdvertiser(pckOut, walk, advertised);
+ adv.advertiseCapability(CAPABILITY_DELETE_REFS);
+ adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
+ if (allowOfsDelta)
+ adv.advertiseCapability(CAPABILITY_OFS_DELTA);
refs = db.getAllRefs();
-
- final StringBuilder m = new StringBuilder(100);
- final char[] idtmp = new char[2 * Constants.OBJECT_ID_LENGTH];
- final Iterator<Ref> i = RefComparator.sort(refs.values()).iterator();
- boolean first = true;
- while (i.hasNext()) {
- final Ref r = i.next();
- if (r.getObjectId() == null)
- continue;
- format(m, idtmp, r.getObjectId(), r.getOrigName());
- if (first) {
- first = false;
- advertiseCapabilities(m);
- }
- writeAdvertisedRef(m);
- }
- if (first) {
- format(m, idtmp, ObjectId.zeroId(), "capabilities^{}");
- advertiseCapabilities(m);
- writeAdvertisedRef(m);
- }
+ adv.send(refs.values());
+ if (adv.isEmpty())
+ adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
pckOut.end();
- }
-
- private void advertiseCapabilities(final StringBuilder m) {
- m.append('\0');
- m.append(' ');
- m.append(CAPABILITY_DELETE_REFS);
- m.append(' ');
- m.append(CAPABILITY_REPORT_STATUS);
- if (allowOfsDelta) {
- m.append(' ');
- m.append(CAPABILITY_OFS_DELTA);
- }
- m.append(' ');
- }
-
- private void format(final StringBuilder m, final char[] idtmp,
- final ObjectId id, final String name) {
- m.setLength(0);
- id.copyTo(idtmp, m);
- m.append(' ');
- m.append(name);
- }
-
- private void writeAdvertisedRef(final StringBuilder m) throws IOException {
- m.append('\n');
- pckOut.writeString(m.toString());
+ walk.disposeFlag(advertised);
}
private void recvCommands() throws IOException {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
new file mode 100644
index 0000000..245891d
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2008, 2009, Google Inc.
+ *
+ * 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.LinkedHashSet;
+import java.util.Set;
+
+import org.spearce.jgit.lib.AnyObjectId;
+import org.spearce.jgit.lib.Constants;
+import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.lib.RefComparator;
+import org.spearce.jgit.revwalk.RevFlag;
+import org.spearce.jgit.revwalk.RevObject;
+import org.spearce.jgit.revwalk.RevTag;
+import org.spearce.jgit.revwalk.RevWalk;
+
+/** Support for the start of {@link UploadPack} and {@link ReceivePack}. */
+class RefAdvertiser {
+ private final PacketLineOut pckOut;
+
+ private final RevWalk walk;
+
+ private final RevFlag ADVERTISED;
+
+ private final StringBuilder tmpLine = new StringBuilder(100);
+
+ private final char[] tmpId = new char[2 * Constants.OBJECT_ID_LENGTH];
+
+ private final Set<String> capablities = new LinkedHashSet<String>();
+
+ private boolean derefTags;
+
+ private boolean first = true;
+
+ RefAdvertiser(final PacketLineOut out, final RevWalk protoWalk,
+ final RevFlag advertisedFlag) {
+ pckOut = out;
+ walk = protoWalk;
+ ADVERTISED = advertisedFlag;
+ }
+
+ void setDerefTags(final boolean deref) {
+ derefTags = deref;
+ }
+
+ void advertiseCapability(String name) {
+ capablities.add(name);
+ }
+
+ void send(final Collection<Ref> refs) throws IOException {
+ for (final Ref r : RefComparator.sort(refs)) {
+ final RevObject obj = parseAnyOrNull(r.getObjectId());
+ if (obj != null) {
+ advertiseAny(obj, r.getOrigName());
+ if (derefTags && obj instanceof RevTag)
+ advertiseTag((RevTag) obj, r.getOrigName() + "^{}");
+ }
+ }
+ }
+
+ boolean isEmpty() {
+ return first;
+ }
+
+ private RevObject parseAnyOrNull(final AnyObjectId id) {
+ if (id == null)
+ return null;
+ try {
+ return walk.parseAny(id);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private void advertiseAny(final RevObject obj, final String refName)
+ throws IOException {
+ obj.add(ADVERTISED);
+ advertiseId(obj, refName);
+ }
+
+ private void advertiseTag(final RevTag tag, final String refName)
+ throws IOException {
+ RevObject o = tag;
+ do {
+ // Fully unwrap here so later on we have these already parsed.
+ final RevObject target = ((RevTag) o).getObject();
+ try {
+ walk.parseHeaders(target);
+ } catch (IOException err) {
+ return;
+ }
+ target.add(ADVERTISED);
+ o = target;
+ } while (o instanceof RevTag);
+ advertiseAny(tag.getObject(), refName);
+ }
+
+ void advertiseId(final AnyObjectId id, final String refName)
+ throws IOException {
+ tmpLine.setLength(0);
+ id.copyTo(tmpId, tmpLine);
+ tmpLine.append(' ');
+ tmpLine.append(refName);
+ if (first) {
+ first = false;
+ if (!capablities.isEmpty()) {
+ tmpLine.append('\0');
+ for (final String capName : capablities) {
+ tmpLine.append(' ');
+ tmpLine.append(capName);
+ }
+ tmpLine.append(' ');
+ }
+ }
+ tmpLine.append('\n');
+ pckOut.writeString(tmpLine.toString());
+ }
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java
index b0fa885..179670f 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java
@@ -50,13 +50,11 @@
import java.util.Set;
import org.spearce.jgit.errors.PackProtocolException;
-import org.spearce.jgit.lib.Constants;
import org.spearce.jgit.lib.NullProgressMonitor;
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.lib.RefComparator;
import org.spearce.jgit.lib.Repository;
import org.spearce.jgit.revwalk.RevCommit;
import org.spearce.jgit.revwalk.RevFlag;
@@ -249,91 +247,20 @@ private void service() throws IOException {
}
private void sendAdvertisedRefs() throws IOException {
+ final RefAdvertiser adv = new RefAdvertiser(pckOut, walk, ADVERTISED);
+ adv.advertiseCapability(OPTION_INCLUDE_TAG);
+ adv.advertiseCapability(OPTION_MULTI_ACK);
+ adv.advertiseCapability(OPTION_OFS_DELTA);
+ adv.advertiseCapability(OPTION_SIDE_BAND);
+ adv.advertiseCapability(OPTION_SIDE_BAND_64K);
+ adv.advertiseCapability(OPTION_THIN_PACK);
+ adv.advertiseCapability(OPTION_NO_PROGRESS);
+ adv.setDerefTags(true);
refs = db.getAllRefs();
-
- final StringBuilder m = new StringBuilder(100);
- final char[] idtmp = new char[2 * Constants.OBJECT_ID_LENGTH];
- final Iterator<Ref> i = RefComparator.sort(refs.values()).iterator();
- if (i.hasNext()) {
- final Ref r = i.next();
- final RevObject o = safeParseAny(r.getObjectId());
- if (o != null) {
- advertise(m, idtmp, o, r.getOrigName());
- m.append('\0');
- m.append(' ');
- m.append(OPTION_INCLUDE_TAG);
- m.append(' ');
- m.append(OPTION_MULTI_ACK);
- m.append(' ');
- m.append(OPTION_OFS_DELTA);
- m.append(' ');
- m.append(OPTION_SIDE_BAND);
- m.append(' ');
- m.append(OPTION_SIDE_BAND_64K);
- m.append(' ');
- m.append(OPTION_THIN_PACK);
- m.append(' ');
- m.append(OPTION_NO_PROGRESS);
- m.append(' ');
- writeAdvertisedRef(m);
- if (o instanceof RevTag)
- writeAdvertisedTag(m, idtmp, o, r.getName());
- }
- }
- while (i.hasNext()) {
- final Ref r = i.next();
- if (r.getObjectId() == null)
- continue;
- final RevObject o = safeParseAny(r.getObjectId());
- if (o != null) {
- advertise(m, idtmp, o, r.getOrigName());
- writeAdvertisedRef(m);
- if (o instanceof RevTag)
- writeAdvertisedTag(m, idtmp, o, r.getName());
- }
- }
+ adv.send(refs.values());
pckOut.end();
}
- private RevObject safeParseAny(final ObjectId id) {
- try {
- return walk.parseAny(id);
- } catch (IOException e) {
- return null;
- }
- }
-
- private void advertise(final StringBuilder m, final char[] idtmp,
- final RevObject o, final String name) {
- o.add(ADVERTISED);
- m.setLength(0);
- o.getId().copyTo(idtmp, m);
- m.append(' ');
- m.append(name);
- }
-
- private void writeAdvertisedRef(final StringBuilder m) throws IOException {
- m.append('\n');
- pckOut.writeString(m.toString());
- }
-
- private void writeAdvertisedTag(final StringBuilder m, final char[] idtmp,
- final RevObject tag, final String name) throws IOException {
- RevObject o = tag;
- while (o instanceof RevTag) {
- // Fully unwrap here so later on we have these already parsed.
- try {
- walk.parseHeaders(((RevTag) o).getObject());
- } catch (IOException err) {
- return;
- }
- o = ((RevTag) o).getObject();
- o.add(ADVERTISED);
- }
- advertise(m, idtmp, ((RevTag) tag).getObject(), name + "^{}");
- writeAdvertisedRef(m);
- }
-
private void recvWants() throws IOException {
boolean isFirst = true;
for (;; isFirst = false) {
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack
2009-07-11 20:19 ` [JGIT PATCH 04/11] Refactor ref advertisement code from server implementations Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 06/11] Add a use reference counter to Repository Shawn O. Pearce
2009-07-21 15:22 ` [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack Robin Rosenberg
0 siblings, 2 replies; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
The HEAD ref cannot be pushed to by a client, if it is a detached HEAD
the client shouldn't be permitted to change it, if it is a symref to
another ref then the client should update the destination ref and not
the symref. Instead offer the HEAD ref as a ".have" line, which is an
invalid ref that the client can't update but still lets the client know
we have its target object reachable.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../org/spearce/jgit/transport/ReceivePack.java | 5 ++++-
.../org/spearce/jgit/transport/RefAdvertiser.java | 15 +++++++++++++++
2 files changed, 19 insertions(+), 1 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
index fd8aa86..1c490af 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
@@ -509,8 +509,11 @@ private void sendAdvertisedRefs() throws IOException {
adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
if (allowOfsDelta)
adv.advertiseCapability(CAPABILITY_OFS_DELTA);
- refs = db.getAllRefs();
+ refs = new HashMap<String, Ref>(db.getAllRefs());
+ final Ref head = refs.remove(Constants.HEAD);
adv.send(refs.values());
+ if (head != null && head.getName() == head.getOrigName())
+ adv.advertiseHave(head.getObjectId());
if (adv.isEmpty())
adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
pckOut.end();
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
index 245891d..91700da 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
@@ -95,6 +95,15 @@ void send(final Collection<Ref> refs) throws IOException {
}
}
+ void advertiseHave(AnyObjectId id) throws IOException {
+ RevObject obj = parseAnyOrNull(id);
+ if (obj != null) {
+ advertiseAnyOnce(obj, ".have");
+ if (obj instanceof RevTag)
+ advertiseAnyOnce(((RevTag) obj).getObject(), ".have");
+ }
+ }
+
boolean isEmpty() {
return first;
}
@@ -109,6 +118,12 @@ private RevObject parseAnyOrNull(final AnyObjectId id) {
}
}
+ private void advertiseAnyOnce(final RevObject obj, final String refName)
+ throws IOException {
+ if (!obj.has(ADVERTISED))
+ advertiseAny(obj, refName);
+ }
+
private void advertiseAny(final RevObject obj, final String refName)
throws IOException {
obj.add(ADVERTISED);
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 06/11] Add a use reference counter to Repository
2009-07-11 20:19 ` [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 07/11] Introduce RepositoryCache to cache handles of Repository objects Shawn O. Pearce
2009-07-21 15:22 ` [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack Robin Rosenberg
1 sibling, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
This permits callers to reuse Repository instances across threads,
by using incrementOpen() to indicate the instance is in use, and
a matched close() to release the usage counter.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/lib/Repository.java | 11 ++++++++++-
1 files changed, 10 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 fb72b64..e3ac01b 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
@@ -54,6 +54,7 @@
import java.util.Map;
import java.util.Set;
import java.util.Vector;
+import java.util.concurrent.atomic.AtomicInteger;
import org.spearce.jgit.errors.IncorrectObjectTypeException;
import org.spearce.jgit.errors.RevisionSyntaxException;
@@ -85,6 +86,8 @@
*
*/
public class Repository {
+ private final AtomicInteger useCnt = new AtomicInteger(1);
+
private final File gitDir;
private final RepositoryConfig config;
@@ -721,11 +724,17 @@ private ObjectId resolveSimple(final String revstr) throws IOException {
return r != null ? r.getObjectId() : null;
}
+ /** Increment the use counter by one, requiring a matched {@link #close()}. */
+ public void incrementOpen() {
+ useCnt.incrementAndGet();
+ }
+
/**
* Close all resources used by this repository
*/
public void close() {
- objectDatabase.close();
+ if (useCnt.decrementAndGet() == 0)
+ objectDatabase.close();
}
/**
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 07/11] Introduce RepositoryCache to cache handles of Repository objects
2009-07-11 20:19 ` [JGIT PATCH 06/11] Add a use reference counter to Repository Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 08/11] Change Daemon to use RepositoryCache Shawn O. Pearce
0 siblings, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git, Constantine Plotnikov
In almost any application using JGit the application code wants to
open more than one Repository at once. Most applications wind up
creating their own form of this RepositoryCache concept, where we
attempt to map some unique key (e.g. the directory path on disk)
to the open Repository instance, so that concurrent accesses to
the same Git repository go through the same JGit object and thus
can share the same memory pools.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
CC: Constantine Plotnikov <constantine.plotnikov@gmail.com>
---
.../org/spearce/jgit/lib/RepositoryCacheTest.java | 126 +++++++
.../org/spearce/jgit/lib/RepositoryTestCase.java | 16 +-
.../jgit/errors/RepositoryNotFoundException.java | 65 ++++
.../src/org/spearce/jgit/lib/RepositoryCache.java | 386 ++++++++++++++++++++
4 files changed, 592 insertions(+), 1 deletions(-)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryCacheTest.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/RepositoryNotFoundException.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryCache.java
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryCacheTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryCacheTest.java
new file mode 100644
index 0000000..f466e00
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryCacheTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2009, Google Inc.
+ *
+ * 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.lib;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.spearce.jgit.errors.RepositoryNotFoundException;
+import org.spearce.jgit.lib.RepositoryCache.FileKey;
+
+public class RepositoryCacheTest extends RepositoryTestCase {
+ public void testNonBareFileKey() {
+ File gitdir = db.getDirectory();
+ File parent = gitdir.getParentFile();
+ File other = new File(parent, "notagit");
+ assertEquals(gitdir, FileKey.exact(gitdir).getFile());
+ assertEquals(parent, FileKey.exact(parent).getFile());
+ assertEquals(other, FileKey.exact(other).getFile());
+
+ assertEquals(gitdir, FileKey.lenient(gitdir).getFile());
+ assertEquals(gitdir, FileKey.lenient(parent).getFile());
+ assertEquals(other, FileKey.lenient(other).getFile());
+ }
+
+ public void testBareFileKey() throws IOException {
+ Repository bare = createNewEmptyRepo(true);
+ File gitdir = bare.getDirectory();
+ File parent = gitdir.getParentFile();
+ String name = gitdir.getName();
+ assertTrue(name.endsWith(".git"));
+ name = name.substring(0, name.length() - 4);
+
+ assertEquals(gitdir, FileKey.exact(gitdir).getFile());
+
+ assertEquals(gitdir, FileKey.lenient(gitdir).getFile());
+ assertEquals(gitdir, FileKey.lenient(new File(parent, name)).getFile());
+ }
+
+ public void testFileKeyOpenExisting() throws IOException {
+ Repository r;
+
+ r = new FileKey(db.getDirectory()).open(true);
+ assertNotNull(r);
+ assertEquals(db.getDirectory(), r.getDirectory());
+ r.close();
+
+ r = new FileKey(db.getDirectory()).open(false);
+ assertNotNull(r);
+ assertEquals(db.getDirectory(), r.getDirectory());
+ r.close();
+ }
+
+ public void testFileKeyOpenNew() throws IOException {
+ final Repository n = createNewEmptyRepo(true);
+ final File gitdir = n.getDirectory();
+ n.close();
+ recursiveDelete(gitdir);
+ assertFalse(gitdir.exists());
+
+ try {
+ new FileKey(gitdir).open(true);
+ fail("incorrectly opened a non existant repository");
+ } catch (RepositoryNotFoundException e) {
+ assertEquals("repository not found: " + gitdir, e.getMessage());
+ }
+
+ final Repository o = new FileKey(gitdir).open(false);
+ assertNotNull(o);
+ assertEquals(gitdir, o.getDirectory());
+ assertFalse(gitdir.exists());
+ }
+
+ public void testCacheRegisterOpen() throws Exception {
+ final File dir = db.getDirectory();
+ RepositoryCache.register(db);
+ assertSame(db, RepositoryCache.open(FileKey.exact(dir)));
+
+ assertEquals(".git", dir.getName());
+ final File parent = dir.getParentFile();
+ assertSame(db, RepositoryCache.open(FileKey.lenient(parent)));
+ }
+
+ public void testCacheOpen() throws Exception {
+ final FileKey loc = FileKey.exact(db.getDirectory());
+ final Repository d2 = RepositoryCache.open(loc);
+ assertNotSame(db, d2);
+ assertSame(d2, RepositoryCache.open(FileKey.exact(loc.getFile())));
+ d2.close();
+ d2.close();
+ }
+}
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java
index 3837ea9..2783180 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java
@@ -307,6 +307,7 @@ public void run() {
}
protected void tearDown() throws Exception {
+ RepositoryCache.clear();
db.close();
for (Repository r : repositoriesToClose)
r.close();
@@ -334,8 +335,21 @@ protected void tearDown() throws Exception {
* @throws IOException
*/
protected Repository createNewEmptyRepo() throws IOException {
+ return createNewEmptyRepo(false);
+ }
+
+ /**
+ * Helper for creating extra empty repos
+ *
+ * @param bare if true, create a bare repository.
+ * @return a new empty git repository for testing purposes
+ *
+ * @throws IOException
+ */
+ protected Repository createNewEmptyRepo(boolean bare) throws IOException {
final File newTestRepo = new File(trashParent, "new"
- + System.currentTimeMillis() + "." + (testcount++) + "/.git");
+ + System.currentTimeMillis() + "." + (testcount++)
+ + (bare ? "" : "/") + ".git");
assertFalse(newTestRepo.exists());
final Repository newRepo = new Repository(newTestRepo);
newRepo.create();
diff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/RepositoryNotFoundException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/RepositoryNotFoundException.java
new file mode 100644
index 0000000..f47f577
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/errors/RepositoryNotFoundException.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009, Google Inc.
+ *
+ * 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.errors;
+
+import java.io.File;
+
+/** Indicates a local repository does not exist. */
+public class RepositoryNotFoundException extends TransportException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs an exception indicating a local repository does not exist.
+ *
+ * @param location
+ * description of the repository not found, usually file path.
+ */
+ public RepositoryNotFoundException(final File location) {
+ this(location.getPath());
+ }
+
+ /**
+ * Constructs an exception indicating a local repository does not exist.
+ *
+ * @param location
+ * description of the repository not found, usually file path.
+ */
+ public RepositoryNotFoundException(final String location) {
+ super("repository not found: " + location);
+ }
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryCache.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryCache.java
new file mode 100644
index 0000000..50b4330
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryCache.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2009, Google Inc.
+ *
+ * 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.lib;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.spearce.jgit.errors.RepositoryNotFoundException;
+import org.spearce.jgit.util.FS;
+
+/** Cache of active {@link Repository} instances. */
+public class RepositoryCache {
+ private static final RepositoryCache cache = new RepositoryCache();
+
+ /**
+ * Open an existing repository, reusing a cached instance if possible.
+ * <p>
+ * When done with the repository, the caller must call
+ * {@link Repository#close()} to decrement the repository's usage counter.
+ *
+ * @param location
+ * where the local repository is. Typically a {@link FileKey}.
+ * @return the repository instance requested; caller must close when done.
+ * @throws IOException
+ * the repository could not be read (likely its core.version
+ * property is not supported).
+ * @throws RepositoryNotFoundException
+ * there is no repository at the given location.
+ */
+ public static Repository open(final Key location) throws IOException,
+ RepositoryNotFoundException {
+ return open(location, true);
+ }
+
+ /**
+ * Open a repository, reusing a cached instance if possible.
+ * <p>
+ * When done with the repository, the caller must call
+ * {@link Repository#close()} to decrement the repository's usage counter.
+ *
+ * @param location
+ * where the local repository is. Typically a {@link FileKey}.
+ * @param mustExist
+ * If true, and the repository is not found, throws {@code
+ * RepositoryNotFoundException}. If false, a repository instance
+ * is created and registered anyway.
+ * @return the repository instance requested; caller must close when done.
+ * @throws IOException
+ * the repository could not be read (likely its core.version
+ * property is not supported).
+ * @throws RepositoryNotFoundException
+ * There is no repository at the given location, only thrown if
+ * {@code mustExist} is true.
+ */
+ public static Repository open(final Key location, final boolean mustExist)
+ throws IOException {
+ return cache.openRepository(location, mustExist);
+ }
+
+ /**
+ * Register one repository into the cache.
+ * <p>
+ * During registration the cache automatically increments the usage counter,
+ * permitting it to retain the reference. A {@link FileKey} for the
+ * repository's {@link Repository#getDirectory()} is used to index the
+ * repository in the cache.
+ * <p>
+ * If another repository already is registered in the cache at this
+ * location, the other instance is closed.
+ *
+ * @param db
+ * repository to register.
+ */
+ public static void register(final Repository db) {
+ cache.registerRepository(FileKey.exact(db.getDirectory()), db);
+ }
+
+ /**
+ * Remove a repository from the cache.
+ * <p>
+ * Removes a repository from the cache, if it is still registered here,
+ * permitting it to close.
+ *
+ * @param db
+ * repository to unregister.
+ */
+ public static void close(final Repository db) {
+ cache.unregisterRepository(FileKey.exact(db.getDirectory()));
+ }
+
+ /** Unregister all repositories from the cache. */
+ public static void clear() {
+ cache.clearAll();
+ }
+
+ private final ConcurrentHashMap<Key, Reference<Repository>> cacheMap;
+
+ private final Lock[] openLocks;
+
+ private RepositoryCache() {
+ cacheMap = new ConcurrentHashMap<Key, Reference<Repository>>();
+ openLocks = new Lock[4];
+ for (int i = 0; i < openLocks.length; i++)
+ openLocks[i] = new Lock();
+ }
+
+ private Repository openRepository(final Key location,
+ final boolean mustExist) throws IOException {
+ Reference<Repository> ref = cacheMap.get(location);
+ Repository db = ref != null ? ref.get() : null;
+ if (db == null) {
+ synchronized (lockFor(location)) {
+ ref = cacheMap.get(location);
+ db = ref != null ? ref.get() : null;
+ if (db == null) {
+ db = location.open(mustExist);
+ ref = new WeakReference<Repository>(db);
+ cacheMap.put(location, ref);
+ }
+ }
+ }
+ db.incrementOpen();
+ return db;
+ }
+
+ private void registerRepository(final Key location, final Repository db) {
+ db.incrementOpen();
+ WeakReference<Repository> newRef = new WeakReference<Repository>(db);
+ Reference<Repository> oldRef = cacheMap.put(location, newRef);
+ Repository oldDb = oldRef != null ? oldRef.get() : null;
+ if (oldDb != null)
+ oldDb.close();
+ }
+
+ private void unregisterRepository(final Key location) {
+ Reference<Repository> oldRef = cacheMap.remove(location);
+ Repository oldDb = oldRef != null ? oldRef.get() : null;
+ if (oldDb != null)
+ oldDb.close();
+ }
+
+ private void clearAll() {
+ for (int stage = 0; stage < 2; stage++) {
+ for (Iterator<Map.Entry<Key, Reference<Repository>>> i = cacheMap
+ .entrySet().iterator(); i.hasNext();) {
+ final Map.Entry<Key, Reference<Repository>> e = i.next();
+ final Repository db = e.getValue().get();
+ if (db != null)
+ db.close();
+ i.remove();
+ }
+ }
+ }
+
+ private Lock lockFor(final Key location) {
+ return openLocks[(location.hashCode() >>> 1) % openLocks.length];
+ }
+
+ private static class Lock {
+ // Used only for its monitor.
+ }
+
+ /**
+ * Abstract hash key for {@link RepositoryCache} entries.
+ * <p>
+ * A Key instance should be lightweight, and implement hashCode() and
+ * equals() such that two Key instances are equal if they represent the same
+ * Repository location.
+ */
+ public static interface Key {
+ /**
+ * Called by {@link RepositoryCache#open(Key)} if it doesn't exist yet.
+ * <p>
+ * If a repository does not exist yet in the cache, the cache will call
+ * this method to acquire a handle to it.
+ *
+ * @param mustExist
+ * true if the repository must exist in order to be opened;
+ * false if a new non-existent repository is permitted to be
+ * created (the caller is responsible for calling create).
+ * @return the new repository instance.
+ * @throws IOException
+ * the repository could not be read (likely its core.version
+ * property is not supported).
+ * @throws RepositoryNotFoundException
+ * There is no repository at the given location, only thrown
+ * if {@code mustExist} is true.
+ */
+ Repository open(boolean mustExist) throws IOException,
+ RepositoryNotFoundException;
+ }
+
+ /** Location of a Repository, using the standard java.io.File API. */
+ public static class FileKey implements Key {
+ /**
+ * Obtain a pointer to an exact location on disk.
+ * <p>
+ * No guessing is performed, the given location is exactly the GIT_DIR
+ * directory of the repository.
+ *
+ * @param directory
+ * location where the repository database is.
+ * @return a key for the given directory.
+ * @see #lenient(File)
+ */
+ public static FileKey exact(final File directory) {
+ return new FileKey(directory);
+ }
+
+ /**
+ * Obtain a pointer to a location on disk.
+ * <p>
+ * The method performs some basic guessing to locate the repository.
+ * Searched paths are:
+ * <ol>
+ * <li>{@code directory} // assume exact match</li>
+ * <li>{@code directory} + "/.git" // assume working directory</li>
+ * <li>{@code directory} + ".git" // assume bare</li>
+ * </ol>
+ *
+ * @param directory
+ * location where the repository database might be.
+ * @return a key for the given directory.
+ * @see #exact(File)
+ */
+ public static FileKey lenient(final File directory) {
+ final File gitdir = resolve(directory);
+ return new FileKey(gitdir != null ? gitdir : directory);
+ }
+
+ private final File path;
+
+ /**
+ * @param directory
+ * exact location of the repository.
+ */
+ protected FileKey(final File directory) {
+ path = canonical(directory);
+ }
+
+ private static File canonical(final File path) {
+ try {
+ return path.getCanonicalFile();
+ } catch (IOException e) {
+ return path.getAbsoluteFile();
+ }
+ }
+
+ /** @return location supplied to the constructor. */
+ public final File getFile() {
+ return path;
+ }
+
+ public Repository open(final boolean mustExist) throws IOException {
+ if (mustExist && !isGitRepository(path))
+ throw new RepositoryNotFoundException(path);
+ return new Repository(path);
+ }
+
+ @Override
+ public int hashCode() {
+ return path.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ return o instanceof FileKey && path.equals(((FileKey) o).path);
+ }
+
+ @Override
+ public String toString() {
+ return path.toString();
+ }
+
+ /**
+ * Guess if a directory contains a Git repository.
+ * <p>
+ * This method guesses by looking for the existence of some key files
+ * and directories.
+ *
+ * @param dir
+ * the location of the directory to examine.
+ * @return true if the directory "looks like" a Git repository; false if
+ * it doesn't look enough like a Git directory to really be a
+ * Git directory.
+ */
+ public static boolean isGitRepository(final File dir) {
+ return FS.resolve(dir, "objects").exists()
+ && FS.resolve(dir, "refs").exists()
+ && isValidHead(new File(dir, Constants.HEAD));
+ }
+
+ private static boolean isValidHead(final File head) {
+ final String ref = readFirstLine(head);
+ return ref != null
+ && (ref.startsWith("ref: refs/") || ObjectId.isId(ref));
+ }
+
+ private static String readFirstLine(final File head) {
+ try {
+ final BufferedReader br = new BufferedReader(
+ new InputStreamReader(new FileInputStream(head),
+ Constants.CHARSET));
+ try {
+ return br.readLine();
+ } finally {
+ br.close();
+ }
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Guess the proper path for a Git repository.
+ * <p>
+ * The method performs some basic guessing to locate the repository.
+ * Searched paths are:
+ * <ol>
+ * <li>{@code directory} // assume exact match</li>
+ * <li>{@code directory} + "/.git" // assume working directory</li>
+ * <li>{@code directory} + ".git" // assume bare</li>
+ * </ol>
+ *
+ * @param directory
+ * location to guess from. Several permutations are tried.
+ * @return the actual directory location if a better match is found;
+ * null if there is no suitable match.
+ */
+ public static File resolve(final File directory) {
+ if (isGitRepository(directory))
+ return directory;
+ if (isGitRepository(new File(directory, ".git")))
+ return new File(directory, ".git");
+
+ final String name = directory.getName();
+ final File parent = directory.getParentFile();
+ if (isGitRepository(new File(parent, name + ".git")))
+ return new File(parent, name + ".git");
+ return null;
+ }
+ }
+}
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 08/11] Change Daemon to use RepositoryCache
2009-07-11 20:19 ` [JGIT PATCH 07/11] Introduce RepositoryCache to cache handles of Repository objects Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 09/11] Expose the Repository's ObjectDatabase object Shawn O. Pearce
0 siblings, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
Now that we have a nice JVM wide cache for Repository instances,
we can use that inside of the Daemon code to manage setting up
a Repository and locating it on disk using the standard search
rules defined by FileKey.resolve().
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/transport/Daemon.java | 38 +++++++++-----------
.../org/spearce/jgit/transport/DaemonService.java | 14 +++++---
2 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
index f55e049..790745d 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
@@ -53,6 +53,8 @@
import org.spearce.jgit.lib.PersonIdent;
import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.lib.RepositoryCache;
+import org.spearce.jgit.lib.RepositoryCache.FileKey;
/** Basic daemon for the anonymous <code>git://</code> transport protocol. */
public class Daemon {
@@ -199,6 +201,7 @@ public void exportRepository(String name, final Repository db) {
if (!name.endsWith(".git"))
name = name + ".git";
exports.put(name, db);
+ RepositoryCache.register(db);
}
/**
@@ -350,34 +353,27 @@ Repository openRepository(String name) {
Repository db;
db = exports.get(name.endsWith(".git") ? name : name + ".git");
- if (db != null)
+ if (db != null) {
+ db.incrementOpen();
return db;
+ }
- for (final File f : exportBase) {
- db = openRepository(new File(f, name));
- if (db != null)
- return db;
-
- db = openRepository(new File(f, name + ".git"));
- if (db != null)
- return db;
-
- db = openRepository(new File(f, name + "/.git"));
- if (db != null)
- return db;
+ for (final File baseDir : exportBase) {
+ final File gitdir = FileKey.resolve(new File(baseDir, name));
+ if (gitdir != null && canExport(gitdir))
+ return openRepository(gitdir);
}
return null;
}
- private Repository openRepository(final File d) {
- if (d.isDirectory() && canExport(d)) {
- try {
- return new Repository(d);
- } catch (IOException err) {
- // Ignore
- }
+ private static Repository openRepository(final File gitdir) {
+ try {
+ return RepositoryCache.open(FileKey.exact(gitdir));
+ } catch (IOException err) {
+ // null signals it "wasn't found", which is all that is suitable
+ // for the remote client to know.
+ return null;
}
- return null;
}
private boolean canExport(final File d) {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonService.java b/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonService.java
index c511d5a..817aeee 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonService.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonService.java
@@ -108,11 +108,15 @@ void execute(final DaemonClient client, final String commandLine)
final Repository db = client.getDaemon().openRepository(name);
if (db == null)
return;
- boolean on = isEnabled();
- if (isOverridable())
- on = db.getConfig().getBoolean("daemon", config, on);
- if (on)
- execute(client, db);
+ try {
+ boolean on = isEnabled();
+ if (isOverridable())
+ on = db.getConfig().getBoolean("daemon", config, on);
+ if (on)
+ execute(client, db);
+ } finally {
+ db.close();
+ }
}
abstract void execute(DaemonClient client, Repository db)
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 09/11] Expose the Repository's ObjectDatabase object
2009-07-11 20:19 ` [JGIT PATCH 08/11] Change Daemon to use RepositoryCache Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 10/11] Use cached Repository instances when resolving alternates Shawn O. Pearce
0 siblings, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
Some code may need direct access to the database reference; this
actually should be preferred over direct access to the objects
directory as the database is a much more abstract concept.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/lib/Repository.java | 7 +++++++
1 files changed, 7 insertions(+), 0 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 e3ac01b..1076fe1 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
@@ -187,6 +187,13 @@ public File getObjectsDirectory() {
}
/**
+ * @return the object database which stores this repository's data.
+ */
+ public ObjectDatabase getObjectDatabase() {
+ return objectDatabase;
+ }
+
+ /**
* @return the configuration of this repository
*/
public RepositoryConfig getConfig() {
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 10/11] Use cached Repository instances when resolving alternates
2009-07-11 20:19 ` [JGIT PATCH 09/11] Expose the Repository's ObjectDatabase object Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 11/11] Send .have lines in ReceivePack for alternate repositories Shawn O. Pearce
0 siblings, 1 reply; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
Using a cached Repository instance when opening an alternate object
database ensures that if multiple repositories share the same base
repository (e.g. 4 forks of the Linux kernel all share the same
base "linus-2.6.git" repository) then the base object directory is
only monitored and tracked once in the JVM, instead of 4 times. In
the case of the WindowCache, this means we will have greater reuse
of the windows from the base, as they won't be loaded 4 different
times under different keys.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../jgit/lib/AlternateRepositoryDatabase.java | 127 ++++++++++++++++++++
.../src/org/spearce/jgit/lib/ObjectDatabase.java | 16 ++-
.../src/org/spearce/jgit/lib/ObjectDirectory.java | 18 +++-
3 files changed, 155 insertions(+), 6 deletions(-)
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/AlternateRepositoryDatabase.java
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/AlternateRepositoryDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/AlternateRepositoryDatabase.java
new file mode 100644
index 0000000..ee4c4cf
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/AlternateRepositoryDatabase.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009, Google Inc.
+ *
+ * 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.lib;
+
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * An ObjectDatabase of another {@link Repository}.
+ * <p>
+ * This {@code ObjectDatabase} wraps around another {@code Repository}'s object
+ * database, providing its contents to the caller, and closing the Repository
+ * when this database is closed. The primary user of this class is
+ * {@link ObjectDirectory}, when the {@code info/alternates} file points at the
+ * {@code objects/} directory of another repository.
+ */
+public final class AlternateRepositoryDatabase extends ObjectDatabase {
+ private final Repository repository;
+
+ private final ObjectDatabase odb;
+
+ /**
+ * @param alt
+ * the alternate repository to wrap and export.
+ */
+ public AlternateRepositoryDatabase(final Repository alt) {
+ repository = alt;
+ odb = repository.getObjectDatabase();
+ }
+
+ /** @return the alternate repository objects are borrowed from. */
+ public Repository getRepository() {
+ return repository;
+ }
+
+ public void closeSelf() {
+ repository.close();
+ }
+
+ public void create() throws IOException {
+ repository.create();
+ }
+
+ public boolean exists() {
+ return odb.exists();
+ }
+
+ @Override
+ protected boolean hasObject1(final AnyObjectId objectId) {
+ return odb.hasObject1(objectId);
+ }
+
+ @Override
+ protected boolean tryAgain1() {
+ return odb.tryAgain1();
+ }
+
+ @Override
+ protected boolean hasObject2(final String objectName) {
+ return odb.hasObject2(objectName);
+ }
+
+ @Override
+ protected ObjectLoader openObject1(final WindowCursor curs,
+ final AnyObjectId objectId) throws IOException {
+ return odb.openObject1(curs, objectId);
+ }
+
+ @Override
+ protected ObjectLoader openObject2(final WindowCursor curs,
+ final String objectName, final AnyObjectId objectId)
+ throws IOException {
+ return odb.openObject2(curs, objectName, objectId);
+ }
+
+ @Override
+ void openObjectInAllPacks1(final Collection<PackedObjectLoader> out,
+ final WindowCursor curs, final AnyObjectId objectId)
+ throws IOException {
+ odb.openObjectInAllPacks1(out, curs, objectId);
+ }
+
+ @Override
+ protected ObjectDatabase[] loadAlternates() throws IOException {
+ return odb.getAlternates();
+ }
+
+ @Override
+ protected void closeAlternates(final ObjectDatabase[] alt) {
+ // Do nothing; these belong to odb to close, not us.
+ }
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectDatabase.java
index 6a9ba4e..a547052 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectDatabase.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectDatabase.java
@@ -108,9 +108,7 @@ public final void closeAlternates() {
ObjectDatabase[] alt = alternates.get();
if (alt != null) {
alternates.set(null);
- for (final ObjectDatabase d : alt) {
- d.close();
- }
+ closeAlternates(alt);
}
}
@@ -364,4 +362,16 @@ protected boolean tryAgain1() {
protected ObjectDatabase[] loadAlternates() throws IOException {
return NO_ALTERNATES;
}
+
+ /**
+ * Close the list of alternates returned by {@link #loadAlternates()}.
+ *
+ * @param alt
+ * the alternate list, from {@link #loadAlternates()}.
+ */
+ protected void closeAlternates(ObjectDatabase[] alt) {
+ for (final ObjectDatabase d : alt) {
+ d.close();
+ }
+ }
}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectDirectory.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectDirectory.java
index d3c43da..7cc459c 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectDirectory.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectDirectory.java
@@ -52,6 +52,7 @@
import java.util.concurrent.atomic.AtomicReference;
import org.spearce.jgit.errors.PackMismatchException;
+import org.spearce.jgit.lib.RepositoryCache.FileKey;
import org.spearce.jgit.util.FS;
/**
@@ -415,11 +416,11 @@ public boolean accept(final File baseDir, final String n) {
@Override
protected ObjectDatabase[] loadAlternates() throws IOException {
final BufferedReader br = open(alternatesFile);
- final List<ObjectDirectory> l = new ArrayList<ObjectDirectory>(4);
+ final List<ObjectDatabase> l = new ArrayList<ObjectDatabase>(4);
try {
String line;
while ((line = br.readLine()) != null) {
- l.add(new ObjectDirectory(FS.resolve(objects, line)));
+ l.add(openAlternate(line));
}
} finally {
br.close();
@@ -428,11 +429,22 @@ public boolean accept(final File baseDir, final String n) {
if (l.isEmpty()) {
return NO_ALTERNATES;
}
- return l.toArray(new ObjectDirectory[l.size()]);
+ return l.toArray(new ObjectDatabase[l.size()]);
}
private static BufferedReader open(final File f)
throws FileNotFoundException {
return new BufferedReader(new FileReader(f));
}
+
+ private ObjectDatabase openAlternate(final String location)
+ throws IOException {
+ final File objdir = FS.resolve(objects, location);
+ final File parent = objdir.getParentFile();
+ if (FileKey.isGitRepository(parent)) {
+ final Repository db = RepositoryCache.open(FileKey.exact(parent));
+ return new AlternateRepositoryDatabase(db);
+ }
+ return new ObjectDirectory(objdir);
+ }
}
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [JGIT PATCH 11/11] Send .have lines in ReceivePack for alternate repositories
2009-07-11 20:19 ` [JGIT PATCH 10/11] Use cached Repository instances when resolving alternates Shawn O. Pearce
@ 2009-07-11 20:19 ` Shawn O. Pearce
0 siblings, 0 replies; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-11 20:19 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
If we are borrowing objects from an alternate repository we should
send the alternates' refs, but under the magical ".have" name, so
that a push client won't transmit objects that our alternate already
contains for us. To save bandwidth during the advertisement phase
of the connection any duplicates are skipped. This way we don't
send a ".have" if we have already sent that same object name under
a different ref, or under a prior have line.
In 5d3a6706eef2 we introduced the client side support for this,
and now we introduce the corresponding server side support.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../org/spearce/jgit/transport/ReceivePack.java | 1 +
.../org/spearce/jgit/transport/RefAdvertiser.java | 19 +++++++++++++++++++
2 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
index 1c490af..472e0b5 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
@@ -514,6 +514,7 @@ private void sendAdvertisedRefs() throws IOException {
adv.send(refs.values());
if (head != null && head.getName() == head.getOrigName())
adv.advertiseHave(head.getObjectId());
+ adv.includeAdditionalHaves();
if (adv.isEmpty())
adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
pckOut.end();
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
index 91700da..1f6adc5 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RefAdvertiser.java
@@ -42,10 +42,13 @@
import java.util.LinkedHashSet;
import java.util.Set;
+import org.spearce.jgit.lib.AlternateRepositoryDatabase;
import org.spearce.jgit.lib.AnyObjectId;
import org.spearce.jgit.lib.Constants;
+import org.spearce.jgit.lib.ObjectDatabase;
import org.spearce.jgit.lib.Ref;
import org.spearce.jgit.lib.RefComparator;
+import org.spearce.jgit.lib.Repository;
import org.spearce.jgit.revwalk.RevFlag;
import org.spearce.jgit.revwalk.RevObject;
import org.spearce.jgit.revwalk.RevTag;
@@ -104,6 +107,22 @@ void advertiseHave(AnyObjectId id) throws IOException {
}
}
+ void includeAdditionalHaves() throws IOException {
+ additionalHaves(walk.getRepository().getObjectDatabase());
+ }
+
+ private void additionalHaves(final ObjectDatabase db) throws IOException {
+ if (db instanceof AlternateRepositoryDatabase)
+ additionalHaves(((AlternateRepositoryDatabase) db).getRepository());
+ for (ObjectDatabase alt : db.getAlternates())
+ additionalHaves(alt);
+ }
+
+ private void additionalHaves(final Repository alt) throws IOException {
+ for (final Ref r : alt.getAllRefs().values())
+ advertiseHave(r.getObjectId());
+ }
+
boolean isEmpty() {
return first;
}
--
1.6.4.rc0.117.g28cb
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack
2009-07-11 20:19 ` [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 06/11] Add a use reference counter to Repository Shawn O. Pearce
@ 2009-07-21 15:22 ` Robin Rosenberg
2009-07-21 15:28 ` Shawn O. Pearce
1 sibling, 1 reply; 14+ messages in thread
From: Robin Rosenberg @ 2009-07-21 15:22 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: git
lördag 11 juli 2009 22:19:20 skrev "Shawn O. Pearce" <spearce@spearce.org>:
> The HEAD ref cannot be pushed to by a client, if it is a detached HEAD
> the client shouldn't be permitted to change it, if it is a symref to
> another ref then the client should update the destination ref and not
> the symref. Instead offer the HEAD ref as a ".have" line, which is an
> invalid ref that the client can't update but still lets the client know
> we have its target object reachable.
>
> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
> ---
> .../org/spearce/jgit/transport/ReceivePack.java | 5 ++++-
> .../org/spearce/jgit/transport/RefAdvertiser.java | 15 +++++++++++++++
> 2 files changed, 19 insertions(+), 1 deletions(-)
>
> diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
> index fd8aa86..1c490af 100644
> --- a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
> +++ b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
> @@ -509,8 +509,11 @@ private void sendAdvertisedRefs() throws IOException {
> adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
> if (allowOfsDelta)
> adv.advertiseCapability(CAPABILITY_OFS_DELTA);
> - refs = db.getAllRefs();
> + refs = new HashMap<String, Ref>(db.getAllRefs());
> + final Ref head = refs.remove(Constants.HEAD);
> adv.send(refs.values());
> + if (head != null && head.getName() == head.getOrigName())
> + adv.advertiseHave(head.getObjectId());
This relies on an implicit guarantee that == works here. Would equals cost
too much? Or perhaps we should document this guarantee as part of the
interface.
-- robin
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack
2009-07-21 15:22 ` [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack Robin Rosenberg
@ 2009-07-21 15:28 ` Shawn O. Pearce
0 siblings, 0 replies; 14+ messages in thread
From: Shawn O. Pearce @ 2009-07-21 15:28 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
> l?rdag 11 juli 2009 22:19:20 skrev "Shawn O. Pearce" <spearce@spearce.org>:
> > diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
> > index fd8aa86..1c490af 100644
> > --- a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
> > +++ b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
> > @@ -509,8 +509,11 @@ private void sendAdvertisedRefs() throws IOException {
> > adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
> > if (allowOfsDelta)
> > adv.advertiseCapability(CAPABILITY_OFS_DELTA);
> > - refs = db.getAllRefs();
> > + refs = new HashMap<String, Ref>(db.getAllRefs());
> > + final Ref head = refs.remove(Constants.HEAD);
> > adv.send(refs.values());
> > + if (head != null && head.getName() == head.getOrigName())
> > + adv.advertiseHave(head.getObjectId());
>
> This relies on an implicit guarantee that == works here. Would equals cost
> too much? Or perhaps we should document this guarantee as part of the
> interface.
Ouch, good point, .equals would be better here. Can you amend?
If its the only issue in the series just amend it to be .equals(),
if there are other things for me to fix I'll be happy to send an
updated patch.
--
Shawn.
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2009-07-21 15:28 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-07-11 20:19 [JGIT PATCH 00/11] Repository instance caching Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 01/11] Change Daemon to use concurrent collections for exported repositories Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 02/11] Make Daemon's exportAll check not require synchronization Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 03/11] Don't retry ".git" suffix in daemon if already tried Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 04/11] Refactor ref advertisement code from server implementations Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 06/11] Add a use reference counter to Repository Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 07/11] Introduce RepositoryCache to cache handles of Repository objects Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 08/11] Change Daemon to use RepositoryCache Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 09/11] Expose the Repository's ObjectDatabase object Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 10/11] Use cached Repository instances when resolving alternates Shawn O. Pearce
2009-07-11 20:19 ` [JGIT PATCH 11/11] Send .have lines in ReceivePack for alternate repositories Shawn O. Pearce
2009-07-21 15:22 ` [JGIT PATCH 05/11] Don't advertise HEAD from ReceivePack Robin Rosenberg
2009-07-21 15:28 ` 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).