Speed up push by hiding refs/changes/

No user (even site administrators) is permitted to make modifications
to the refs/changes/ namespace during push.  This restriction
prevents commits needed to remember patch sets of a change from
being deleted or replaced after the review has already started.

The primary reason the git receive-pack server advertises references
to the send-pack client is to let it know what references the client
can change, and allow the client to detect if the change should
require the --force flag.  The secondary reason is to help the
client compute a common ancestor with the server, and reduce the
amount of objects it must upload.  Either way the client doesn't
need the list of all commits submitted for review, it can function
perfectly fine without the refs/changes/ names.

For some really busy repositories, this can save megabytes worth
of text that needs to be sent from server to client each time the
client tries to push a change for review.

Change-Id: I4815adcdb58b8fea24b77b6b5e92a0d073dddb0f
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce 2010-12-10 10:00:45 -08:00
parent c30dec3a9a
commit 2edc2b3d06
6 changed files with 54 additions and 5 deletions

View File

@ -198,7 +198,7 @@ public class ProjectServlet extends GitServlet {
UploadPack up = new UploadPack(repo); UploadPack up = new UploadPack(repo);
up.setPackConfig(packConfig); up.setPackConfig(packConfig);
if (!pc.allRefsAreVisible()) { if (!pc.allRefsAreVisible()) {
up.setRefFilter(new VisibleRefFilter(repo, pc, db.get())); up.setRefFilter(new VisibleRefFilter(repo, pc, db.get(), true));
} }
return up; return up;
} }

View File

@ -296,7 +296,7 @@ class PushOp implements ProjectRunnable {
return Collections.emptyList(); return Collections.emptyList();
} }
try { try {
local = new VisibleRefFilter(db, pc, meta).filter(local); local = new VisibleRefFilter(db, pc, meta, true).filter(local);
} finally { } finally {
meta.close(); meta.close();
} }

View File

@ -208,8 +208,9 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
if (!projectControl.allRefsAreVisible()) { if (!projectControl.allRefsAreVisible()) {
rp.setCheckReferencedObjectsAreReachable(true); rp.setCheckReferencedObjectsAreReachable(true);
rp.setRefFilter(new VisibleRefFilter(repo, projectControl, db)); rp.setRefFilter(new VisibleRefFilter(repo, projectControl, db, false));
} }
rp.setRefFilter(new ReceiveCommitsRefFilter(rp.getRefFilter()));
rp.setPreReceiveHook(this); rp.setPreReceiveHook(this);
rp.setPostReceiveHook(this); rp.setPostReceiveHook(this);

View File

@ -0,0 +1,41 @@
// Copyright (C) 2010 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.git;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.RefFilter;
import java.util.HashMap;
import java.util.Map;
/** Exposes only the non refs/changes/ reference names. */
class ReceiveCommitsRefFilter implements RefFilter {
private final RefFilter base;
public ReceiveCommitsRefFilter(RefFilter base) {
this.base = base != null ? base : RefFilter.DEFAULT;
}
@Override
public Map<String, Ref> filter(Map<String, Ref> refs) {
HashMap<String, Ref> r = new HashMap<String, Ref>();
for (Map.Entry<String, Ref> e : refs.entrySet()) {
if (!e.getKey().startsWith("refs/changes/")) {
r.put(e.getKey(), e.getValue());
}
}
return base.filter(r);
}
}

View File

@ -52,12 +52,15 @@ public class VisibleRefFilter implements RefFilter {
private final Repository db; private final Repository db;
private final ProjectControl projectCtl; private final ProjectControl projectCtl;
private final ReviewDb reviewDb; private final ReviewDb reviewDb;
private final boolean showChanges;
public VisibleRefFilter(final Repository db, public VisibleRefFilter(final Repository db,
final ProjectControl projectControl, final ReviewDb reviewDb) { final ProjectControl projectControl, final ReviewDb reviewDb,
final boolean showChanges) {
this.db = db; this.db = db;
this.projectCtl = projectControl; this.projectCtl = projectControl;
this.reviewDb = reviewDb; this.reviewDb = reviewDb;
this.showChanges = showChanges;
} }
@Override @Override
@ -99,6 +102,10 @@ public class VisibleRefFilter implements RefFilter {
} }
private Set<Change.Id> visibleChanges() { private Set<Change.Id> visibleChanges() {
if (!showChanges) {
return Collections.emptySet();
}
final Project project = projectCtl.getProject(); final Project project = projectCtl.getProject();
try { try {
final Set<Change.Id> visibleChanges = new HashSet<Change.Id>(); final Set<Change.Id> visibleChanges = new HashSet<Change.Id>();

View File

@ -42,7 +42,7 @@ final class Upload extends AbstractGitCommand {
final UploadPack up = new UploadPack(repo); final UploadPack up = new UploadPack(repo);
if (!projectControl.allRefsAreVisible()) { if (!projectControl.allRefsAreVisible()) {
up.setRefFilter(new VisibleRefFilter(repo, projectControl, db.get())); up.setRefFilter(new VisibleRefFilter(repo, projectControl, db.get(), true));
} }
up.setPackConfig(config.getPackConfig()); up.setPackConfig(config.getPackConfig());
up.setTimeout(config.getTimeout()); up.setTimeout(config.getTimeout());