/*
 * Copyright (C) 2008-2010, Google Inc.
 * and other copyright owners as documented in the project's IP log.
 *
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Distribution License v1.0 which
 * accompanies this distribution, is reproduced below, and is
 * available at http://www.eclipse.org/org/documents/edl-v10.php
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.
 *
 * - Neither the name of the Eclipse Foundation, Inc. nor the
 *   names of its contributors may be used to endorse or promote
 *   products derived from this software without specific prior
 *   written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.eclipse.jgit.transport;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_DELETE_REFS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_OPTIONS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.InvalidObjectIdException;
import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.errors.TooLargePackException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.PackLock;
import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException;
import org.eclipse.jgit.internal.transport.parser.FirstCommand;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.GitmoduleEntry;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectDatabase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdSubclassMap;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PacketLineIn.InputOverLimitIOException;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.util.io.InterruptTimer;
import org.eclipse.jgit.util.io.LimitedInputStream;
import org.eclipse.jgit.util.io.TimeoutInputStream;
import org.eclipse.jgit.util.io.TimeoutOutputStream;

Base implementation of the side of a push connection that receives objects.

Contains high-level operations for initializing and closing streams, advertising refs, reading commands, and receiving and applying a pack. Subclasses compose these operations into full service implementations.

/** * Base implementation of the side of a push connection that receives objects. * <p> * Contains high-level operations for initializing and closing streams, * advertising refs, reading commands, and receiving and applying a pack. * Subclasses compose these operations into full service implementations. */
public abstract class BaseReceivePack {
Data in the first line of a request, the line itself plus capabilities.
Deprecated:Use FirstCommand instead.
/** * Data in the first line of a request, the line itself plus capabilities. * * @deprecated Use {@link FirstCommand} instead. */
@Deprecated public static class FirstLine { private final FirstCommand command;
Parse the first line of a receive-pack request.
Params:
  • line – line from the client.
/** * Parse the first line of a receive-pack request. * * @param line * line from the client. */
public FirstLine(String line) { command = FirstCommand.fromLine(line); }
Returns:non-capabilities part of the line.
/** @return non-capabilities part of the line. */
public String getLine() { return command.getLine(); }
Returns:capabilities parsed from the line.
/** @return capabilities parsed from the line. */
public Set<String> getCapabilities() { return command.getCapabilities(); } }
Database we write the stored objects into.
/** Database we write the stored objects into. */
final Repository db;
Revision traversal support over db.
/** Revision traversal support over {@link #db}. */
final RevWalk walk;
Is the client connection a bi-directional socket or pipe?

If true, this class assumes it can perform multiple read and write cycles with the client over the input and output streams. This matches the functionality available with a standard TCP/IP connection, or a local operating system or in-memory pipe.

If false, this class runs in a read everything then output results mode, making it suitable for single round-trip systems RPCs such as HTTP.

/** * Is the client connection a bi-directional socket or pipe? * <p> * If true, this class assumes it can perform multiple read and write cycles * with the client over the input and output streams. This matches the * functionality available with a standard TCP/IP connection, or a local * operating system or in-memory pipe. * <p> * If false, this class runs in a read everything then output results mode, * making it suitable for single round-trip systems RPCs such as HTTP. */
private boolean biDirectionalPipe = true;
Expecting data after the pack footer
/** Expecting data after the pack footer */
private boolean expectDataAfterPackFooter;
Should an incoming transfer validate objects?
/** Should an incoming transfer validate objects? */
private ObjectChecker objectChecker;
Should an incoming transfer permit create requests?
/** Should an incoming transfer permit create requests? */
private boolean allowCreates;
Should an incoming transfer permit delete requests?
/** Should an incoming transfer permit delete requests? */
private boolean allowAnyDeletes; private boolean allowBranchDeletes;
Should an incoming transfer permit non-fast-forward requests?
/** Should an incoming transfer permit non-fast-forward requests? */
private boolean allowNonFastForwards;
Should an incoming transfer permit push options?
/** Should an incoming transfer permit push options? **/
private boolean allowPushOptions;
Should the requested ref updates be performed as a single atomic transaction?
/** * Should the requested ref updates be performed as a single atomic * transaction? */
private boolean atomic; private boolean allowOfsDelta; private boolean allowQuiet = true;
Identity to record action as within the reflog.
/** Identity to record action as within the reflog. */
private PersonIdent refLogIdent;
Hook used while advertising the refs to the client.
/** Hook used while advertising the refs to the client. */
private AdvertiseRefsHook advertiseRefsHook;
Filter used while advertising the refs to the client.
/** Filter used while advertising the refs to the client. */
RefFilter refFilter;
Timeout in seconds to wait for client interaction.
/** Timeout in seconds to wait for client interaction. */
private int timeout;
Timer to manage timeout.
/** Timer to manage {@link #timeout}. */
private InterruptTimer timer; private TimeoutInputStream timeoutIn; // Original stream passed to init(), since rawOut may be wrapped in a // sideband. private OutputStream origOut;
Raw input stream.
/** Raw input stream. */
protected InputStream rawIn;
Raw output stream.
/** Raw output stream. */
protected OutputStream rawOut;
Optional message output stream.
/** Optional message output stream. */
protected OutputStream msgOut; private SideBandOutputStream errOut;
Packet line input stream around rawIn.
/** Packet line input stream around {@link #rawIn}. */
protected PacketLineIn pckIn;
Packet line output stream around rawOut.
/** Packet line output stream around {@link #rawOut}. */
protected PacketLineOut pckOut; private final MessageOutputWrapper msgOutWrapper = new MessageOutputWrapper(); private PackParser parser;
The refs we advertised as existing at the start of the connection.
/** The refs we advertised as existing at the start of the connection. */
Map<String, Ref> refs;
All SHA-1s shown to the client, which can be possible edges.
/** All SHA-1s shown to the client, which can be possible edges. */
Set<ObjectId> advertisedHaves;
Capabilities requested by the client.
/** Capabilities requested by the client. */
private Set<String> enabledCapabilities; String userAgent; private Set<ObjectId> clientShallowCommits; private List<ReceiveCommand> commands; private long maxCommandBytes; private long maxDiscardBytes; private StringBuilder advertiseError; /** If {@link BasePackPushConnection#CAPABILITY_SIDE_BAND_64K} is enabled. */ private boolean sideBand; private boolean quiet;
Lock around the received pack file, while updating refs.
/** Lock around the received pack file, while updating refs. */
private PackLock packLock; private boolean checkReferencedIsReachable;
Git object size limit
/** Git object size limit */
private long maxObjectSizeLimit;
Total pack size limit
/** Total pack size limit */
private long maxPackSizeLimit = -1;
The size of the received pack, including index size
/** The size of the received pack, including index size */
private Long packSize; private PushCertificateParser pushCertificateParser; private SignedPushConfig signedPushConfig; PushCertificate pushCert; private ReceivedPackStatistics stats;
Get the push certificate used to verify the pusher's identity.

Only valid after commands are read from the wire.

Returns:the parsed certificate, or null if push certificates are disabled or no cert was presented by the client.
Since:4.1
Deprecated:use ReceivePack.getPushCertificate.
/** * Get the push certificate used to verify the pusher's identity. * <p> * Only valid after commands are read from the wire. * * @return the parsed certificate, or null if push certificates are disabled * or no cert was presented by the client. * @since 4.1 * @deprecated use {@link ReceivePack#getPushCertificate}. */
@Deprecated public abstract PushCertificate getPushCertificate();
Set the push certificate used to verify the pusher's identity.

Should only be called if reconstructing an instance without going through the normal recvCommands() flow.

Params:
  • cert – the push certificate to set.
Since:4.1
Deprecated:use ReceivePack.setPushCertificate(PushCertificate).
/** * Set the push certificate used to verify the pusher's identity. * <p> * Should only be called if reconstructing an instance without going through * the normal {@link #recvCommands()} flow. * * @param cert * the push certificate to set. * @since 4.1 * @deprecated use {@link ReceivePack#setPushCertificate(PushCertificate)}. */
@Deprecated public abstract void setPushCertificate(PushCertificate cert);
Create a new pack receive for an open repository.
Params:
  • into – the destination repository.
/** * Create a new pack receive for an open repository. * * @param into * the destination repository. */
protected BaseReceivePack(Repository into) { db = into; walk = new RevWalk(db); walk.setRetainBody(false); TransferConfig tc = db.getConfig().get(TransferConfig.KEY); objectChecker = tc.newReceiveObjectChecker(); ReceiveConfig rc = db.getConfig().get(ReceiveConfig::new); allowCreates = rc.allowCreates; allowAnyDeletes = true; allowBranchDeletes = rc.allowDeletes; allowNonFastForwards = rc.allowNonFastForwards; allowOfsDelta = rc.allowOfsDelta; allowPushOptions = rc.allowPushOptions; maxCommandBytes = rc.maxCommandBytes; maxDiscardBytes = rc.maxDiscardBytes; advertiseRefsHook = AdvertiseRefsHook.DEFAULT; refFilter = RefFilter.DEFAULT; advertisedHaves = new HashSet<>(); clientShallowCommits = new HashSet<>(); signedPushConfig = rc.signedPush; }
Configuration for receive operations.
/** Configuration for receive operations. */
protected static class ReceiveConfig { final boolean allowCreates; final boolean allowDeletes; final boolean allowNonFastForwards; final boolean allowOfsDelta; final boolean allowPushOptions; final long maxCommandBytes; final long maxDiscardBytes; final SignedPushConfig signedPush; ReceiveConfig(Config config) { allowCreates = true; allowDeletes = !config.getBoolean("receive", "denydeletes", false); //$NON-NLS-1$ //$NON-NLS-2$ allowNonFastForwards = !config.getBoolean("receive", //$NON-NLS-1$ "denynonfastforwards", false); //$NON-NLS-1$ allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset", //$NON-NLS-1$ //$NON-NLS-2$ true); allowPushOptions = config.getBoolean("receive", "pushoptions", //$NON-NLS-1$ //$NON-NLS-2$ false); maxCommandBytes = config.getLong("receive", //$NON-NLS-1$ "maxCommandBytes", //$NON-NLS-1$ 3 << 20); maxDiscardBytes = config.getLong("receive", //$NON-NLS-1$ "maxCommandDiscardBytes", //$NON-NLS-1$ -1); signedPush = SignedPushConfig.KEY.parse(config); } }
Output stream that wraps the current BaseReceivePack.msgOut.

We don't want to expose BaseReceivePack.msgOut directly because it can change several times over the course of a session.

/** * Output stream that wraps the current {@link #msgOut}. * <p> * We don't want to expose {@link #msgOut} directly because it can change * several times over the course of a session. */
class MessageOutputWrapper extends OutputStream { @Override public void write(int ch) { if (msgOut != null) { try { msgOut.write(ch); } catch (IOException e) { // Ignore write failures. } } } @Override public void write(byte[] b, int off, int len) { if (msgOut != null) { try { msgOut.write(b, off, len); } catch (IOException e) { // Ignore write failures. } } } @Override public void write(byte[] b) { write(b, 0, b.length); } @Override public void flush() { if (msgOut != null) { try { msgOut.flush(); } catch (IOException e) { // Ignore write failures. } } } }
Get the process name used for pack lock messages.
Returns:the process name used for pack lock messages.
/** * Get the process name used for pack lock messages. * * @return the process name used for pack lock messages. */
protected abstract String getLockMessageProcessName();
Get the repository this receive completes into.
Returns:the repository this receive completes into.
Deprecated:use ReceivePack.getRepository
/** * Get the repository this receive completes into. * * @return the repository this receive completes into. * @deprecated use {@link ReceivePack#getRepository} */
@Deprecated public abstract Repository getRepository();
Get the RevWalk instance used by this connection.
Returns:the RevWalk instance used by this connection.
Deprecated:use ReceivePack.getRevWalk
/** * Get the RevWalk instance used by this connection. * * @return the RevWalk instance used by this connection. * @deprecated use {@link ReceivePack#getRevWalk} */
@Deprecated public abstract RevWalk getRevWalk();
Get refs which were advertised to the client.
Returns:all refs which were advertised to the client, or null if setAdvertisedRefs(Map<String,Ref>, Set<ObjectId>) has not been called yet.
Deprecated:use ReceivePack.getAdvertisedRefs
/** * Get refs which were advertised to the client. * * @return all refs which were advertised to the client, or null if * {@link #setAdvertisedRefs(Map, Set)} has not been called yet. * @deprecated use {@link ReceivePack#getAdvertisedRefs} */
@Deprecated public abstract Map<String, Ref> getAdvertisedRefs();
Set the refs advertised by this ReceivePack.

Intended to be called from a PreReceiveHook.

Params:
  • allRefs – explicit set of references to claim as advertised by this ReceivePack instance. This overrides any references that may exist in the source repository. The map is passed to the configured getRefFilter(). If null, assumes all refs were advertised.
  • additionalHaves – explicit set of additional haves to claim as advertised. If null, assumes the default set of additional haves from the repository.
Deprecated:use ReceivePack.setAdvertisedRefs
/** * Set the refs advertised by this ReceivePack. * <p> * Intended to be called from a * {@link org.eclipse.jgit.transport.PreReceiveHook}. * * @param allRefs * explicit set of references to claim as advertised by this * ReceivePack instance. This overrides any references that may * exist in the source repository. The map is passed to the * configured {@link #getRefFilter()}. If null, assumes all refs * were advertised. * @param additionalHaves * explicit set of additional haves to claim as advertised. If * null, assumes the default set of additional haves from the * repository. * @deprecated use {@link ReceivePack#setAdvertisedRefs} */
@Deprecated public abstract void setAdvertisedRefs(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves);
Get objects advertised to the client.
Returns:the set of objects advertised to the as present in this repository, or null if setAdvertisedRefs(Map<String,Ref>, Set<ObjectId>) has not been called yet.
/** * Get objects advertised to the client. * * @return the set of objects advertised to the as present in this repository, * or null if {@link #setAdvertisedRefs(Map, Set)} has not been called * yet. */
public final Set<ObjectId> getAdvertisedObjects() { return advertisedHaves; }
Whether this instance will validate all referenced, but not supplied by the client, objects are reachable from another reference.
Returns:true if this instance will validate all referenced, but not supplied by the client, objects are reachable from another reference.
/** * Whether this instance will validate all referenced, but not supplied by * the client, objects are reachable from another reference. * * @return true if this instance will validate all referenced, but not * supplied by the client, objects are reachable from another * reference. */
public boolean isCheckReferencedObjectsAreReachable() { return checkReferencedIsReachable; }
Validate all referenced but not supplied objects are reachable.

If enabled, this instance will verify that references to objects not contained within the received pack are already reachable through at least one other reference displayed as part of getAdvertisedRefs().

This feature is useful when the application doesn't trust the client to not provide a forged SHA-1 reference to an object, in an attempt to access parts of the DAG that they aren't allowed to see and which have been hidden from them via the configured AdvertiseRefsHook or RefFilter.

Enabling this feature may imply at least some, if not all, of the same functionality performed by setCheckReceivedObjects(boolean). Applications are encouraged to enable both features, if desired.

Params:
  • b – true to enable the additional check.
/** * Validate all referenced but not supplied objects are reachable. * <p> * If enabled, this instance will verify that references to objects not * contained within the received pack are already reachable through at least * one other reference displayed as part of {@link #getAdvertisedRefs()}. * <p> * This feature is useful when the application doesn't trust the client to * not provide a forged SHA-1 reference to an object, in an attempt to * access parts of the DAG that they aren't allowed to see and which have * been hidden from them via the configured * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} or * {@link org.eclipse.jgit.transport.RefFilter}. * <p> * Enabling this feature may imply at least some, if not all, of the same * functionality performed by {@link #setCheckReceivedObjects(boolean)}. * Applications are encouraged to enable both features, if desired. * * @param b * {@code true} to enable the additional check. */
public void setCheckReferencedObjectsAreReachable(boolean b) { this.checkReferencedIsReachable = b; }
Whether this class expects a bi-directional pipe opened between the client and itself.
Returns:true if this class expects a bi-directional pipe opened between the client and itself. The default is true.
/** * Whether this class expects a bi-directional pipe opened between the * client and itself. * * @return true if this class expects a bi-directional pipe opened between * the client and itself. The default is true. */
public boolean isBiDirectionalPipe() { return biDirectionalPipe; }
Whether this class will assume the socket is a fully bidirectional pipe between the two peers and takes advantage of that by first transmitting the known refs, then waiting to read commands.
Params:
  • twoWay – if true, this class will assume the socket is a fully bidirectional pipe between the two peers and takes advantage of that by first transmitting the known refs, then waiting to read commands. If false, this class assumes it must read the commands before writing output and does not perform the initial advertising.
/** * Whether this class will assume the socket is a fully bidirectional pipe * between the two peers and takes advantage of that by first transmitting * the known refs, then waiting to read commands. * * @param twoWay * if true, this class will assume the socket is a fully * bidirectional pipe between the two peers and takes advantage * of that by first transmitting the known refs, then waiting to * read commands. If false, this class assumes it must read the * commands before writing output and does not perform the * initial advertising. */
public void setBiDirectionalPipe(boolean twoWay) { biDirectionalPipe = twoWay; }
Whether there is data expected after the pack footer.
Returns:true if there is data expected after the pack footer.
/** * Whether there is data expected after the pack footer. * * @return {@code true} if there is data expected after the pack footer. */
public boolean isExpectDataAfterPackFooter() { return expectDataAfterPackFooter; }
Whether there is additional data in InputStream after pack.
Params:
  • e – true if there is additional data in InputStream after pack.
/** * Whether there is additional data in InputStream after pack. * * @param e * {@code true} if there is additional data in InputStream after * pack. */
public void setExpectDataAfterPackFooter(boolean e) { expectDataAfterPackFooter = e; }
Whether this instance will verify received objects are formatted correctly.
Returns:true if this instance will verify received objects are formatted correctly. Validating objects requires more CPU time on this side of the connection.
/** * Whether this instance will verify received objects are formatted * correctly. * * @return {@code true} if this instance will verify received objects are * formatted correctly. Validating objects requires more CPU time on * this side of the connection. */
public boolean isCheckReceivedObjects() { return objectChecker != null; }
Whether to enable checking received objects
Params:
  • check – true to enable checking received objects; false to assume all received objects are valid.
See Also:
/** * Whether to enable checking received objects * * @param check * {@code true} to enable checking received objects; false to * assume all received objects are valid. * @see #setObjectChecker(ObjectChecker) */
public void setCheckReceivedObjects(boolean check) { if (check && objectChecker == null) setObjectChecker(new ObjectChecker()); else if (!check && objectChecker != null) setObjectChecker(null); }
Set the object checking instance to verify each received object with
Params:
  • impl – if non-null the object checking instance to verify each received object with; null to disable object checking.
Since:3.4
/** * Set the object checking instance to verify each received object with * * @param impl * if non-null the object checking instance to verify each * received object with; null to disable object checking. * @since 3.4 */
public void setObjectChecker(ObjectChecker impl) { objectChecker = impl; }
Whether the client can request refs to be created.
Returns:true if the client can request refs to be created.
/** * Whether the client can request refs to be created. * * @return {@code true} if the client can request refs to be created. */
public boolean isAllowCreates() { return allowCreates; }
Whether to permit create ref commands to be processed.
Params:
  • canCreate – true to permit create ref commands to be processed.
/** * Whether to permit create ref commands to be processed. * * @param canCreate * {@code true} to permit create ref commands to be processed. */
public void setAllowCreates(boolean canCreate) { allowCreates = canCreate; }
Whether the client can request refs to be deleted.
Returns:true if the client can request refs to be deleted.
/** * Whether the client can request refs to be deleted. * * @return {@code true} if the client can request refs to be deleted. */
public boolean isAllowDeletes() { return allowAnyDeletes; }
Whether to permit delete ref commands to be processed.
Params:
  • canDelete – true to permit delete ref commands to be processed.
/** * Whether to permit delete ref commands to be processed. * * @param canDelete * {@code true} to permit delete ref commands to be processed. */
public void setAllowDeletes(boolean canDelete) { allowAnyDeletes = canDelete; }
Whether the client can delete from refs/heads/.
Returns:true if the client can delete from refs/heads/.
Since:3.6
/** * Whether the client can delete from {@code refs/heads/}. * * @return {@code true} if the client can delete from {@code refs/heads/}. * @since 3.6 */
public boolean isAllowBranchDeletes() { return allowBranchDeletes; }
Configure whether to permit deletion of branches from the refs/heads/ namespace.
Params:
  • canDelete – true to permit deletion of branches from the refs/heads/ namespace.
Since:3.6
/** * Configure whether to permit deletion of branches from the * {@code refs/heads/} namespace. * * @param canDelete * {@code true} to permit deletion of branches from the * {@code refs/heads/} namespace. * @since 3.6 */
public void setAllowBranchDeletes(boolean canDelete) { allowBranchDeletes = canDelete; }
Whether the client can request non-fast-forward updates of a ref, possibly making objects unreachable.
Returns:true if the client can request non-fast-forward updates of a ref, possibly making objects unreachable.
/** * Whether the client can request non-fast-forward updates of a ref, * possibly making objects unreachable. * * @return {@code true} if the client can request non-fast-forward updates * of a ref, possibly making objects unreachable. */
public boolean isAllowNonFastForwards() { return allowNonFastForwards; }
Configure whether to permit the client to ask for non-fast-forward updates of an existing ref.
Params:
  • canRewind – true to permit the client to ask for non-fast-forward updates of an existing ref.
/** * Configure whether to permit the client to ask for non-fast-forward * updates of an existing ref. * * @param canRewind * {@code true} to permit the client to ask for non-fast-forward * updates of an existing ref. */
public void setAllowNonFastForwards(boolean canRewind) { allowNonFastForwards = canRewind; }
Whether the client's commands should be performed as a single atomic transaction.
Returns:true if the client's commands should be performed as a single atomic transaction.
Since:4.4
/** * Whether the client's commands should be performed as a single atomic * transaction. * * @return {@code true} if the client's commands should be performed as a * single atomic transaction. * @since 4.4 */
public boolean isAtomic() { return atomic; }
Configure whether to perform the client's commands as a single atomic transaction.
Params:
  • atomic – true to perform the client's commands as a single atomic transaction.
Since:4.4
/** * Configure whether to perform the client's commands as a single atomic * transaction. * * @param atomic * {@code true} to perform the client's commands as a single * atomic transaction. * @since 4.4 */
public void setAtomic(boolean atomic) { this.atomic = atomic; }
Get identity of the user making the changes in the reflog.
Returns:identity of the user making the changes in the reflog.
/** * Get identity of the user making the changes in the reflog. * * @return identity of the user making the changes in the reflog. */
public PersonIdent getRefLogIdent() { return refLogIdent; }
Set the identity of the user appearing in the affected reflogs.

The timestamp portion of the identity is ignored. A new identity with the current timestamp will be created automatically when the updates occur and the log records are written.

Params:
  • pi – identity of the user. If null the identity will be automatically determined based on the repository configuration.
/** * Set the identity of the user appearing in the affected reflogs. * <p> * The timestamp portion of the identity is ignored. A new identity with the * current timestamp will be created automatically when the updates occur * and the log records are written. * * @param pi * identity of the user. If null the identity will be * automatically determined based on the repository * configuration. */
public void setRefLogIdent(PersonIdent pi) { refLogIdent = pi; }
Get the hook used while advertising the refs to the client
Returns:the hook used while advertising the refs to the client
/** * Get the hook used while advertising the refs to the client * * @return the hook used while advertising the refs to the client */
public AdvertiseRefsHook getAdvertiseRefsHook() { return advertiseRefsHook; }
Get the filter used while advertising the refs to the client
Returns:the filter used while advertising the refs to the client
/** * Get the filter used while advertising the refs to the client * * @return the filter used while advertising the refs to the client */
public RefFilter getRefFilter() { return refFilter; }
Set the hook used while advertising the refs to the client.

If the AdvertiseRefsHook chooses to call setAdvertisedRefs(Map<String,Ref>, Set<ObjectId>), only refs set by this hook and selected by the RefFilter will be shown to the client. Clients may still attempt to create or update a reference not advertised by the configured AdvertiseRefsHook. These attempts should be rejected by a matching PreReceiveHook.

Params:
  • advertiseRefsHook – the hook; may be null to show all refs.
/** * Set the hook used while advertising the refs to the client. * <p> * If the {@link org.eclipse.jgit.transport.AdvertiseRefsHook} chooses to * call {@link #setAdvertisedRefs(Map,Set)}, only refs set by this hook * <em>and</em> selected by the {@link org.eclipse.jgit.transport.RefFilter} * will be shown to the client. Clients may still attempt to create or * update a reference not advertised by the configured * {@link org.eclipse.jgit.transport.AdvertiseRefsHook}. These attempts * should be rejected by a matching * {@link org.eclipse.jgit.transport.PreReceiveHook}. * * @param advertiseRefsHook * the hook; may be null to show all refs. */
public void setAdvertiseRefsHook(AdvertiseRefsHook advertiseRefsHook) { if (advertiseRefsHook != null) this.advertiseRefsHook = advertiseRefsHook; else this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT; }
Set the filter used while advertising the refs to the client.

Only refs allowed by this filter will be shown to the client. The filter is run against the refs specified by the AdvertiseRefsHook (if applicable).

Params:
  • refFilter – the filter; may be null to show all refs.
/** * Set the filter used while advertising the refs to the client. * <p> * Only refs allowed by this filter will be shown to the client. The filter * is run against the refs specified by the * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} (if applicable). * * @param refFilter * the filter; may be null to show all refs. */
public void setRefFilter(RefFilter refFilter) { this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT; }
Get timeout (in seconds) before aborting an IO operation.
Returns:timeout (in seconds) before aborting an IO operation.
/** * Get timeout (in seconds) before aborting an IO operation. * * @return timeout (in seconds) before aborting an IO operation. */
public int getTimeout() { return timeout; }
Set the timeout before willing to abort an IO call.
Params:
  • seconds – number of seconds to wait (with no data transfer occurring) before aborting an IO read or write operation with the connected client.
/** * Set the timeout before willing to abort an IO call. * * @param seconds * number of seconds to wait (with no data transfer occurring) * before aborting an IO read or write operation with the * connected client. */
public void setTimeout(int seconds) { timeout = seconds; }
Set the maximum number of command bytes to read from the client.
Params:
  • limit – command limit in bytes; if 0 there is no limit.
Since:4.7
/** * Set the maximum number of command bytes to read from the client. * * @param limit * command limit in bytes; if 0 there is no limit. * @since 4.7 */
public void setMaxCommandBytes(long limit) { maxCommandBytes = limit; }
Set the maximum number of command bytes to discard from the client.

Discarding remaining bytes allows this instance to consume the rest of the command block and send a human readable over-limit error via the side-band channel. If the client sends an excessive number of bytes this limit kicks in and the instance disconnects, resulting in a non-specific 'pipe closed', 'end of stream', or similar generic error at the client.

When the limit is set to -1 the implementation will default to the larger of 3 * maxCommandBytes or 3 MiB.

Params:
  • limit – discard limit in bytes; if 0 there is no limit; if -1 the implementation tries to set a reasonable default.
Since:4.7
/** * Set the maximum number of command bytes to discard from the client. * <p> * Discarding remaining bytes allows this instance to consume the rest of * the command block and send a human readable over-limit error via the * side-band channel. If the client sends an excessive number of bytes this * limit kicks in and the instance disconnects, resulting in a non-specific * 'pipe closed', 'end of stream', or similar generic error at the client. * <p> * When the limit is set to {@code -1} the implementation will default to * the larger of {@code 3 * maxCommandBytes} or {@code 3 MiB}. * * @param limit * discard limit in bytes; if 0 there is no limit; if -1 the * implementation tries to set a reasonable default. * @since 4.7 */
public void setMaxCommandDiscardBytes(long limit) { maxDiscardBytes = limit; }
Set the maximum allowed Git object size.

If an object is larger than the given size the pack-parsing will throw an exception aborting the receive-pack operation.

Params:
  • limit – the Git object size limit. If zero then there is not limit.
/** * Set the maximum allowed Git object size. * <p> * If an object is larger than the given size the pack-parsing will throw an * exception aborting the receive-pack operation. * * @param limit * the Git object size limit. If zero then there is not limit. */
public void setMaxObjectSizeLimit(long limit) { maxObjectSizeLimit = limit; }
Set the maximum allowed pack size.

A pack exceeding this size will be rejected.

Params:
  • limit – the pack size limit, in bytes
Since:3.3
/** * Set the maximum allowed pack size. * <p> * A pack exceeding this size will be rejected. * * @param limit * the pack size limit, in bytes * @since 3.3 */
public void setMaxPackSizeLimit(long limit) { if (limit < 0) throw new IllegalArgumentException(MessageFormat.format( JGitText.get().receivePackInvalidLimit, Long.valueOf(limit))); maxPackSizeLimit = limit; }
Check whether the client expects a side-band stream.
Throws:
  • RequestNotYetReadException – if the client's request has not yet been read from the wire, so we do not know if they expect side-band. Note that the client may have already written the request, it just has not been read.
Returns:true if the client has advertised a side-band capability, false otherwise.
/** * Check whether the client expects a side-band stream. * * @return true if the client has advertised a side-band capability, false * otherwise. * @throws org.eclipse.jgit.transport.RequestNotYetReadException * if the client's request has not yet been read from the wire, so * we do not know if they expect side-band. Note that the client * may have already written the request, it just has not been * read. */
public boolean isSideBand() throws RequestNotYetReadException { checkRequestWasRead(); return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K); }
Whether clients may request avoiding noisy progress messages.
Returns:true if clients may request avoiding noisy progress messages.
Since:4.0
/** * Whether clients may request avoiding noisy progress messages. * * @return true if clients may request avoiding noisy progress messages. * @since 4.0 */
public boolean isAllowQuiet() { return allowQuiet; }
Configure if clients may request the server skip noisy messages.
Params:
  • allow – true to allow clients to request quiet behavior; false to refuse quiet behavior and send messages anyway. This may be necessary if processing is slow and the client-server network connection can timeout.
Since:4.0
/** * Configure if clients may request the server skip noisy messages. * * @param allow * true to allow clients to request quiet behavior; false to * refuse quiet behavior and send messages anyway. This may be * necessary if processing is slow and the client-server network * connection can timeout. * @since 4.0 */
public void setAllowQuiet(boolean allow) { allowQuiet = allow; }
Whether the server supports receiving push options.
Returns:true if the server supports receiving push options.
Since:4.5
/** * Whether the server supports receiving push options. * * @return true if the server supports receiving push options. * @since 4.5 */
public boolean isAllowPushOptions() { return allowPushOptions; }
Configure if the server supports receiving push options.
Params:
  • allow – true to optionally accept option strings from the client.
Since:4.5
/** * Configure if the server supports receiving push options. * * @param allow * true to optionally accept option strings from the client. * @since 4.5 */
public void setAllowPushOptions(boolean allow) { allowPushOptions = allow; }
True if the client wants less verbose output.
Throws:
  • RequestNotYetReadException – if the client's request has not yet been read from the wire, so we do not know if they expect side-band. Note that the client may have already written the request, it just has not been read.
Returns:true if the client has requested the server to be less verbose.
Since:4.0
/** * True if the client wants less verbose output. * * @return true if the client has requested the server to be less verbose. * @throws org.eclipse.jgit.transport.RequestNotYetReadException * if the client's request has not yet been read from the wire, * so we do not know if they expect side-band. Note that the * client may have already written the request, it just has not * been read. * @since 4.0 */
public boolean isQuiet() throws RequestNotYetReadException { checkRequestWasRead(); return quiet; }
Set the configuration for push certificate verification.
Params:
Since:4.1
/** * Set the configuration for push certificate verification. * * @param cfg * new configuration; if this object is null or its {@link * SignedPushConfig#getCertNonceSeed()} is null, push certificate * verification will be disabled. * @since 4.1 */
public void setSignedPushConfig(SignedPushConfig cfg) { signedPushConfig = cfg; } private PushCertificateParser getPushCertificateParser() { if (pushCertificateParser == null) { pushCertificateParser = new PushCertificateParser(db, signedPushConfig); } return pushCertificateParser; }
Get the user agent of the client.

If the client is new enough to use agent= capability that value will be returned. Older HTTP clients may also supply their version using the HTTP User-Agent header. The capability overrides the HTTP header if both are available.

When an HTTP request has been received this method returns the HTTP User-Agent header value until capabilities have been parsed.

Returns:user agent supplied by the client. Available only if the client is new enough to advertise its user agent.
Since:4.0
/** * Get the user agent of the client. * <p> * If the client is new enough to use {@code agent=} capability that value * will be returned. Older HTTP clients may also supply their version using * the HTTP {@code User-Agent} header. The capability overrides the HTTP * header if both are available. * <p> * When an HTTP request has been received this method returns the HTTP * {@code User-Agent} header value until capabilities have been parsed. * * @return user agent supplied by the client. Available only if the client * is new enough to advertise its user agent. * @since 4.0 */
public String getPeerUserAgent() { return UserAgent.getAgent(enabledCapabilities, userAgent); }
Get all of the command received by the current request.
Returns:all of the command received by the current request.
/** * Get all of the command received by the current request. * * @return all of the command received by the current request. */
public List<ReceiveCommand> getAllCommands() { return Collections.unmodifiableList(commands); }
Send an error message to the client.

If any error messages are sent before the references are advertised to the client, the errors will be sent instead of the advertisement and the receive operation will be aborted. All clients should receive and display such early stage errors.

If the reference advertisements have already been sent, messages are sent in a side channel. If the client doesn't support receiving messages, the message will be discarded, with no other indication to the caller or to the client.

PreReceiveHooks should always try to use ReceiveCommand.setResult(Result, String) with a result status of Result.REJECTED_OTHER_REASON to indicate any reasons for rejecting an update. Messages attached to a command are much more likely to be returned to the client.

Params:
  • what – string describing the problem identified by the hook. The string must not end with an LF, and must not contain an LF.
/** * Send an error message to the client. * <p> * If any error messages are sent before the references are advertised to * the client, the errors will be sent instead of the advertisement and the * receive operation will be aborted. All clients should receive and display * such early stage errors. * <p> * If the reference advertisements have already been sent, messages are sent * in a side channel. If the client doesn't support receiving messages, the * message will be discarded, with no other indication to the caller or to * the client. * <p> * {@link org.eclipse.jgit.transport.PreReceiveHook}s should always try to * use * {@link org.eclipse.jgit.transport.ReceiveCommand#setResult(Result, String)} * with a result status of * {@link org.eclipse.jgit.transport.ReceiveCommand.Result#REJECTED_OTHER_REASON} * to indicate any reasons for rejecting an update. Messages attached to a * command are much more likely to be returned to the client. * * @param what * string describing the problem identified by the hook. The * string must not end with an LF, and must not contain an LF. */
public void sendError(String what) { if (refs == null) { if (advertiseError == null) advertiseError = new StringBuilder(); advertiseError.append(what).append('\n'); } else { msgOutWrapper.write(Constants.encode("error: " + what + "\n")); //$NON-NLS-1$ //$NON-NLS-2$ } } private void fatalError(String msg) { if (errOut != null) { try { errOut.write(Constants.encode(msg)); errOut.flush(); } catch (IOException e) { // Ignore write failures } } else { sendError(msg); } }
Send a message to the client, if it supports receiving them.

If the client doesn't support receiving messages, the message will be discarded, with no other indication to the caller or to the client.

Params:
  • what – string describing the problem identified by the hook. The string must not end with an LF, and must not contain an LF.
/** * Send a message to the client, if it supports receiving them. * <p> * If the client doesn't support receiving messages, the message will be * discarded, with no other indication to the caller or to the client. * * @param what * string describing the problem identified by the hook. The * string must not end with an LF, and must not contain an LF. */
public void sendMessage(String what) { msgOutWrapper.write(Constants.encode(what + "\n")); //$NON-NLS-1$ }
Get an underlying stream for sending messages to the client.
Returns:an underlying stream for sending messages to the client.
/** * Get an underlying stream for sending messages to the client. * * @return an underlying stream for sending messages to the client. */
public OutputStream getMessageOutputStream() { return msgOutWrapper; }
Get the size of the received pack file including the index size. This can only be called if the pack is already received.
Throws:
Returns:the size of the received pack including index size
Since:3.3
/** * Get the size of the received pack file including the index size. * * This can only be called if the pack is already received. * * @return the size of the received pack including index size * @throws java.lang.IllegalStateException * if called before the pack has been received * @since 3.3 */
public long getPackSize() { if (packSize != null) return packSize.longValue(); throw new IllegalStateException(JGitText.get().packSizeNotSetYet); }
Get the commits from the client's shallow file.
Returns:if the client is a shallow repository, the list of edge commits that define the client's shallow boundary. Empty set if the client is earlier than Git 1.9, or is a full clone.
Since:3.5
/** * Get the commits from the client's shallow file. * * @return if the client is a shallow repository, the list of edge commits * that define the client's shallow boundary. Empty set if the client * is earlier than Git 1.9, or is a full clone. * @since 3.5 */
protected Set<ObjectId> getClientShallowCommits() { return clientShallowCommits; }
Whether any commands to be executed have been read.
Returns:true if any commands to be executed have been read.
/** * Whether any commands to be executed have been read. * * @return {@code true} if any commands to be executed have been read. */
protected boolean hasCommands() { return !commands.isEmpty(); }
Whether an error occurred that should be advertised.
Returns:true if an error occurred that should be advertised.
/** * Whether an error occurred that should be advertised. * * @return true if an error occurred that should be advertised. */
protected boolean hasError() { return advertiseError != null; }
Initialize the instance with the given streams.
Params:
  • input – raw input to read client commands and pack data from. Caller must ensure the input is buffered, otherwise read performance may suffer.
  • output – response back to the Git network client. Caller must ensure the output is buffered, otherwise write performance may suffer.
  • messages – secondary "notice" channel to send additional messages out through. When run over SSH this should be tied back to the standard error channel of the command execution. For most other network connections this should be null.
/** * Initialize the instance with the given streams. * * @param input * raw input to read client commands and pack data from. Caller * must ensure the input is buffered, otherwise read performance * may suffer. * @param output * response back to the Git network client. Caller must ensure * the output is buffered, otherwise write performance may * suffer. * @param messages * secondary "notice" channel to send additional messages out * through. When run over SSH this should be tied back to the * standard error channel of the command execution. For most * other network connections this should be null. */
protected void init(final InputStream input, final OutputStream output, final OutputStream messages) { origOut = output; rawIn = input; rawOut = output; msgOut = messages; if (timeout > 0) { final Thread caller = Thread.currentThread(); timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$ timeoutIn = new TimeoutInputStream(rawIn, timer); TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer); timeoutIn.setTimeout(timeout * 1000); o.setTimeout(timeout * 1000); rawIn = timeoutIn; rawOut = o; } pckIn = new PacketLineIn(rawIn); pckOut = new PacketLineOut(rawOut); pckOut.setFlushOnEnd(false); enabledCapabilities = new HashSet<>(); commands = new ArrayList<>(); }
Get advertised refs, or the default if not explicitly advertised.
Returns:advertised refs, or the default if not explicitly advertised.
/** * Get advertised refs, or the default if not explicitly advertised. * * @return advertised refs, or the default if not explicitly advertised. */
protected Map<String, Ref> getAdvertisedOrDefaultRefs() { if (refs == null) setAdvertisedRefs(null, null); return refs; }
Receive a pack from the stream and check connectivity if necessary.
Throws:
  • IOException – an error occurred during unpacking or connectivity checking.
/** * Receive a pack from the stream and check connectivity if necessary. * * @throws java.io.IOException * an error occurred during unpacking or connectivity checking. */
protected void receivePackAndCheckConnectivity() throws IOException { receivePack(); if (needCheckConnectivity()) { checkSubmodules(); checkConnectivity(); } parser = null; }
Unlock the pack written by this object.
Throws:
  • IOException – the pack could not be unlocked.
/** * Unlock the pack written by this object. * * @throws java.io.IOException * the pack could not be unlocked. */
protected void unlockPack() throws IOException { if (packLock != null) { packLock.unlock(); packLock = null; } }
Generate an advertisement of available refs and capabilities.
Params:
  • adv – the advertisement formatter.
Throws:
/** * Generate an advertisement of available refs and capabilities. * * @param adv * the advertisement formatter. * @throws java.io.IOException * the formatter failed to write an advertisement. * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException * the hook denied advertisement. */
public void sendAdvertisedRefs(RefAdvertiser adv) throws IOException, ServiceMayNotContinueException { if (advertiseError != null) { adv.writeOne("ERR " + advertiseError); //$NON-NLS-1$ return; } try { advertiseRefsHook.advertiseRefs(this); } catch (ServiceMayNotContinueException fail) { if (fail.getMessage() != null) { adv.writeOne("ERR " + fail.getMessage()); //$NON-NLS-1$ fail.setOutput(); } throw fail; } adv.init(db); adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K); adv.advertiseCapability(CAPABILITY_DELETE_REFS); adv.advertiseCapability(CAPABILITY_REPORT_STATUS); if (allowQuiet) adv.advertiseCapability(CAPABILITY_QUIET); String nonce = getPushCertificateParser().getAdvertiseNonce(); if (nonce != null) { adv.advertiseCapability(nonce); } if (db.getRefDatabase().performsAtomicTransactions()) adv.advertiseCapability(CAPABILITY_ATOMIC); if (allowOfsDelta) adv.advertiseCapability(CAPABILITY_OFS_DELTA); if (allowPushOptions) { adv.advertiseCapability(CAPABILITY_PUSH_OPTIONS); } adv.advertiseCapability(OPTION_AGENT, UserAgent.get()); adv.send(getAdvertisedOrDefaultRefs().values()); for (ObjectId obj : advertisedHaves) adv.advertiseHave(obj); if (adv.isEmpty()) adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$ adv.end(); }
Returns the statistics on the received pack if available. This should be called after receivePack is called.
Returns:ReceivedPackStatistics
Since:4.6
/** * Returns the statistics on the received pack if available. This should be * called after {@link #receivePack} is called. * * @return ReceivedPackStatistics * @since 4.6 */
@Nullable public ReceivedPackStatistics getReceivedPackStatistics() { return stats; }
Receive a list of commands from the input.
Throws:
  • IOException –
/** * Receive a list of commands from the input. * * @throws java.io.IOException */
protected void recvCommands() throws IOException { PacketLineIn pck = maxCommandBytes > 0 ? new PacketLineIn(rawIn, maxCommandBytes) : pckIn; PushCertificateParser certParser = getPushCertificateParser(); boolean firstPkt = true; try { for (;;) { String line; try { line = pck.readString(); } catch (EOFException eof) { if (commands.isEmpty()) return; throw eof; } if (PacketLineIn.isEnd(line)) { break; } if (line.length() >= 48 && line.startsWith("shallow ")) { //$NON-NLS-1$ parseShallow(line.substring(8, 48)); continue; } if (firstPkt) { firstPkt = false; FirstCommand firstLine = FirstCommand.fromLine(line); enabledCapabilities = firstLine.getCapabilities(); line = firstLine.getLine(); enableCapabilities(); if (line.equals(GitProtocolConstants.OPTION_PUSH_CERT)) { certParser.receiveHeader(pck, !isBiDirectionalPipe()); continue; } } if (line.equals(PushCertificateParser.BEGIN_SIGNATURE)) { certParser.receiveSignature(pck); continue; } ReceiveCommand cmd = parseCommand(line); if (cmd.getRefName().equals(Constants.HEAD)) { cmd.setResult(Result.REJECTED_CURRENT_BRANCH); } else { cmd.setRef(refs.get(cmd.getRefName())); } commands.add(cmd); if (certParser.enabled()) { certParser.addCommand(cmd); } } pushCert = certParser.build(); if (hasCommands()) { readPostCommands(pck); } } catch (PackProtocolException e) { discardCommands(); fatalError(e.getMessage()); throw e; } catch (InputOverLimitIOException e) { String msg = JGitText.get().tooManyCommands; discardCommands(); fatalError(msg); throw new PackProtocolException(msg); } } private void discardCommands() { if (sideBand) { long max = maxDiscardBytes; if (max < 0) { max = Math.max(3 * maxCommandBytes, 3L << 20); } try { new PacketLineIn(rawIn, max).discardUntilEnd(); } catch (IOException e) { // Ignore read failures attempting to discard. } } } private void parseShallow(String idStr) throws PackProtocolException { ObjectId id; try { id = ObjectId.fromString(idStr); } catch (InvalidObjectIdException e) { throw new PackProtocolException(e.getMessage(), e); } clientShallowCommits.add(id); } static ReceiveCommand parseCommand(String line) throws PackProtocolException { if (line == null || line.length() < 83) { throw new PackProtocolException( JGitText.get().errorInvalidProtocolWantedOldNewRef); } String oldStr = line.substring(0, 40); String newStr = line.substring(41, 81); ObjectId oldId, newId; try { oldId = ObjectId.fromString(oldStr); newId = ObjectId.fromString(newStr); } catch (InvalidObjectIdException e) { throw new PackProtocolException( JGitText.get().errorInvalidProtocolWantedOldNewRef, e); } String name = line.substring(82); if (!Repository.isValidRefName(name)) { throw new PackProtocolException( JGitText.get().errorInvalidProtocolWantedOldNewRef); } return new ReceiveCommand(oldId, newId, name); }
Params:
  • in – request stream.
Throws:
/** * @param in * request stream. * @throws IOException * request line cannot be read. */
void readPostCommands(PacketLineIn in) throws IOException { // Do nothing by default. }
Enable capabilities based on a previously read capabilities line.
/** * Enable capabilities based on a previously read capabilities line. */
protected void enableCapabilities() { sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K); quiet = allowQuiet && isCapabilityEnabled(CAPABILITY_QUIET); if (sideBand) { OutputStream out = rawOut; rawOut = new SideBandOutputStream(CH_DATA, MAX_BUF, out); msgOut = new SideBandOutputStream(CH_PROGRESS, MAX_BUF, out); errOut = new SideBandOutputStream(CH_ERROR, MAX_BUF, out); pckOut = new PacketLineOut(rawOut); pckOut.setFlushOnEnd(false); } }
Check if the peer requested a capability.
Params:
  • name – protocol name identifying the capability.
Returns:true if the peer requested the capability to be enabled.
/** * Check if the peer requested a capability. * * @param name * protocol name identifying the capability. * @return true if the peer requested the capability to be enabled. */
protected boolean isCapabilityEnabled(String name) { return enabledCapabilities.contains(name); } void checkRequestWasRead() { if (enabledCapabilities == null) throw new RequestNotYetReadException(); }
Whether a pack is expected based on the list of commands.
Returns:true if a pack is expected based on the list of commands.
/** * Whether a pack is expected based on the list of commands. * * @return {@code true} if a pack is expected based on the list of commands. */
protected boolean needPack() { for (ReceiveCommand cmd : commands) { if (cmd.getType() != ReceiveCommand.Type.DELETE) return true; } return false; }
Receive a pack from the input and store it in the repository.
Throws:
  • IOException – an error occurred reading or indexing the pack.
/** * Receive a pack from the input and store it in the repository. * * @throws IOException * an error occurred reading or indexing the pack. */
private void receivePack() throws IOException { // It might take the client a while to pack the objects it needs // to send to us. We should increase our timeout so we don't // abort while the client is computing. // if (timeoutIn != null) timeoutIn.setTimeout(10 * timeout * 1000); ProgressMonitor receiving = NullProgressMonitor.INSTANCE; ProgressMonitor resolving = NullProgressMonitor.INSTANCE; if (sideBand && !quiet) resolving = new SideBandProgressMonitor(msgOut); try (ObjectInserter ins = db.newObjectInserter()) { String lockMsg = "jgit receive-pack"; //$NON-NLS-1$ if (getRefLogIdent() != null) lockMsg += " from " + getRefLogIdent().toExternalString(); //$NON-NLS-1$ parser = ins.newPackParser(packInputStream()); parser.setAllowThin(true); parser.setNeedNewObjectIds(checkReferencedIsReachable); parser.setNeedBaseObjectIds(checkReferencedIsReachable); parser.setCheckEofAfterPackFooter(!biDirectionalPipe && !isExpectDataAfterPackFooter()); parser.setExpectDataAfterPackFooter(isExpectDataAfterPackFooter()); parser.setObjectChecker(objectChecker); parser.setLockMessage(lockMsg); parser.setMaxObjectSizeLimit(maxObjectSizeLimit); packLock = parser.parse(receiving, resolving); packSize = Long.valueOf(parser.getPackSize()); stats = parser.getReceivedPackStatistics(); ins.flush(); } if (timeoutIn != null) timeoutIn.setTimeout(timeout * 1000); } private InputStream packInputStream() { InputStream packIn = rawIn; if (maxPackSizeLimit >= 0) { packIn = new LimitedInputStream(packIn, maxPackSizeLimit) { @Override protected void limitExceeded() throws TooLargePackException { throw new TooLargePackException(limit); } }; } return packIn; } private boolean needCheckConnectivity() { return isCheckReceivedObjects() || isCheckReferencedObjectsAreReachable() || !getClientShallowCommits().isEmpty(); } private void checkSubmodules() throws IOException { ObjectDatabase odb = db.getObjectDatabase(); if (objectChecker == null) { return; } for (GitmoduleEntry entry : objectChecker.getGitsubmodules()) { AnyObjectId blobId = entry.getBlobId(); ObjectLoader blob = odb.open(blobId, Constants.OBJ_BLOB); try { SubmoduleValidator.assertValidGitModulesFile( new String(blob.getBytes(), UTF_8)); } catch (LargeObjectException | SubmoduleValidationException e) { throw new IOException(e); } } } private void checkConnectivity() throws IOException { ObjectIdSubclassMap<ObjectId> baseObjects = null; ObjectIdSubclassMap<ObjectId> providedObjects = null; ProgressMonitor checking = NullProgressMonitor.INSTANCE; if (sideBand && !quiet) { SideBandProgressMonitor m = new SideBandProgressMonitor(msgOut); m.setDelayStart(750, TimeUnit.MILLISECONDS); checking = m; } if (checkReferencedIsReachable) { baseObjects = parser.getBaseObjectIds(); providedObjects = parser.getNewObjectIds(); } parser = null; try (ObjectWalk ow = new ObjectWalk(db)) { if (baseObjects != null) { ow.sort(RevSort.TOPO); if (!baseObjects.isEmpty()) ow.sort(RevSort.BOUNDARY, true); } for (ReceiveCommand cmd : commands) { if (cmd.getResult() != Result.NOT_ATTEMPTED) continue; if (cmd.getType() == ReceiveCommand.Type.DELETE) continue; ow.markStart(ow.parseAny(cmd.getNewId())); } for (ObjectId have : advertisedHaves) { RevObject o = ow.parseAny(have); ow.markUninteresting(o); if (baseObjects != null && !baseObjects.isEmpty()) { o = ow.peel(o); if (o instanceof RevCommit) o = ((RevCommit) o).getTree(); if (o instanceof RevTree) ow.markUninteresting(o); } } checking.beginTask(JGitText.get().countingObjects, ProgressMonitor.UNKNOWN); RevCommit c; while ((c = ow.next()) != null) { checking.update(1); if (providedObjects != null // && !c.has(RevFlag.UNINTERESTING) // && !providedObjects.contains(c)) throw new MissingObjectException(c, Constants.TYPE_COMMIT); } RevObject o; while ((o = ow.nextObject()) != null) { checking.update(1); if (o.has(RevFlag.UNINTERESTING)) continue; if (providedObjects != null) { if (providedObjects.contains(o)) continue; else throw new MissingObjectException(o, o.getType()); } if (o instanceof RevBlob && !db.getObjectDatabase().has(o)) throw new MissingObjectException(o, Constants.TYPE_BLOB); } checking.endTask(); if (baseObjects != null) { for (ObjectId id : baseObjects) { o = ow.parseAny(id); if (!o.has(RevFlag.UNINTERESTING)) throw new MissingObjectException(o, o.getType()); } } } }
Validate the command list.
/** * Validate the command list. */
protected void validateCommands() { for (ReceiveCommand cmd : commands) { final Ref ref = cmd.getRef(); if (cmd.getResult() != Result.NOT_ATTEMPTED) continue; if (cmd.getType() == ReceiveCommand.Type.DELETE) { if (!isAllowDeletes()) { // Deletes are not supported on this repository. cmd.setResult(Result.REJECTED_NODELETE); continue; } if (!isAllowBranchDeletes() && ref.getName().startsWith(Constants.R_HEADS)) { // Branches cannot be deleted, but other refs can. cmd.setResult(Result.REJECTED_NODELETE); continue; } } if (cmd.getType() == ReceiveCommand.Type.CREATE) { if (!isAllowCreates()) { cmd.setResult(Result.REJECTED_NOCREATE); continue; } if (ref != null && !isAllowNonFastForwards()) { // Creation over an existing ref is certainly not going // to be a fast-forward update. We can reject it early. // cmd.setResult(Result.REJECTED_NONFASTFORWARD); continue; } if (ref != null) { // A well behaved client shouldn't have sent us a // create command for a ref we advertised to it. // cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().refAlreadyExists); continue; } } if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null) { ObjectId id = ref.getObjectId(); if (id == null) { id = ObjectId.zeroId(); } if (!ObjectId.zeroId().equals(cmd.getOldId()) && !id.equals(cmd.getOldId())) { // Delete commands can be sent with the old id matching our // advertised value, *OR* with the old id being 0{40}. Any // other requested old id is invalid. // cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().invalidOldIdSent); continue; } } if (cmd.getType() == ReceiveCommand.Type.UPDATE) { if (ref == null) { // The ref must have been advertised in order to be updated. // cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().noSuchRef); continue; } ObjectId id = ref.getObjectId(); if (id == null) { // We cannot update unborn branch cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().cannotUpdateUnbornBranch); continue; } if (!id.equals(cmd.getOldId())) { // A properly functioning client will send the same // object id we advertised. // cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().invalidOldIdSent); continue; } // Is this possibly a non-fast-forward style update? // RevObject oldObj, newObj; try { oldObj = walk.parseAny(cmd.getOldId()); } catch (IOException e) { cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd .getOldId().name()); continue; } try { newObj = walk.parseAny(cmd.getNewId()); } catch (IOException e) { cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd .getNewId().name()); continue; } if (oldObj instanceof RevCommit && newObj instanceof RevCommit) { try { if (walk.isMergedInto((RevCommit) oldObj, (RevCommit) newObj)) cmd.setTypeFastForwardUpdate(); else cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD); } catch (MissingObjectException e) { cmd.setResult(Result.REJECTED_MISSING_OBJECT, e .getMessage()); } catch (IOException e) { cmd.setResult(Result.REJECTED_OTHER_REASON); } } else { cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD); } if (cmd.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD && !isAllowNonFastForwards()) { cmd.setResult(Result.REJECTED_NONFASTFORWARD); continue; } } if (!cmd.getRefName().startsWith(Constants.R_REFS) || !Repository.isValidRefName(cmd.getRefName())) { cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().funnyRefname); } } }
Whether any commands have been rejected so far.
Returns:if any commands have been rejected so far.
Since:3.6
/** * Whether any commands have been rejected so far. * * @return if any commands have been rejected so far. * @since 3.6 */
protected boolean anyRejects() { for (ReceiveCommand cmd : commands) { if (cmd.getResult() != Result.NOT_ATTEMPTED && cmd.getResult() != Result.OK) return true; } return false; }
Set the result to fail for any command that was not processed yet.
Since:3.6
/** * Set the result to fail for any command that was not processed yet. * * @since 3.6 */
protected void failPendingCommands() { ReceiveCommand.abort(commands); }
Filter the list of commands according to result.
Params:
  • want – desired status to filter by.
Returns:a copy of the command list containing only those commands with the desired status.
/** * Filter the list of commands according to result. * * @param want * desired status to filter by. * @return a copy of the command list containing only those commands with the * desired status. */
protected List<ReceiveCommand> filterCommands(Result want) { return ReceiveCommand.filter(commands, want); }
Execute commands to update references.
/** * Execute commands to update references. */
protected void executeCommands() { List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED); if (toApply.isEmpty()) return; ProgressMonitor updating = NullProgressMonitor.INSTANCE; if (sideBand) { SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut); pm.setDelayStart(250, TimeUnit.MILLISECONDS); updating = pm; } BatchRefUpdate batch = db.getRefDatabase().newBatchUpdate(); batch.setAllowNonFastForwards(isAllowNonFastForwards()); batch.setAtomic(isAtomic()); batch.setRefLogIdent(getRefLogIdent()); batch.setRefLogMessage("push", true); //$NON-NLS-1$ batch.addCommand(toApply); try { batch.setPushCertificate(getPushCertificate()); batch.execute(walk, updating); } catch (IOException err) { for (ReceiveCommand cmd : toApply) { if (cmd.getResult() == Result.NOT_ATTEMPTED) cmd.reject(err); } } }
Send a status report.
Params:
  • forClient – true if this report is for a Git client, false if it is for an end-user.
  • unpackError – an error that occurred during unpacking, or null
  • out – the reporter for sending the status strings.
Throws:
  • IOException – an error occurred writing the status report.
/** * Send a status report. * * @param forClient * true if this report is for a Git client, false if it is for an * end-user. * @param unpackError * an error that occurred during unpacking, or {@code null} * @param out * the reporter for sending the status strings. * @throws java.io.IOException * an error occurred writing the status report. */
protected void sendStatusReport(final boolean forClient, final Throwable unpackError, final Reporter out) throws IOException { if (unpackError != null) { out.sendString("unpack error " + unpackError.getMessage()); //$NON-NLS-1$ if (forClient) { for (ReceiveCommand cmd : commands) { out.sendString("ng " + cmd.getRefName() //$NON-NLS-1$ + " n/a (unpacker error)"); //$NON-NLS-1$ } } return; } if (forClient) out.sendString("unpack ok"); //$NON-NLS-1$ for (ReceiveCommand cmd : commands) { if (cmd.getResult() == Result.OK) { if (forClient) out.sendString("ok " + cmd.getRefName()); //$NON-NLS-1$ continue; } final StringBuilder r = new StringBuilder(); if (forClient) r.append("ng ").append(cmd.getRefName()).append(" "); //$NON-NLS-1$ //$NON-NLS-2$ else r.append(" ! [rejected] ").append(cmd.getRefName()).append(" ("); //$NON-NLS-1$ //$NON-NLS-2$ switch (cmd.getResult()) { case NOT_ATTEMPTED: r.append("server bug; ref not processed"); //$NON-NLS-1$ break; case REJECTED_NOCREATE: r.append("creation prohibited"); //$NON-NLS-1$ break; case REJECTED_NODELETE: r.append("deletion prohibited"); //$NON-NLS-1$ break; case REJECTED_NONFASTFORWARD: r.append("non-fast forward"); //$NON-NLS-1$ break; case REJECTED_CURRENT_BRANCH: r.append("branch is currently checked out"); //$NON-NLS-1$ break; case REJECTED_MISSING_OBJECT: if (cmd.getMessage() == null) r.append("missing object(s)"); //$NON-NLS-1$ else if (cmd.getMessage().length() == Constants.OBJECT_ID_STRING_LENGTH) { r.append("object "); //$NON-NLS-1$ r.append(cmd.getMessage()); r.append(" missing"); //$NON-NLS-1$ } else r.append(cmd.getMessage()); break; case REJECTED_OTHER_REASON: if (cmd.getMessage() == null) r.append("unspecified reason"); //$NON-NLS-1$ else r.append(cmd.getMessage()); break; case LOCK_FAILURE: r.append("failed to lock"); //$NON-NLS-1$ break; case OK: // We shouldn't have reached this case (see 'ok' case above). continue; } if (!forClient) r.append(")"); //$NON-NLS-1$ out.sendString(r.toString()); } }
Close and flush (if necessary) the underlying streams.
Throws:
  • IOException –
/** * Close and flush (if necessary) the underlying streams. * * @throws java.io.IOException */
protected void close() throws IOException { if (sideBand) { // If we are using side band, we need to send a final // flush-pkt to tell the remote peer the side band is // complete and it should stop decoding. We need to // use the original output stream as rawOut is now the // side band data channel. // ((SideBandOutputStream) msgOut).flushBuffer(); ((SideBandOutputStream) rawOut).flushBuffer(); PacketLineOut plo = new PacketLineOut(origOut); plo.setFlushOnEnd(false); plo.end(); } if (biDirectionalPipe) { // If this was a native git connection, flush the pipe for // the caller. For smart HTTP we don't do this flush and // instead let the higher level HTTP servlet code do it. // if (!sideBand && msgOut != null) msgOut.flush(); rawOut.flush(); } }
Release any resources used by this object.
Throws:
  • IOException – the pack could not be unlocked.
/** * Release any resources used by this object. * * @throws java.io.IOException * the pack could not be unlocked. */
protected void release() throws IOException { walk.close(); unlockPack(); timeoutIn = null; rawIn = null; rawOut = null; msgOut = null; pckIn = null; pckOut = null; refs = null; // Keep the capabilities. If responses are sent after this release // we need to remember at least whether sideband communication has to be // used commands = null; if (timer != null) { try { timer.terminate(); } finally { timer = null; } } }
Interface for reporting status messages.
/** Interface for reporting status messages. */
static abstract class Reporter { abstract void sendString(String s) throws IOException; } }