/*
 * Copyright (C) 2008-2012, Google Inc.
 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0 which is available at
 * https://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package org.eclipse.jgit.lib;

import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
import static java.util.stream.Collectors.toCollection;

import java.io.IOException;
import java.text.MessageFormat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeoutException;

import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushCertificate;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.time.ProposedTimestamp;

Batch of reference updates to be applied to a repository.

The batch update is primarily useful in the transport code, where a client or server is making changes to more than one reference at a time.

/** * Batch of reference updates to be applied to a repository. * <p> * The batch update is primarily useful in the transport code, where a client or * server is making changes to more than one reference at a time. */
public class BatchRefUpdate {
Maximum delay the calling thread will tolerate while waiting for a MonotonicClock to resolve associated ProposedTimestamps.

A default of 5 seconds was chosen by guessing. A common assumption is clock skew between machines on the same LAN using an NTP server also on the same LAN should be under 5 seconds. 5 seconds is also not that long for a large `git push` operation to complete.

Since:4.9
/** * Maximum delay the calling thread will tolerate while waiting for a * {@code MonotonicClock} to resolve associated {@link ProposedTimestamp}s. * <p> * A default of 5 seconds was chosen by guessing. A common assumption is * clock skew between machines on the same LAN using an NTP server also on * the same LAN should be under 5 seconds. 5 seconds is also not that long * for a large `git push` operation to complete. * * @since 4.9 */
protected static final Duration MAX_WAIT = Duration.ofSeconds(5); private final RefDatabase refdb;
Commands to apply during this batch.
/** Commands to apply during this batch. */
private final List<ReceiveCommand> commands;
Does the caller permit a forced update on a reference?
/** Does the caller permit a forced update on a reference? */
private boolean allowNonFastForwards;
Identity to record action as within the reflog.
/** Identity to record action as within the reflog. */
private PersonIdent refLogIdent;
Message the caller wants included in the reflog.
/** Message the caller wants included in the reflog. */
private String refLogMessage;
Should the result value be appended to refLogMessage.
/** Should the result value be appended to {@link #refLogMessage}. */
private boolean refLogIncludeResult;
Should reflogs be written even if the configured default for this ref is not to write it.
/** * Should reflogs be written even if the configured default for this ref is * not to write it. */
private boolean forceRefLog;
Push certificate associated with this update.
/** Push certificate associated with this update. */
private PushCertificate pushCert;
Whether updates should be atomic.
/** Whether updates should be atomic. */
private boolean atomic;
Push options associated with this update.
/** Push options associated with this update. */
private List<String> pushOptions;
Associated timestamps that should be blocked on before update.
/** Associated timestamps that should be blocked on before update. */
private List<ProposedTimestamp> timestamps;
Initialize a new batch update.
Params:
  • refdb – the reference database of the repository to be updated.
/** * Initialize a new batch update. * * @param refdb * the reference database of the repository to be updated. */
protected BatchRefUpdate(RefDatabase refdb) { this.refdb = refdb; this.commands = new ArrayList<>(); this.atomic = refdb.performsAtomicTransactions(); }
Whether the batch update will permit a non-fast-forward update to an existing reference.
Returns:true if the batch update will permit a non-fast-forward update to an existing reference.
/** * Whether the batch update will permit a non-fast-forward update to an * existing reference. * * @return true if the batch update will permit a non-fast-forward update to * an existing reference. */
public boolean isAllowNonFastForwards() { return allowNonFastForwards; }
Set if this update wants to permit a forced update.
Params:
  • allow – true if this update batch should ignore merge tests.
Returns:this.
/** * Set if this update wants to permit a forced update. * * @param allow * true if this update batch should ignore merge tests. * @return {@code this}. */
public BatchRefUpdate setAllowNonFastForwards(boolean allow) { allowNonFastForwards = allow; return this; }
Get identity of the user making the change in the reflog.
Returns:identity of the user making the change in the reflog.
/** * Get identity of the user making the change in the reflog. * * @return identity of the user making the change in the reflog. */
public PersonIdent getRefLogIdent() { return refLogIdent; }
Set the identity of the user appearing in the reflog.

The timestamp portion of the identity is ignored. A new identity with the current timestamp will be created automatically when the update occurs and the log record is written.

Params:
  • pi – identity of the user. If null the identity will be automatically determined based on the repository configuration.
Returns:this.
/** * Set the identity of the user appearing in the reflog. * <p> * The timestamp portion of the identity is ignored. A new identity with the * current timestamp will be created automatically when the update occurs * and the log record is written. * * @param pi * identity of the user. If null the identity will be * automatically determined based on the repository * configuration. * @return {@code this}. */
public BatchRefUpdate setRefLogIdent(PersonIdent pi) { refLogIdent = pi; return this; }
Get the message to include in the reflog.
Returns:message the caller wants to include in the reflog; null if the update should not be logged.
/** * Get the message to include in the reflog. * * @return message the caller wants to include in the reflog; null if the * update should not be logged. */
@Nullable public String getRefLogMessage() { return refLogMessage; }
Check whether the reflog message should include the result of the update, such as fast-forward or force-update.

Describes the default for commands in this batch that do not override it with ReceiveCommand.setRefLogMessage(String, boolean).

Returns:true if the message should include the result.
/** * Check whether the reflog message should include the result of the update, * such as fast-forward or force-update. * <p> * Describes the default for commands in this batch that do not override it * with * {@link org.eclipse.jgit.transport.ReceiveCommand#setRefLogMessage(String, boolean)}. * * @return true if the message should include the result. */
public boolean isRefLogIncludingResult() { return refLogIncludeResult; }
Set the message to include in the reflog.

Repository implementations may limit which reflogs are written by default, based on the project configuration. If a repo is not configured to write logs for this ref by default, setting the message alone may have no effect. To indicate that the repo should write logs for this update in spite of configured defaults, use setForceRefLog(boolean).

Describes the default for commands in this batch that do not override it with ReceiveCommand.setRefLogMessage(String, boolean).

Params:
  • msg – the message to describe this change. If null and appendStatus is false, the reflog will not be updated.
  • appendStatus – true if the status of the ref change (fast-forward or forced-update) should be appended to the user supplied message.
Returns:this.
/** * Set the message to include in the reflog. * <p> * Repository implementations may limit which reflogs are written by * default, based on the project configuration. If a repo is not configured * to write logs for this ref by default, setting the message alone may have * no effect. To indicate that the repo should write logs for this update in * spite of configured defaults, use {@link #setForceRefLog(boolean)}. * <p> * Describes the default for commands in this batch that do not override it * with * {@link org.eclipse.jgit.transport.ReceiveCommand#setRefLogMessage(String, boolean)}. * * @param msg * the message to describe this change. If null and appendStatus * is false, the reflog will not be updated. * @param appendStatus * true if the status of the ref change (fast-forward or * forced-update) should be appended to the user supplied * message. * @return {@code this}. */
public BatchRefUpdate setRefLogMessage(String msg, boolean appendStatus) { if (msg == null && !appendStatus) disableRefLog(); else if (msg == null && appendStatus) { refLogMessage = ""; //$NON-NLS-1$ refLogIncludeResult = true; } else { refLogMessage = msg; refLogIncludeResult = appendStatus; } return this; }
Don't record this update in the ref's associated reflog.

Equivalent to setRefLogMessage(null, false).

Returns:this.
/** * Don't record this update in the ref's associated reflog. * <p> * Equivalent to {@code setRefLogMessage(null, false)}. * * @return {@code this}. */
public BatchRefUpdate disableRefLog() { refLogMessage = null; refLogIncludeResult = false; return this; }
Force writing a reflog for the updated ref.
Params:
  • force – whether to force.
Returns:this
Since:4.9
/** * Force writing a reflog for the updated ref. * * @param force whether to force. * @return {@code this} * @since 4.9 */
public BatchRefUpdate setForceRefLog(boolean force) { forceRefLog = force; return this; }
Check whether log has been disabled by disableRefLog().
Returns:true if disabled.
/** * Check whether log has been disabled by {@link #disableRefLog()}. * * @return true if disabled. */
public boolean isRefLogDisabled() { return refLogMessage == null; }
Check whether the reflog should be written regardless of repo defaults.
Returns:whether force writing is enabled.
Since:4.9
/** * Check whether the reflog should be written regardless of repo defaults. * * @return whether force writing is enabled. * @since 4.9 */
protected boolean isForceRefLog() { return forceRefLog; }
Request that all updates in this batch be performed atomically.

When atomic updates are used, either all commands apply successfully, or none do. Commands that might have otherwise succeeded are rejected with REJECTED_OTHER_REASON.

This method only works if the underlying ref database supports atomic transactions, i.e. RefDatabase.performsAtomicTransactions() returns true. Calling this method with true if the underlying ref database does not support atomic transactions will cause all commands to fail with REJECTED_OTHER_REASON.

Params:
  • atomic – whether updates should be atomic.
Returns:this
Since:4.4
/** * Request that all updates in this batch be performed atomically. * <p> * When atomic updates are used, either all commands apply successfully, or * none do. Commands that might have otherwise succeeded are rejected with * {@code REJECTED_OTHER_REASON}. * <p> * This method only works if the underlying ref database supports atomic * transactions, i.e. * {@link org.eclipse.jgit.lib.RefDatabase#performsAtomicTransactions()} * returns true. Calling this method with true if the underlying ref * database does not support atomic transactions will cause all commands to * fail with {@code * REJECTED_OTHER_REASON}. * * @param atomic * whether updates should be atomic. * @return {@code this} * @since 4.4 */
public BatchRefUpdate setAtomic(boolean atomic) { this.atomic = atomic; return this; }
Whether updates should be atomic.
Returns:atomic whether updates should be atomic.
Since:4.4
/** * Whether updates should be atomic. * * @return atomic whether updates should be atomic. * @since 4.4 */
public boolean isAtomic() { return atomic; }
Set a push certificate associated with this update.

This usually includes commands to update the refs in this batch, but is not required to.

Params:
  • cert – push certificate, may be null.
Since:4.1
/** * Set a push certificate associated with this update. * <p> * This usually includes commands to update the refs in this batch, but is not * required to. * * @param cert * push certificate, may be null. * @since 4.1 */
public void setPushCertificate(PushCertificate cert) { pushCert = cert; }
Set the push certificate associated with this update.

This usually includes commands to update the refs in this batch, but is not required to.

Returns:push certificate, may be null.
Since:4.1
/** * Set the push certificate associated with this update. * <p> * This usually includes commands to update the refs in this batch, but is not * required to. * * @return push certificate, may be null. * @since 4.1 */
protected PushCertificate getPushCertificate() { return pushCert; }
Get commands this update will process.
Returns:commands this update will process.
/** * Get commands this update will process. * * @return commands this update will process. */
public List<ReceiveCommand> getCommands() { return Collections.unmodifiableList(commands); }
Add a single command to this batch update.
Params:
  • cmd – the command to add, must not be null.
Returns:this.
/** * Add a single command to this batch update. * * @param cmd * the command to add, must not be null. * @return {@code this}. */
public BatchRefUpdate addCommand(ReceiveCommand cmd) { commands.add(cmd); return this; }
Add commands to this batch update.
Params:
  • cmd – the commands to add, must not be null.
Returns:this.
/** * Add commands to this batch update. * * @param cmd * the commands to add, must not be null. * @return {@code this}. */
public BatchRefUpdate addCommand(ReceiveCommand... cmd) { return addCommand(Arrays.asList(cmd)); }
Add commands to this batch update.
Params:
  • cmd – the commands to add, must not be null.
Returns:this.
/** * Add commands to this batch update. * * @param cmd * the commands to add, must not be null. * @return {@code this}. */
public BatchRefUpdate addCommand(Collection<ReceiveCommand> cmd) { commands.addAll(cmd); return this; }
Gets the list of option strings associated with this update.
Returns:push options that were passed to execute; prior to calling execute, always returns null.
Since:4.5
/** * Gets the list of option strings associated with this update. * * @return push options that were passed to {@link #execute}; prior to calling * {@link #execute}, always returns null. * @since 4.5 */
@Nullable public List<String> getPushOptions() { return pushOptions; }
Set push options associated with this update.

Implementations must call this at the top of execute(RevWalk, ProgressMonitor, List<String>).

Params:
  • options – options passed to execute.
Since:4.9
/** * Set push options associated with this update. * <p> * Implementations must call this at the top of {@link #execute(RevWalk, * ProgressMonitor, List)}. * * @param options options passed to {@code execute}. * @since 4.9 */
protected void setPushOptions(List<String> options) { pushOptions = options; }
Get list of timestamps the batch must wait for.
Returns:list of timestamps the batch must wait for.
Since:4.6
/** * Get list of timestamps the batch must wait for. * * @return list of timestamps the batch must wait for. * @since 4.6 */
public List<ProposedTimestamp> getProposedTimestamps() { if (timestamps != null) { return Collections.unmodifiableList(timestamps); } return Collections.emptyList(); }
Request the batch to wait for the affected timestamps to resolve.
Params:
Returns:this.
Since:4.6
/** * Request the batch to wait for the affected timestamps to resolve. * * @param ts * a {@link org.eclipse.jgit.util.time.ProposedTimestamp} object. * @return {@code this}. * @since 4.6 */
public BatchRefUpdate addProposedTimestamp(ProposedTimestamp ts) { if (timestamps == null) { timestamps = new ArrayList<>(4); } timestamps.add(ts); return this; }
Execute this batch update.

The default implementation of this method performs a sequential reference update over each reference.

Implementations must respect the atomicity requirements of the underlying database as described in setAtomic(boolean) and RefDatabase.performsAtomicTransactions().

Params:
  • walk – a RevWalk to parse tags in case the storage system wants to store them pre-peeled, a common performance optimization.
  • monitor – progress monitor to receive update status on.
  • options – a list of option strings; set null to execute without
Throws:
  • IOException – the database is unable to accept the update. Individual command status must be tested to determine if there is a partial failure, or a total failure.
Since:4.5
/** * Execute this batch update. * <p> * The default implementation of this method performs a sequential reference * update over each reference. * <p> * Implementations must respect the atomicity requirements of the underlying * database as described in {@link #setAtomic(boolean)} and * {@link org.eclipse.jgit.lib.RefDatabase#performsAtomicTransactions()}. * * @param walk * a RevWalk to parse tags in case the storage system wants to * store them pre-peeled, a common performance optimization. * @param monitor * progress monitor to receive update status on. * @param options * a list of option strings; set null to execute without * @throws java.io.IOException * the database is unable to accept the update. Individual * command status must be tested to determine if there is a * partial failure, or a total failure. * @since 4.5 */
public void execute(RevWalk walk, ProgressMonitor monitor, List<String> options) throws IOException { if (atomic && !refdb.performsAtomicTransactions()) { for (ReceiveCommand c : commands) { if (c.getResult() == NOT_ATTEMPTED) { c.setResult(REJECTED_OTHER_REASON, JGitText.get().atomicRefUpdatesNotSupported); } } return; } if (!blockUntilTimestamps(MAX_WAIT)) { return; } if (options != null) { setPushOptions(options); } monitor.beginTask(JGitText.get().updatingReferences, commands.size()); List<ReceiveCommand> commands2 = new ArrayList<>( commands.size()); // First delete refs. This may free the name space for some of the // updates. for (ReceiveCommand cmd : commands) { try { if (cmd.getResult() == NOT_ATTEMPTED) { if (isMissing(walk, cmd.getOldId()) || isMissing(walk, cmd.getNewId())) { cmd.setResult(ReceiveCommand.Result.REJECTED_MISSING_OBJECT); continue; } cmd.updateType(walk); switch (cmd.getType()) { case CREATE: commands2.add(cmd); break; case UPDATE: case UPDATE_NONFASTFORWARD: commands2.add(cmd); break; case DELETE: RefUpdate rud = newUpdate(cmd); monitor.update(1); cmd.setResult(rud.delete(walk)); } } } catch (IOException err) { cmd.setResult( REJECTED_OTHER_REASON, MessageFormat.format(JGitText.get().lockError, err.getMessage())); } } if (!commands2.isEmpty()) { // What part of the name space is already taken Collection<String> takenNames = refdb.getRefs().stream() .map(Ref::getName) .collect(toCollection(HashSet::new)); Collection<String> takenPrefixes = getTakenPrefixes(takenNames); // Now to the update that may require more room in the name space for (ReceiveCommand cmd : commands2) { try { if (cmd.getResult() == NOT_ATTEMPTED) { cmd.updateType(walk); RefUpdate ru = newUpdate(cmd); SWITCH: switch (cmd.getType()) { case DELETE: // Performed in the first phase break; case UPDATE: case UPDATE_NONFASTFORWARD: RefUpdate ruu = newUpdate(cmd); cmd.setResult(ruu.update(walk)); break; case CREATE: for (String prefix : getPrefixes(cmd.getRefName())) { if (takenNames.contains(prefix)) { cmd.setResult(Result.LOCK_FAILURE); break SWITCH; } } if (takenPrefixes.contains(cmd.getRefName())) { cmd.setResult(Result.LOCK_FAILURE); break SWITCH; } ru.setCheckConflicting(false); takenPrefixes.addAll(getPrefixes(cmd.getRefName())); takenNames.add(cmd.getRefName()); cmd.setResult(ru.update(walk)); } } } catch (IOException err) { cmd.setResult(REJECTED_OTHER_REASON, MessageFormat.format( JGitText.get().lockError, err.getMessage())); } finally { monitor.update(1); } } } monitor.endTask(); } private static boolean isMissing(RevWalk walk, ObjectId id) throws IOException { if (id.equals(ObjectId.zeroId())) { return false; // Explicit add or delete is not missing. } try { walk.parseAny(id); return false; } catch (MissingObjectException e) { return true; } }
Wait for timestamps to be in the past, aborting commands on timeout.
Params:
  • maxWait – maximum amount of time to wait for timestamps to resolve.
Returns:true if timestamps were successfully waited for; false if commands were aborted.
Since:4.6
/** * Wait for timestamps to be in the past, aborting commands on timeout. * * @param maxWait * maximum amount of time to wait for timestamps to resolve. * @return true if timestamps were successfully waited for; false if * commands were aborted. * @since 4.6 */
protected boolean blockUntilTimestamps(Duration maxWait) { if (timestamps == null) { return true; } try { ProposedTimestamp.blockUntil(timestamps, maxWait); return true; } catch (TimeoutException | InterruptedException e) { String msg = JGitText.get().timeIsUncertain; for (ReceiveCommand c : commands) { if (c.getResult() == NOT_ATTEMPTED) { c.setResult(REJECTED_OTHER_REASON, msg); } } return false; } }
Execute this batch update without option strings.
Params:
  • walk – a RevWalk to parse tags in case the storage system wants to store them pre-peeled, a common performance optimization.
  • monitor – progress monitor to receive update status on.
Throws:
  • IOException – the database is unable to accept the update. Individual command status must be tested to determine if there is a partial failure, or a total failure.
/** * Execute this batch update without option strings. * * @param walk * a RevWalk to parse tags in case the storage system wants to * store them pre-peeled, a common performance optimization. * @param monitor * progress monitor to receive update status on. * @throws java.io.IOException * the database is unable to accept the update. Individual * command status must be tested to determine if there is a * partial failure, or a total failure. */
public void execute(RevWalk walk, ProgressMonitor monitor) throws IOException { execute(walk, monitor, null); } private static Collection<String> getTakenPrefixes(Collection<String> names) { Collection<String> ref = new HashSet<>(); for (String name : names) { addPrefixesTo(name, ref); } return ref; }
Get all path prefixes of a ref name.
Params:
  • name – ref name.
Returns:path prefixes of the ref name. For refs/heads/foo, returns refs and refs/heads.
Since:4.9
/** * Get all path prefixes of a ref name. * * @param name * ref name. * @return path prefixes of the ref name. For {@code refs/heads/foo}, returns * {@code refs} and {@code refs/heads}. * @since 4.9 */
protected static Collection<String> getPrefixes(String name) { Collection<String> ret = new HashSet<>(); addPrefixesTo(name, ret); return ret; }
Add prefixes of a ref name to an existing collection.
Params:
  • name – ref name.
  • out – path prefixes of the ref name. For refs/heads/foo, returns refs and refs/heads.
Since:4.9
/** * Add prefixes of a ref name to an existing collection. * * @param name * ref name. * @param out * path prefixes of the ref name. For {@code refs/heads/foo}, * returns {@code refs} and {@code refs/heads}. * @since 4.9 */
protected static void addPrefixesTo(String name, Collection<String> out) { int p1 = name.indexOf('/'); while (p1 > 0) { out.add(name.substring(0, p1)); p1 = name.indexOf('/', p1 + 1); } }
Create a new RefUpdate copying the batch settings.
Params:
  • cmd – specific command the update should be created to copy.
Throws:
  • IOException – the reference database cannot make a new update object for the given reference.
Returns:a single reference update command.
/** * Create a new RefUpdate copying the batch settings. * * @param cmd * specific command the update should be created to copy. * @return a single reference update command. * @throws java.io.IOException * the reference database cannot make a new update object for * the given reference. */
protected RefUpdate newUpdate(ReceiveCommand cmd) throws IOException { RefUpdate ru = refdb.newUpdate(cmd.getRefName(), false); if (isRefLogDisabled(cmd)) { ru.disableRefLog(); } else { ru.setRefLogIdent(refLogIdent); ru.setRefLogMessage(getRefLogMessage(cmd), isRefLogIncludingResult(cmd)); ru.setForceRefLog(isForceRefLog(cmd)); } ru.setPushCertificate(pushCert); switch (cmd.getType()) { case DELETE: if (!ObjectId.zeroId().equals(cmd.getOldId())) ru.setExpectedOldObjectId(cmd.getOldId()); ru.setForceUpdate(true); return ru; case CREATE: case UPDATE: case UPDATE_NONFASTFORWARD: default: ru.setForceUpdate(isAllowNonFastForwards()); ru.setExpectedOldObjectId(cmd.getOldId()); ru.setNewObjectId(cmd.getNewId()); return ru; } }
Check whether reflog is disabled for a command.
Params:
  • cmd – specific command.
Returns:whether the reflog is disabled, taking into account the state from this instance as well as overrides in the given command.
Since:4.9
/** * Check whether reflog is disabled for a command. * * @param cmd * specific command. * @return whether the reflog is disabled, taking into account the state from * this instance as well as overrides in the given command. * @since 4.9 */
protected boolean isRefLogDisabled(ReceiveCommand cmd) { return cmd.hasCustomRefLog() ? cmd.isRefLogDisabled() : isRefLogDisabled(); }
Get reflog message for a command.
Params:
  • cmd – specific command.
Returns:reflog message, taking into account the state from this instance as well as overrides in the given command.
Since:4.9
/** * Get reflog message for a command. * * @param cmd * specific command. * @return reflog message, taking into account the state from this instance as * well as overrides in the given command. * @since 4.9 */
protected String getRefLogMessage(ReceiveCommand cmd) { return cmd.hasCustomRefLog() ? cmd.getRefLogMessage() : getRefLogMessage(); }
Check whether the reflog message for a command should include the result.
Params:
  • cmd – specific command.
Returns:whether the reflog message should show the result, taking into account the state from this instance as well as overrides in the given command.
Since:4.9
/** * Check whether the reflog message for a command should include the result. * * @param cmd * specific command. * @return whether the reflog message should show the result, taking into * account the state from this instance as well as overrides in the * given command. * @since 4.9 */
protected boolean isRefLogIncludingResult(ReceiveCommand cmd) { return cmd.hasCustomRefLog() ? cmd.isRefLogIncludingResult() : isRefLogIncludingResult(); }
Check whether the reflog for a command should be written regardless of repo defaults.
Params:
  • cmd – specific command.
Returns:whether force writing is enabled.
Since:4.9
/** * Check whether the reflog for a command should be written regardless of repo * defaults. * * @param cmd * specific command. * @return whether force writing is enabled. * @since 4.9 */
protected boolean isForceRefLog(ReceiveCommand cmd) { Boolean isForceRefLog = cmd.isForceRefLog(); return isForceRefLog != null ? isForceRefLog.booleanValue() : isForceRefLog(); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public String toString() { StringBuilder r = new StringBuilder(); r.append(getClass().getSimpleName()).append('['); if (commands.isEmpty()) return r.append(']').toString(); r.append('\n'); for (ReceiveCommand cmd : commands) { r.append(" "); //$NON-NLS-1$ r.append(cmd); r.append(" (").append(cmd.getResult()); //$NON-NLS-1$ if (cmd.getMessage() != null) { r.append(": ").append(cmd.getMessage()); //$NON-NLS-1$ } r.append(")\n"); //$NON-NLS-1$ } return r.append(']').toString(); } }