View Javadoc
1   /*
2    * Copyright (C) 2008, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.transport;
12  
13  import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
14  import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
15  
16  import java.io.IOException;
17  import java.text.MessageFormat;
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.List;
21  
22  import org.eclipse.jgit.annotations.NonNull;
23  import org.eclipse.jgit.annotations.Nullable;
24  import org.eclipse.jgit.internal.JGitText;
25  import org.eclipse.jgit.lib.AnyObjectId;
26  import org.eclipse.jgit.lib.ObjectId;
27  import org.eclipse.jgit.lib.Ref;
28  import org.eclipse.jgit.lib.RefUpdate;
29  import org.eclipse.jgit.revwalk.RevCommit;
30  import org.eclipse.jgit.revwalk.RevObject;
31  import org.eclipse.jgit.revwalk.RevWalk;
32  
33  /**
34   * A command being processed by
35   * {@link org.eclipse.jgit.transport.ReceivePack}.
36   * <p>
37   * This command instance roughly translates to the server side representation of
38   * the {@link org.eclipse.jgit.transport.RemoteRefUpdate} created by the client.
39   */
40  public class ReceiveCommand {
41  	/** Type of operation requested. */
42  	public enum Type {
43  		/** Create a new ref; the ref must not already exist. */
44  		CREATE,
45  
46  		/**
47  		 * Update an existing ref with a fast-forward update.
48  		 * <p>
49  		 * During a fast-forward update no changes will be lost; only new
50  		 * commits are inserted into the ref.
51  		 */
52  		UPDATE,
53  
54  		/**
55  		 * Update an existing ref by potentially discarding objects.
56  		 * <p>
57  		 * The current value of the ref is not fully reachable from the new
58  		 * value of the ref, so a successful command may result in one or more
59  		 * objects becoming unreachable.
60  		 */
61  		UPDATE_NONFASTFORWARD,
62  
63  		/** Delete an existing ref; the ref should already exist. */
64  		DELETE;
65  	}
66  
67  	/** Result of the update command. */
68  	public enum Result {
69  		/** The command has not yet been attempted by the server. */
70  		NOT_ATTEMPTED,
71  
72  		/** The server is configured to deny creation of this ref. */
73  		REJECTED_NOCREATE,
74  
75  		/** The server is configured to deny deletion of this ref. */
76  		REJECTED_NODELETE,
77  
78  		/** The update is a non-fast-forward update and isn't permitted. */
79  		REJECTED_NONFASTFORWARD,
80  
81  		/** The update affects <code>HEAD</code> and cannot be permitted. */
82  		REJECTED_CURRENT_BRANCH,
83  
84  		/**
85  		 * One or more objects aren't in the repository.
86  		 * <p>
87  		 * This is severe indication of either repository corruption on the
88  		 * server side, or a bug in the client wherein the client did not supply
89  		 * all required objects during the pack transfer.
90  		 */
91  		REJECTED_MISSING_OBJECT,
92  
93  		/** Other failure; see {@link ReceiveCommand#getMessage()}. */
94  		REJECTED_OTHER_REASON,
95  
96  		/** The ref could not be locked and updated atomically; try again. */
97  		LOCK_FAILURE,
98  
99  		/** The change was completed successfully. */
100 		OK;
101 	}
102 
103 	/**
104 	 * Filter a collection of commands according to result.
105 	 *
106 	 * @param in
107 	 *            commands to filter.
108 	 * @param want
109 	 *            desired status to filter by.
110 	 * @return a copy of the command list containing only those commands with
111 	 *         the desired status.
112 	 * @since 4.2
113 	 */
114 	public static List<ReceiveCommand> filter(Iterable<ReceiveCommand> in,
115 			Result want) {
116 		List<ReceiveCommand> r;
117 		if (in instanceof Collection)
118 			r = new ArrayList<>(((Collection<?>) in).size());
119 		else
120 			r = new ArrayList<>();
121 		for (ReceiveCommand cmd : in) {
122 			if (cmd.getResult() == want)
123 				r.add(cmd);
124 		}
125 		return r;
126 	}
127 
128 	/**
129 	 * Filter a list of commands according to result.
130 	 *
131 	 * @param commands
132 	 *            commands to filter.
133 	 * @param want
134 	 *            desired status to filter by.
135 	 * @return a copy of the command list containing only those commands with
136 	 *         the desired status.
137 	 * @since 2.0
138 	 */
139 	public static List<ReceiveCommand> filter(List<ReceiveCommand> commands,
140 			Result want) {
141 		return filter((Iterable<ReceiveCommand>) commands, want);
142 	}
143 
144 	/**
145 	 * Set unprocessed commands as failed due to transaction aborted.
146 	 * <p>
147 	 * If a command is still
148 	 * {@link org.eclipse.jgit.transport.ReceiveCommand.Result#NOT_ATTEMPTED} it
149 	 * will be set to
150 	 * {@link org.eclipse.jgit.transport.ReceiveCommand.Result#REJECTED_OTHER_REASON}.
151 	 *
152 	 * @param commands
153 	 *            commands to mark as failed.
154 	 * @since 4.2
155 	 */
156 	public static void abort(Iterable<ReceiveCommand> commands) {
157 		for (ReceiveCommand c : commands) {
158 			if (c.getResult() == NOT_ATTEMPTED) {
159 				c.setResult(REJECTED_OTHER_REASON,
160 						JGitText.get().transactionAborted);
161 			}
162 		}
163 	}
164 
165 	/**
166 	 * Check whether a command failed due to transaction aborted.
167 	 *
168 	 * @param cmd
169 	 *            command.
170 	 * @return whether the command failed due to transaction aborted, as in
171 	 *         {@link #abort(Iterable)}.
172 	 * @since 4.9
173 	 */
174 	public static boolean isTransactionAborted(ReceiveCommand cmd) {
175 		return cmd.getResult() == REJECTED_OTHER_REASON
176 				&& cmd.getMessage().equals(JGitText.get().transactionAborted);
177 	}
178 
179 	/**
180 	 * Create a command to switch a reference from object to symbolic.
181 	 *
182 	 * @param oldId
183 	 *            expected oldId. May be {@code zeroId} to create.
184 	 * @param newTarget
185 	 *            new target; must begin with {@code "refs/"}.
186 	 * @param name
187 	 *            name of the reference to make symbolic.
188 	 * @return command instance.
189 	 * @since 4.10
190 	 */
191 	public static ReceiveCommand link(@NonNull ObjectId oldId,
192 			@NonNull String newTarget, @NonNull String name) {
193 		return new ReceiveCommand(oldId, newTarget, name);
194 	}
195 
196 	/**
197 	 * Create a command to switch a symbolic reference's target.
198 	 *
199 	 * @param oldTarget
200 	 *            expected old target. May be null to create.
201 	 * @param newTarget
202 	 *            new target; must begin with {@code "refs/"}.
203 	 * @param name
204 	 *            name of the reference to make symbolic.
205 	 * @return command instance.
206 	 * @since 4.10
207 	 */
208 	public static ReceiveCommand link(@Nullable String oldTarget,
209 			@NonNull String newTarget, @NonNull String name) {
210 		return new ReceiveCommand(oldTarget, newTarget, name);
211 	}
212 
213 	/**
214 	 * Create a command to switch a reference from symbolic to object.
215 	 *
216 	 * @param oldTarget
217 	 *            expected old target.
218 	 * @param newId
219 	 *            new object identifier. May be {@code zeroId()} to delete.
220 	 * @param name
221 	 *            name of the reference to convert from symbolic.
222 	 * @return command instance.
223 	 * @since 4.10
224 	 */
225 	public static ReceiveCommand unlink(@NonNull String oldTarget,
226 			@NonNull ObjectId newId, @NonNull String name) {
227 		return new ReceiveCommand(oldTarget, newId, name);
228 	}
229 
230 	private final ObjectId oldId;
231 
232 	private final String oldSymref;
233 
234 	private final ObjectId newId;
235 
236 	private final String newSymref;
237 
238 	private final String name;
239 
240 	private Type type;
241 
242 	private boolean typeIsCorrect;
243 
244 	private Ref ref;
245 
246 	private Result status = Result.NOT_ATTEMPTED;
247 
248 	private String message;
249 
250 	private boolean customRefLog;
251 
252 	private String refLogMessage;
253 
254 	private boolean refLogIncludeResult;
255 
256 	private Boolean forceRefLog;
257 
258 	/**
259 	 * Create a new command for
260 	 * {@link org.eclipse.jgit.transport.ReceivePack}.
261 	 *
262 	 * @param oldId
263 	 *            the expected old object id; must not be null. Use
264 	 *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate a
265 	 *            ref creation.
266 	 * @param newId
267 	 *            the new object id; must not be null. Use
268 	 *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate a
269 	 *            ref deletion.
270 	 * @param name
271 	 *            name of the ref being affected.
272 	 */
273 	public ReceiveCommand(final ObjectIdhtml#ObjectId">ObjectId oldId, final ObjectId newId,
274 			final String name) {
275 		if (oldId == null) {
276 			throw new IllegalArgumentException(
277 					JGitText.get().oldIdMustNotBeNull);
278 		}
279 		if (newId == null) {
280 			throw new IllegalArgumentException(
281 					JGitText.get().newIdMustNotBeNull);
282 		}
283 		if (name == null || name.isEmpty()) {
284 			throw new IllegalArgumentException(
285 					JGitText.get().nameMustNotBeNullOrEmpty);
286 		}
287 		this.oldId = oldId;
288 		this.oldSymref = null;
289 		this.newId = newId;
290 		this.newSymref = null;
291 		this.name = name;
292 
293 		type = Type.UPDATE;
294 		if (ObjectId.zeroId().equals(oldId)) {
295 			type = Type.CREATE;
296 		}
297 		if (ObjectId.zeroId().equals(newId)) {
298 			type = Type.DELETE;
299 		}
300 	}
301 
302 	/**
303 	 * Create a new command for
304 	 * {@link org.eclipse.jgit.transport.ReceivePack}.
305 	 *
306 	 * @param oldId
307 	 *            the old object id; must not be null. Use
308 	 *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate a
309 	 *            ref creation.
310 	 * @param newId
311 	 *            the new object id; must not be null. Use
312 	 *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate a
313 	 *            ref deletion.
314 	 * @param name
315 	 *            name of the ref being affected.
316 	 * @param type
317 	 *            type of the command. Must be
318 	 *            {@link org.eclipse.jgit.transport.ReceiveCommand.Type#CREATE}
319 	 *            if {@code
320 	 *            oldId} is zero, or
321 	 *            {@link org.eclipse.jgit.transport.ReceiveCommand.Type#DELETE}
322 	 *            if {@code newId} is zero.
323 	 * @since 2.0
324 	 */
325 	public ReceiveCommand(final ObjectIdhtml#ObjectId">ObjectId oldId, final ObjectId newId,
326 			final String name, final Type type) {
327 		if (oldId == null) {
328 			throw new IllegalArgumentException(
329 					JGitText.get().oldIdMustNotBeNull);
330 		}
331 		if (newId == null) {
332 			throw new IllegalArgumentException(
333 					JGitText.get().newIdMustNotBeNull);
334 		}
335 		if (name == null || name.isEmpty()) {
336 			throw new IllegalArgumentException(
337 					JGitText.get().nameMustNotBeNullOrEmpty);
338 		}
339 		this.oldId = oldId;
340 		this.oldSymref = null;
341 		this.newId = newId;
342 		this.newSymref = null;
343 		this.name = name;
344 		switch (type) {
345 		case CREATE:
346 			if (!ObjectId.zeroId().equals(oldId)) {
347 				throw new IllegalArgumentException(
348 						JGitText.get().createRequiresZeroOldId);
349 			}
350 			break;
351 		case DELETE:
352 			if (!ObjectId.zeroId().equals(newId)) {
353 				throw new IllegalArgumentException(
354 						JGitText.get().deleteRequiresZeroNewId);
355 			}
356 			break;
357 		case UPDATE:
358 		case UPDATE_NONFASTFORWARD:
359 			if (ObjectId.zeroId().equals(newId)
360 					|| ObjectId.zeroId().equals(oldId)) {
361 				throw new IllegalArgumentException(
362 						JGitText.get().updateRequiresOldIdAndNewId);
363 			}
364 			break;
365 		default:
366 			throw new IllegalStateException(
367 					JGitText.get().enumValueNotSupported0);
368 		}
369 		this.type = type;
370 	}
371 
372 	/**
373 	 * Create a command to switch a reference from object to symbolic.
374 	 *
375 	 * @param oldId
376 	 *            the old object id; must not be null. Use
377 	 *            {@link ObjectId#zeroId()} to indicate a ref creation.
378 	 * @param newSymref
379 	 *            new target, must begin with {@code "refs/"}. Use {@code null}
380 	 *            to indicate a ref deletion.
381 	 * @param name
382 	 *            name of the reference to make symbolic.
383 	 * @since 4.10
384 	 */
385 	private ReceiveCommand(ObjectId oldId, String newSymref, String name) {
386 		if (oldId == null) {
387 			throw new IllegalArgumentException(
388 					JGitText.get().oldIdMustNotBeNull);
389 		}
390 		if (name == null || name.isEmpty()) {
391 			throw new IllegalArgumentException(
392 					JGitText.get().nameMustNotBeNullOrEmpty);
393 		}
394 		this.oldId = oldId;
395 		this.oldSymref = null;
396 		this.newId = ObjectId.zeroId();
397 		this.newSymref = newSymref;
398 		this.name = name;
399 		if (AnyObjectId.isEqual(ObjectId.zeroId(), oldId)) {
400 			type = Type.CREATE;
401 		} else if (newSymref != null) {
402 			type = Type.UPDATE;
403 		} else {
404 			type = Type.DELETE;
405 		}
406 		typeIsCorrect = true;
407 	}
408 
409 	/**
410 	 * Create a command to switch a reference from symbolic to object.
411 	 *
412 	 * @param oldSymref
413 	 *            expected old target. Use {@code null} to indicate a ref
414 	 *            creation.
415 	 * @param newId
416 	 *            the new object id; must not be null. Use
417 	 *            {@link ObjectId#zeroId()} to indicate a ref deletion.
418 	 * @param name
419 	 *            name of the reference to convert from symbolic.
420 	 * @since 4.10
421 	 */
422 	private ReceiveCommand(String oldSymref, ObjectId newId, String name) {
423 		if (newId == null) {
424 			throw new IllegalArgumentException(
425 					JGitText.get().newIdMustNotBeNull);
426 		}
427 		if (name == null || name.isEmpty()) {
428 			throw new IllegalArgumentException(
429 					JGitText.get().nameMustNotBeNullOrEmpty);
430 		}
431 		this.oldId = ObjectId.zeroId();
432 		this.oldSymref = oldSymref;
433 		this.newId = newId;
434 		this.newSymref = null;
435 		this.name = name;
436 		if (oldSymref == null) {
437 			type = Type.CREATE;
438 		} else if (!AnyObjectId.isEqual(ObjectId.zeroId(), newId)) {
439 			type = Type.UPDATE;
440 		} else {
441 			type = Type.DELETE;
442 		}
443 		typeIsCorrect = true;
444 	}
445 
446 	/**
447 	 * Create a command to switch a symbolic reference's target.
448 	 *
449 	 * @param oldTarget
450 	 *            expected old target. Use {@code null} to indicate a ref
451 	 *            creation.
452 	 * @param newTarget
453 	 *            new target. Use {@code null} to indicate a ref deletion.
454 	 * @param name
455 	 *            name of the reference to make symbolic.
456 	 * @since 4.10
457 	 */
458 	private ReceiveCommand(@Nullable String oldTarget, String newTarget, String name) {
459 		if (name == null || name.isEmpty()) {
460 			throw new IllegalArgumentException(
461 					JGitText.get().nameMustNotBeNullOrEmpty);
462 		}
463 		this.oldId = ObjectId.zeroId();
464 		this.oldSymref = oldTarget;
465 		this.newId = ObjectId.zeroId();
466 		this.newSymref = newTarget;
467 		this.name = name;
468 		if (oldTarget == null) {
469 			if (newTarget == null) {
470 				throw new IllegalArgumentException(
471 						JGitText.get().bothRefTargetsMustNotBeNull);
472 			}
473 			type = Type.CREATE;
474 		} else if (newTarget != null) {
475 			type = Type.UPDATE;
476 		} else {
477 			type = Type.DELETE;
478 		}
479 		typeIsCorrect = true;
480 	}
481 
482 	/**
483 	 * Get the old value the client thinks the ref has.
484 	 *
485 	 * @return the old value the client thinks the ref has.
486 	 */
487 	public ObjectId getOldId() {
488 		return oldId;
489 	}
490 
491 	/**
492 	 * Get expected old target for a symbolic reference.
493 	 *
494 	 * @return expected old target for a symbolic reference.
495 	 * @since 4.10
496 	 */
497 	@Nullable
498 	public String getOldSymref() {
499 		return oldSymref;
500 	}
501 
502 	/**
503 	 * Get the requested new value for this ref.
504 	 *
505 	 * @return the requested new value for this ref.
506 	 */
507 	public ObjectId getNewId() {
508 		return newId;
509 	}
510 
511 	/**
512 	 * Get requested new target for a symbolic reference.
513 	 *
514 	 * @return requested new target for a symbolic reference.
515 	 * @since 4.10
516 	 */
517 	@Nullable
518 	public String getNewSymref() {
519 		return newSymref;
520 	}
521 
522 	/**
523 	 * Get the name of the ref being updated.
524 	 *
525 	 * @return the name of the ref being updated.
526 	 */
527 	public String getRefName() {
528 		return name;
529 	}
530 
531 	/**
532 	 * Get the type of this command; see {@link Type}.
533 	 *
534 	 * @return the type of this command; see {@link Type}.
535 	 */
536 	public Type getType() {
537 		return type;
538 	}
539 
540 	/**
541 	 * Get the ref, if this was advertised by the connection.
542 	 *
543 	 * @return the ref, if this was advertised by the connection.
544 	 */
545 	public Ref getRef() {
546 		return ref;
547 	}
548 
549 	/**
550 	 * Get the current status code of this command.
551 	 *
552 	 * @return the current status code of this command.
553 	 */
554 	public Result getResult() {
555 		return status;
556 	}
557 
558 	/**
559 	 * Get the message associated with a failure status.
560 	 *
561 	 * @return the message associated with a failure status.
562 	 */
563 	public String getMessage() {
564 		return message;
565 	}
566 
567 	/**
568 	 * Set the message to include in the reflog.
569 	 * <p>
570 	 * Overrides the default set by {@code setRefLogMessage} on any containing
571 	 * {@link org.eclipse.jgit.lib.BatchRefUpdate}.
572 	 *
573 	 * @param msg
574 	 *            the message to describe this change. If null and appendStatus is
575 	 *            false, the reflog will not be updated.
576 	 * @param appendStatus
577 	 *            true if the status of the ref change (fast-forward or
578 	 *            forced-update) should be appended to the user supplied message.
579 	 * @since 4.9
580 	 */
581 	public void setRefLogMessage(String msg, boolean appendStatus) {
582 		customRefLog = true;
583 		if (msg == null && !appendStatus) {
584 			disableRefLog();
585 		} else if (msg == null && appendStatus) {
586 			refLogMessage = ""; //$NON-NLS-1$
587 			refLogIncludeResult = true;
588 		} else {
589 			refLogMessage = msg;
590 			refLogIncludeResult = appendStatus;
591 		}
592 	}
593 
594 	/**
595 	 * Don't record this update in the ref's associated reflog.
596 	 * <p>
597 	 * Equivalent to {@code setRefLogMessage(null, false)}.
598 	 *
599 	 * @since 4.9
600 	 */
601 	public void disableRefLog() {
602 		customRefLog = true;
603 		refLogMessage = null;
604 		refLogIncludeResult = false;
605 	}
606 
607 	/**
608 	 * Force writing a reflog for the updated ref.
609 	 *
610 	 * @param force whether to force.
611 	 * @since 4.9
612 	 */
613 	public void setForceRefLog(boolean force) {
614 		forceRefLog = Boolean.valueOf(force);
615 	}
616 
617 	/**
618 	 * Check whether this command has a custom reflog message setting that should
619 	 * override defaults in any containing
620 	 * {@link org.eclipse.jgit.lib.BatchRefUpdate}.
621 	 * <p>
622 	 * Does not take into account whether {@code #setForceRefLog(boolean)} has
623 	 * been called.
624 	 *
625 	 * @return whether a custom reflog is set.
626 	 * @since 4.9
627 	 */
628 	public boolean hasCustomRefLog() {
629 		return customRefLog;
630 	}
631 
632 	/**
633 	 * Check whether log has been disabled by {@link #disableRefLog()}.
634 	 *
635 	 * @return true if disabled.
636 	 * @since 4.9
637 	 */
638 	public boolean isRefLogDisabled() {
639 		return refLogMessage == null;
640 	}
641 
642 	/**
643 	 * Get the message to include in the reflog.
644 	 *
645 	 * @return message the caller wants to include in the reflog; null if the
646 	 *         update should not be logged.
647 	 * @since 4.9
648 	 */
649 	@Nullable
650 	public String getRefLogMessage() {
651 		return refLogMessage;
652 	}
653 
654 	/**
655 	 * Check whether the reflog message should include the result of the update,
656 	 * such as fast-forward or force-update.
657 	 *
658 	 * @return true if the message should include the result.
659 	 * @since 4.9
660 	 */
661 	public boolean isRefLogIncludingResult() {
662 		return refLogIncludeResult;
663 	}
664 
665 	/**
666 	 * Check whether the reflog should be written regardless of repo defaults.
667 	 *
668 	 * @return whether force writing is enabled; {@code null} if
669 	 *         {@code #setForceRefLog(boolean)} was never called.
670 	 * @since 4.9
671 	 */
672 	@Nullable
673 	public Boolean isForceRefLog() {
674 		return forceRefLog;
675 	}
676 
677 	/**
678 	 * Set the status of this command.
679 	 *
680 	 * @param s
681 	 *            the new status code for this command.
682 	 */
683 	public void setResult(Result s) {
684 		setResult(s, null);
685 	}
686 
687 	/**
688 	 * Set the status of this command.
689 	 *
690 	 * @param s
691 	 *            new status code for this command.
692 	 * @param m
693 	 *            optional message explaining the new status.
694 	 */
695 	public void setResult(Result s, String m) {
696 		status = s;
697 		message = m;
698 	}
699 
700 	/**
701 	 * Update the type of this command by checking for fast-forward.
702 	 * <p>
703 	 * If the command's current type is UPDATE, a merge test will be performed
704 	 * using the supplied RevWalk to determine if {@link #getOldId()} is fully
705 	 * merged into {@link #getNewId()}. If some commits are not merged the
706 	 * update type is changed to
707 	 * {@link org.eclipse.jgit.transport.ReceiveCommand.Type#UPDATE_NONFASTFORWARD}.
708 	 *
709 	 * @param walk
710 	 *            an instance to perform the merge test with. The caller must
711 	 *            allocate and release this object.
712 	 * @throws java.io.IOException
713 	 *             either oldId or newId is not accessible in the repository
714 	 *             used by the RevWalk. This usually indicates data corruption,
715 	 *             and the command cannot be processed.
716 	 */
717 	public void updateType(RevWalk walk) throws IOException {
718 		if (typeIsCorrect)
719 			return;
720 		if (type == Type.UPDATE && !AnyObjectId.isEqual(oldId, newId)) {
721 			RevObject o = walk.parseAny(oldId);
722 			RevObject n = walk.parseAny(newId);
723 			if (!(o instanceof RevCommit)
724 					|| !(n instanceof RevCommit)
725 					|| !walk.isMergedInto((RevCommit="../../../../org/eclipse/jgit/revwalk/RevCommit.html#RevCommit">RevCommit) o, (RevCommit) n))
726 				setType(Type.UPDATE_NONFASTFORWARD);
727 		}
728 		typeIsCorrect = true;
729 	}
730 
731 	/**
732 	 * Execute this command during a receive-pack session.
733 	 * <p>
734 	 * Sets the status of the command as a side effect.
735 	 *
736 	 * @param rp
737 	 *            receive-pack session.
738 	 * @since 5.6
739 	 */
740 	public void execute(ReceivePack rp) {
741 		try {
742 			String expTarget = getOldSymref();
743 			boolean detach = getNewSymref() != null
744 					|| (type == Type.DELETE && expTarget != null);
745 			RefUpdate ru = rp.getRepository().updateRef(getRefName(), detach);
746 			if (expTarget != null) {
747 				if (!ru.getRef().isSymbolic() || !ru.getRef().getTarget()
748 						.getName().equals(expTarget)) {
749 					setResult(Result.LOCK_FAILURE);
750 					return;
751 				}
752 			}
753 
754 			ru.setRefLogIdent(rp.getRefLogIdent());
755 			ru.setRefLogMessage(refLogMessage, refLogIncludeResult);
756 			switch (getType()) {
757 			case DELETE:
758 				if (!ObjectId.zeroId().equals(getOldId())) {
759 					// We can only do a CAS style delete if the client
760 					// didn't bork its delete request by sending the
761 					// wrong zero id rather than the advertised one.
762 					//
763 					ru.setExpectedOldObjectId(getOldId());
764 				}
765 				ru.setForceUpdate(true);
766 				setResult(ru.delete(rp.getRevWalk()));
767 				break;
768 
769 			case CREATE:
770 			case UPDATE:
771 			case UPDATE_NONFASTFORWARD:
772 				ru.setForceUpdate(rp.isAllowNonFastForwards());
773 				ru.setExpectedOldObjectId(getOldId());
774 				ru.setRefLogMessage("push", true); //$NON-NLS-1$
775 				if (getNewSymref() != null) {
776 					setResult(ru.link(getNewSymref()));
777 				} else {
778 					ru.setNewObjectId(getNewId());
779 					setResult(ru.update(rp.getRevWalk()));
780 				}
781 				break;
782 			}
783 		} catch (IOException err) {
784 			reject(err);
785 		}
786 	}
787 
788 	void setRef(Ref r) {
789 		ref = r;
790 	}
791 
792 	void setType(Type t) {
793 		type = t;
794 	}
795 
796 	void setTypeFastForwardUpdate() {
797 		type = Type.UPDATE;
798 		typeIsCorrect = true;
799 	}
800 
801 	/**
802 	 * Set the result of this command.
803 	 *
804 	 * @param r
805 	 *            the new result code for this command.
806 	 */
807 	public void setResult(RefUpdate.Result r) {
808 		switch (r) {
809 		case NOT_ATTEMPTED:
810 			setResult(Result.NOT_ATTEMPTED);
811 			break;
812 
813 		case LOCK_FAILURE:
814 		case IO_FAILURE:
815 			setResult(Result.LOCK_FAILURE);
816 			break;
817 
818 		case NO_CHANGE:
819 		case NEW:
820 		case FORCED:
821 		case FAST_FORWARD:
822 			setResult(Result.OK);
823 			break;
824 
825 		case REJECTED:
826 			setResult(Result.REJECTED_NONFASTFORWARD);
827 			break;
828 
829 		case REJECTED_CURRENT_BRANCH:
830 			setResult(Result.REJECTED_CURRENT_BRANCH);
831 			break;
832 
833 		case REJECTED_MISSING_OBJECT:
834 			setResult(Result.REJECTED_MISSING_OBJECT);
835 			break;
836 
837 		case REJECTED_OTHER_REASON:
838 			setResult(Result.REJECTED_OTHER_REASON);
839 			break;
840 
841 		default:
842 			setResult(Result.REJECTED_OTHER_REASON, r.name());
843 			break;
844 		}
845 	}
846 
847 	void reject(IOException err) {
848 		setResult(Result.REJECTED_OTHER_REASON, MessageFormat.format(
849 				JGitText.get().lockError, err.getMessage()));
850 	}
851 
852 	/** {@inheritDoc} */
853 	@SuppressWarnings("nls")
854 	@Override
855 	public String toString() {
856 		return getType().name() + ": " + getOldId().name() + " "
857 				+ getNewId().name() + " " + getRefName();
858 	}
859 }