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