2 * Copyright (C) 2008-2009, Google Inc.
3 * and other copyright owners as documented in the project's IP log.
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
10 * All rights reserved.
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
16 * - Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
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.
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
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.
44 package org
.eclipse
.jgit
.transport
;
46 import java
.io
.BufferedWriter
;
47 import java
.io
.EOFException
;
48 import java
.io
.IOException
;
49 import java
.io
.InputStream
;
50 import java
.io
.OutputStream
;
51 import java
.io
.OutputStreamWriter
;
52 import java
.io
.PrintWriter
;
53 import java
.util
.ArrayList
;
54 import java
.util
.Collections
;
55 import java
.util
.HashMap
;
56 import java
.util
.HashSet
;
57 import java
.util
.List
;
61 import org
.eclipse
.jgit
.errors
.MissingObjectException
;
62 import org
.eclipse
.jgit
.errors
.PackProtocolException
;
63 import org
.eclipse
.jgit
.lib
.Config
;
64 import org
.eclipse
.jgit
.lib
.Constants
;
65 import org
.eclipse
.jgit
.lib
.NullProgressMonitor
;
66 import org
.eclipse
.jgit
.lib
.ObjectId
;
67 import org
.eclipse
.jgit
.lib
.PackLock
;
68 import org
.eclipse
.jgit
.lib
.PersonIdent
;
69 import org
.eclipse
.jgit
.lib
.Ref
;
70 import org
.eclipse
.jgit
.lib
.RefUpdate
;
71 import org
.eclipse
.jgit
.lib
.Repository
;
72 import org
.eclipse
.jgit
.lib
.Config
.SectionParser
;
73 import org
.eclipse
.jgit
.revwalk
.ObjectWalk
;
74 import org
.eclipse
.jgit
.revwalk
.RevCommit
;
75 import org
.eclipse
.jgit
.revwalk
.RevFlag
;
76 import org
.eclipse
.jgit
.revwalk
.RevObject
;
77 import org
.eclipse
.jgit
.revwalk
.RevWalk
;
78 import org
.eclipse
.jgit
.transport
.ReceiveCommand
.Result
;
79 import org
.eclipse
.jgit
.transport
.RefAdvertiser
.PacketLineOutRefAdvertiser
;
80 import org
.eclipse
.jgit
.util
.io
.InterruptTimer
;
81 import org
.eclipse
.jgit
.util
.io
.TimeoutInputStream
;
82 import org
.eclipse
.jgit
.util
.io
.TimeoutOutputStream
;
85 * Implements the server side of a push connection, receiving objects.
87 public class ReceivePack
{
88 static final String CAPABILITY_REPORT_STATUS
= BasePackPushConnection
.CAPABILITY_REPORT_STATUS
;
90 static final String CAPABILITY_DELETE_REFS
= BasePackPushConnection
.CAPABILITY_DELETE_REFS
;
92 static final String CAPABILITY_OFS_DELTA
= BasePackPushConnection
.CAPABILITY_OFS_DELTA
;
94 /** Database we write the stored objects into. */
95 private final Repository db
;
97 /** Revision traversal support over {@link #db}. */
98 private final RevWalk walk
;
101 * Is the client connection a bi-directional socket or pipe?
103 * If true, this class assumes it can perform multiple read and write cycles
104 * with the client over the input and output streams. This matches the
105 * functionality available with a standard TCP/IP connection, or a local
106 * operating system or in-memory pipe.
108 * If false, this class runs in a read everything then output results mode,
109 * making it suitable for single round-trip systems RPCs such as HTTP.
111 private boolean biDirectionalPipe
= true;
113 /** Should an incoming transfer validate objects? */
114 private boolean checkReceivedObjects
;
116 /** Should an incoming transfer permit create requests? */
117 private boolean allowCreates
;
119 /** Should an incoming transfer permit delete requests? */
120 private boolean allowDeletes
;
122 /** Should an incoming transfer permit non-fast-forward requests? */
123 private boolean allowNonFastForwards
;
125 private boolean allowOfsDelta
;
127 /** Identity to record action as within the reflog. */
128 private PersonIdent refLogIdent
;
130 /** Hook to validate the update commands before execution. */
131 private PreReceiveHook preReceive
;
133 /** Hook to report on the commands after execution. */
134 private PostReceiveHook postReceive
;
136 /** Timeout in seconds to wait for client interaction. */
139 /** Timer to manage {@link #timeout}. */
140 private InterruptTimer timer
;
142 private TimeoutInputStream timeoutIn
;
144 private InputStream rawIn
;
146 private OutputStream rawOut
;
148 private PacketLineIn pckIn
;
150 private PacketLineOut pckOut
;
152 private PrintWriter msgs
;
154 /** The refs we advertised as existing at the start of the connection. */
155 private Map
<String
, Ref
> refs
;
157 /** Capabilities requested by the client. */
158 private Set
<String
> enabledCapablities
;
160 /** Commands to execute, as received by the client. */
161 private List
<ReceiveCommand
> commands
;
163 /** An exception caught while unpacking and fsck'ing the objects. */
164 private Throwable unpackError
;
166 /** if {@link #enabledCapablities} has {@link #CAPABILITY_REPORT_STATUS} */
167 private boolean reportStatus
;
169 /** Lock around the received pack file, while updating refs. */
170 private PackLock packLock
;
173 * Create a new pack receive for an open repository.
176 * the destination repository.
178 public ReceivePack(final Repository into
) {
180 walk
= new RevWalk(db
);
182 final ReceiveConfig cfg
= db
.getConfig().get(ReceiveConfig
.KEY
);
183 checkReceivedObjects
= cfg
.checkReceivedObjects
;
184 allowCreates
= cfg
.allowCreates
;
185 allowDeletes
= cfg
.allowDeletes
;
186 allowNonFastForwards
= cfg
.allowNonFastForwards
;
187 allowOfsDelta
= cfg
.allowOfsDelta
;
188 preReceive
= PreReceiveHook
.NULL
;
189 postReceive
= PostReceiveHook
.NULL
;
192 private static class ReceiveConfig
{
193 static final SectionParser
<ReceiveConfig
> KEY
= new SectionParser
<ReceiveConfig
>() {
194 public ReceiveConfig
parse(final Config cfg
) {
195 return new ReceiveConfig(cfg
);
199 final boolean checkReceivedObjects
;
201 final boolean allowCreates
;
203 final boolean allowDeletes
;
205 final boolean allowNonFastForwards
;
207 final boolean allowOfsDelta
;
209 ReceiveConfig(final Config config
) {
210 checkReceivedObjects
= config
.getBoolean("receive", "fsckobjects",
213 allowDeletes
= !config
.getBoolean("receive", "denydeletes", false);
214 allowNonFastForwards
= !config
.getBoolean("receive",
215 "denynonfastforwards", false);
216 allowOfsDelta
= config
.getBoolean("repack", "usedeltabaseoffset",
221 /** @return the repository this receive completes into. */
222 public final Repository
getRepository() {
226 /** @return the RevWalk instance used by this connection. */
227 public final RevWalk
getRevWalk() {
231 /** @return all refs which were advertised to the client. */
232 public final Map
<String
, Ref
> getAdvertisedRefs() {
237 * @return true if this class expects a bi-directional pipe opened between
238 * the client and itself. The default is true.
240 public boolean isBiDirectionalPipe() {
241 return biDirectionalPipe
;
246 * if true, this class will assume the socket is a fully
247 * bidirectional pipe between the two peers and takes advantage
248 * of that by first transmitting the known refs, then waiting to
249 * read commands. If false, this class assumes it must read the
250 * commands before writing output and does not perform the
251 * initial advertising.
253 public void setBiDirectionalPipe(final boolean twoWay
) {
254 biDirectionalPipe
= twoWay
;
258 * @return true if this instance will verify received objects are formatted
259 * correctly. Validating objects requires more CPU time on this side
262 public boolean isCheckReceivedObjects() {
263 return checkReceivedObjects
;
268 * true to enable checking received objects; false to assume all
269 * received objects are valid.
271 public void setCheckReceivedObjects(final boolean check
) {
272 checkReceivedObjects
= check
;
275 /** @return true if the client can request refs to be created. */
276 public boolean isAllowCreates() {
282 * true to permit create ref commands to be processed.
284 public void setAllowCreates(final boolean canCreate
) {
285 allowCreates
= canCreate
;
288 /** @return true if the client can request refs to be deleted. */
289 public boolean isAllowDeletes() {
295 * true to permit delete ref commands to be processed.
297 public void setAllowDeletes(final boolean canDelete
) {
298 allowDeletes
= canDelete
;
302 * @return true if the client can request non-fast-forward updates of a ref,
303 * possibly making objects unreachable.
305 public boolean isAllowNonFastForwards() {
306 return allowNonFastForwards
;
311 * true to permit the client to ask for non-fast-forward updates
312 * of an existing ref.
314 public void setAllowNonFastForwards(final boolean canRewind
) {
315 allowNonFastForwards
= canRewind
;
318 /** @return identity of the user making the changes in the reflog. */
319 public PersonIdent
getRefLogIdent() {
324 * Set the identity of the user appearing in the affected reflogs.
326 * The timestamp portion of the identity is ignored. A new identity with the
327 * current timestamp will be created automatically when the updates occur
328 * and the log records are written.
331 * identity of the user. If null the identity will be
332 * automatically determined based on the repository
335 public void setRefLogIdent(final PersonIdent pi
) {
339 /** @return get the hook invoked before updates occur. */
340 public PreReceiveHook
getPreReceiveHook() {
345 * Set the hook which is invoked prior to commands being executed.
347 * Only valid commands (those which have no obvious errors according to the
348 * received input and this instance's configuration) are passed into the
349 * hook. The hook may mark a command with a result of any value other than
350 * {@link Result#NOT_ATTEMPTED} to block its execution.
352 * The hook may be called with an empty command collection if the current
353 * set is completely invalid.
356 * the hook instance; may be null to disable the hook.
358 public void setPreReceiveHook(final PreReceiveHook h
) {
359 preReceive
= h
!= null ? h
: PreReceiveHook
.NULL
;
362 /** @return get the hook invoked after updates occur. */
363 public PostReceiveHook
getPostReceiveHook() {
368 * Set the hook which is invoked after commands are executed.
370 * Only successful commands (type is {@link Result#OK}) are passed into the
371 * hook. The hook may be called with an empty command collection if the
372 * current set all resulted in an error.
375 * the hook instance; may be null to disable the hook.
377 public void setPostReceiveHook(final PostReceiveHook h
) {
378 postReceive
= h
!= null ? h
: PostReceiveHook
.NULL
;
381 /** @return timeout (in seconds) before aborting an IO operation. */
382 public int getTimeout() {
387 * Set the timeout before willing to abort an IO call.
390 * number of seconds to wait (with no data transfer occurring)
391 * before aborting an IO read or write operation with the
394 public void setTimeout(final int seconds
) {
398 /** @return all of the command received by the current request. */
399 public List
<ReceiveCommand
> getAllCommands() {
400 return Collections
.unmodifiableList(commands
);
404 * Send an error message to the client, if it supports receiving them.
406 * If the client doesn't support receiving messages, the message will be
407 * discarded, with no other indication to the caller or to the client.
409 * {@link PreReceiveHook}s should always try to use
410 * {@link ReceiveCommand#setResult(Result, String)} with a result status of
411 * {@link Result#REJECTED_OTHER_REASON} to indicate any reasons for
412 * rejecting an update. Messages attached to a command are much more likely
413 * to be returned to the client.
416 * string describing the problem identified by the hook. The
417 * string must not end with an LF, and must not contain an LF.
419 public void sendError(final String what
) {
420 sendMessage("error", what
);
424 * Send a message to the client, if it supports receiving them.
426 * If the client doesn't support receiving messages, the message will be
427 * discarded, with no other indication to the caller or to the client.
430 * string describing the problem identified by the hook. The
431 * string must not end with an LF, and must not contain an LF.
433 public void sendMessage(final String what
) {
434 sendMessage("remote", what
);
437 private void sendMessage(final String type
, final String what
) {
439 msgs
.println(type
+ ": " + what
);
443 * Execute the receive task on the socket.
446 * raw input to read client commands and pack data from. Caller
447 * must ensure the input is buffered, otherwise read performance
450 * response back to the Git network client. Caller must ensure
451 * the output is buffered, otherwise write performance may
454 * secondary "notice" channel to send additional messages out
455 * through. When run over SSH this should be tied back to the
456 * standard error channel of the command execution. For most
457 * other network connections this should be null.
458 * @throws IOException
460 public void receive(final InputStream input
, final OutputStream output
,
461 final OutputStream messages
) throws IOException
{
467 final Thread caller
= Thread
.currentThread();
468 timer
= new InterruptTimer(caller
.getName() + "-Timer");
469 timeoutIn
= new TimeoutInputStream(rawIn
, timer
);
470 TimeoutOutputStream o
= new TimeoutOutputStream(rawOut
, timer
);
471 timeoutIn
.setTimeout(timeout
* 1000);
472 o
.setTimeout(timeout
* 1000);
477 pckIn
= new PacketLineIn(rawIn
);
478 pckOut
= new PacketLineOut(rawOut
);
479 if (messages
!= null) {
480 msgs
= new PrintWriter(new BufferedWriter(
481 new OutputStreamWriter(messages
, Constants
.CHARSET
),
484 public void println() {
490 enabledCapablities
= new HashSet
<String
>();
491 commands
= new ArrayList
<ReceiveCommand
>();
508 enabledCapablities
= null;
521 private void service() throws IOException
{
522 if (biDirectionalPipe
)
523 sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut
));
525 refs
= db
.getAllRefs();
527 if (!commands
.isEmpty()) {
528 enableCapabilities();
533 if (isCheckReceivedObjects())
536 } catch (IOException err
) {
538 } catch (RuntimeException err
) {
540 } catch (Error err
) {
545 if (unpackError
== null) {
552 sendStatusReport(true, new Reporter() {
553 void sendString(final String s
) throws IOException
{
554 pckOut
.writeString(s
+ "\n");
558 } else if (msgs
!= null) {
559 sendStatusReport(false, new Reporter() {
560 void sendString(final String s
) throws IOException
{
567 postReceive
.onPostReceive(this, filterCommands(Result
.OK
));
571 private void unlockPack() {
572 if (packLock
!= null) {
579 * Generate an advertisement of available refs and capabilities.
582 * the advertisement formatter.
583 * @throws IOException
584 * the formatter failed to write an advertisement.
586 public void sendAdvertisedRefs(final RefAdvertiser adv
) throws IOException
{
587 final RevFlag advertised
= walk
.newFlag("ADVERTISED");
588 adv
.init(walk
, advertised
);
589 adv
.advertiseCapability(CAPABILITY_DELETE_REFS
);
590 adv
.advertiseCapability(CAPABILITY_REPORT_STATUS
);
592 adv
.advertiseCapability(CAPABILITY_OFS_DELTA
);
593 refs
= new HashMap
<String
, Ref
>(db
.getAllRefs());
594 final Ref head
= refs
.remove(Constants
.HEAD
);
595 adv
.send(refs
.values());
596 if (head
!= null && head
.getName().equals(head
.getOrigName()))
597 adv
.advertiseHave(head
.getObjectId());
598 adv
.includeAdditionalHaves();
600 adv
.advertiseId(ObjectId
.zeroId(), "capabilities^{}");
604 private void recvCommands() throws IOException
{
608 line
= pckIn
.readStringRaw();
609 } catch (EOFException eof
) {
610 if (commands
.isEmpty())
614 if (line
== PacketLineIn
.END
)
617 if (commands
.isEmpty()) {
618 final int nul
= line
.indexOf('\0');
620 for (String c
: line
.substring(nul
+ 1).split(" "))
621 enabledCapablities
.add(c
);
622 line
= line
.substring(0, nul
);
626 if (line
.length() < 83) {
627 final String m
= "error: invalid protocol: wanted 'old new ref'";
629 throw new PackProtocolException(m
);
632 final ObjectId oldId
= ObjectId
.fromString(line
.substring(0, 40));
633 final ObjectId newId
= ObjectId
.fromString(line
.substring(41, 81));
634 final String name
= line
.substring(82);
635 final ReceiveCommand cmd
= new ReceiveCommand(oldId
, newId
, name
);
636 if (name
.equals(Constants
.HEAD
)) {
637 cmd
.setResult(Result
.REJECTED_CURRENT_BRANCH
);
639 cmd
.setRef(refs
.get(cmd
.getRefName()));
645 private void enableCapabilities() {
646 reportStatus
= enabledCapablities
.contains(CAPABILITY_REPORT_STATUS
);
649 private boolean needPack() {
650 for (final ReceiveCommand cmd
: commands
) {
651 if (cmd
.getType() != ReceiveCommand
.Type
.DELETE
)
657 private void receivePack() throws IOException
{
658 // It might take the client a while to pack the objects it needs
659 // to send to us. We should increase our timeout so we don't
660 // abort while the client is computing.
662 if (timeoutIn
!= null)
663 timeoutIn
.setTimeout(10 * timeout
* 1000);
665 final IndexPack ip
= IndexPack
.create(db
, rawIn
);
667 ip
.setObjectChecking(isCheckReceivedObjects());
668 ip
.index(NullProgressMonitor
.INSTANCE
);
670 String lockMsg
= "jgit receive-pack";
671 if (getRefLogIdent() != null)
672 lockMsg
+= " from " + getRefLogIdent().toExternalString();
673 packLock
= ip
.renameAndOpenPack(lockMsg
);
675 if (timeoutIn
!= null)
676 timeoutIn
.setTimeout(timeout
* 1000);
679 private void checkConnectivity() throws IOException
{
680 final ObjectWalk ow
= new ObjectWalk(db
);
681 for (final ReceiveCommand cmd
: commands
) {
682 if (cmd
.getResult() != Result
.NOT_ATTEMPTED
)
684 if (cmd
.getType() == ReceiveCommand
.Type
.DELETE
)
686 ow
.markStart(ow
.parseAny(cmd
.getNewId()));
688 for (final Ref ref
: refs
.values())
689 ow
.markUninteresting(ow
.parseAny(ref
.getObjectId()));
690 ow
.checkConnectivity();
693 private void validateCommands() {
694 for (final ReceiveCommand cmd
: commands
) {
695 final Ref ref
= cmd
.getRef();
696 if (cmd
.getResult() != Result
.NOT_ATTEMPTED
)
699 if (cmd
.getType() == ReceiveCommand
.Type
.DELETE
700 && !isAllowDeletes()) {
701 // Deletes are not supported on this repository.
703 cmd
.setResult(Result
.REJECTED_NODELETE
);
707 if (cmd
.getType() == ReceiveCommand
.Type
.CREATE
) {
708 if (!isAllowCreates()) {
709 cmd
.setResult(Result
.REJECTED_NOCREATE
);
713 if (ref
!= null && !isAllowNonFastForwards()) {
714 // Creation over an existing ref is certainly not going
715 // to be a fast-forward update. We can reject it early.
717 cmd
.setResult(Result
.REJECTED_NONFASTFORWARD
);
722 // A well behaved client shouldn't have sent us a
723 // create command for a ref we advertised to it.
725 cmd
.setResult(Result
.REJECTED_OTHER_REASON
, "ref exists");
730 if (cmd
.getType() == ReceiveCommand
.Type
.DELETE
&& ref
!= null
731 && !ObjectId
.zeroId().equals(cmd
.getOldId())
732 && !ref
.getObjectId().equals(cmd
.getOldId())) {
733 // Delete commands can be sent with the old id matching our
734 // advertised value, *OR* with the old id being 0{40}. Any
735 // other requested old id is invalid.
737 cmd
.setResult(Result
.REJECTED_OTHER_REASON
,
738 "invalid old id sent");
742 if (cmd
.getType() == ReceiveCommand
.Type
.UPDATE
) {
744 // The ref must have been advertised in order to be updated.
746 cmd
.setResult(Result
.REJECTED_OTHER_REASON
, "no such ref");
750 if (!ref
.getObjectId().equals(cmd
.getOldId())) {
751 // A properly functioning client will send the same
752 // object id we advertised.
754 cmd
.setResult(Result
.REJECTED_OTHER_REASON
,
755 "invalid old id sent");
759 // Is this possibly a non-fast-forward style update?
761 RevObject oldObj
, newObj
;
763 oldObj
= walk
.parseAny(cmd
.getOldId());
764 } catch (IOException e
) {
765 cmd
.setResult(Result
.REJECTED_MISSING_OBJECT
, cmd
771 newObj
= walk
.parseAny(cmd
.getNewId());
772 } catch (IOException e
) {
773 cmd
.setResult(Result
.REJECTED_MISSING_OBJECT
, cmd
778 if (oldObj
instanceof RevCommit
&& newObj
instanceof RevCommit
) {
780 if (!walk
.isMergedInto((RevCommit
) oldObj
,
781 (RevCommit
) newObj
)) {
783 .setType(ReceiveCommand
.Type
.UPDATE_NONFASTFORWARD
);
785 } catch (MissingObjectException e
) {
786 cmd
.setResult(Result
.REJECTED_MISSING_OBJECT
, e
788 } catch (IOException e
) {
789 cmd
.setResult(Result
.REJECTED_OTHER_REASON
);
792 cmd
.setType(ReceiveCommand
.Type
.UPDATE_NONFASTFORWARD
);
796 if (!cmd
.getRefName().startsWith(Constants
.R_REFS
)
797 || !Repository
.isValidRefName(cmd
.getRefName())) {
798 cmd
.setResult(Result
.REJECTED_OTHER_REASON
, "funny refname");
803 private void executeCommands() {
804 preReceive
.onPreReceive(this, filterCommands(Result
.NOT_ATTEMPTED
));
805 for (final ReceiveCommand cmd
: filterCommands(Result
.NOT_ATTEMPTED
))
809 private void execute(final ReceiveCommand cmd
) {
811 final RefUpdate ru
= db
.updateRef(cmd
.getRefName());
812 ru
.setRefLogIdent(getRefLogIdent());
813 switch (cmd
.getType()) {
815 if (!ObjectId
.zeroId().equals(cmd
.getOldId())) {
816 // We can only do a CAS style delete if the client
817 // didn't bork its delete request by sending the
818 // wrong zero id rather than the advertised one.
820 ru
.setExpectedOldObjectId(cmd
.getOldId());
822 ru
.setForceUpdate(true);
823 status(cmd
, ru
.delete(walk
));
828 case UPDATE_NONFASTFORWARD
:
829 ru
.setForceUpdate(isAllowNonFastForwards());
830 ru
.setExpectedOldObjectId(cmd
.getOldId());
831 ru
.setNewObjectId(cmd
.getNewId());
832 ru
.setRefLogMessage("push", true);
833 status(cmd
, ru
.update(walk
));
836 } catch (IOException err
) {
837 cmd
.setResult(Result
.REJECTED_OTHER_REASON
, "lock error: "
842 private void status(final ReceiveCommand cmd
, final RefUpdate
.Result result
) {
845 cmd
.setResult(Result
.NOT_ATTEMPTED
);
850 cmd
.setResult(Result
.LOCK_FAILURE
);
857 cmd
.setResult(Result
.OK
);
861 cmd
.setResult(Result
.REJECTED_NONFASTFORWARD
);
864 case REJECTED_CURRENT_BRANCH
:
865 cmd
.setResult(Result
.REJECTED_CURRENT_BRANCH
);
869 cmd
.setResult(Result
.REJECTED_OTHER_REASON
, result
.name());
874 private List
<ReceiveCommand
> filterCommands(final Result want
) {
875 final List
<ReceiveCommand
> r
= new ArrayList
<ReceiveCommand
>(commands
877 for (final ReceiveCommand cmd
: commands
) {
878 if (cmd
.getResult() == want
)
884 private void sendStatusReport(final boolean forClient
, final Reporter out
)
886 if (unpackError
!= null) {
887 out
.sendString("unpack error " + unpackError
.getMessage());
889 for (final ReceiveCommand cmd
: commands
) {
890 out
.sendString("ng " + cmd
.getRefName()
891 + " n/a (unpacker error)");
898 out
.sendString("unpack ok");
899 for (final ReceiveCommand cmd
: commands
) {
900 if (cmd
.getResult() == Result
.OK
) {
902 out
.sendString("ok " + cmd
.getRefName());
906 final StringBuilder r
= new StringBuilder();
908 r
.append(cmd
.getRefName());
911 switch (cmd
.getResult()) {
913 r
.append("server bug; ref not processed");
916 case REJECTED_NOCREATE
:
917 r
.append("creation prohibited");
920 case REJECTED_NODELETE
:
921 r
.append("deletion prohibited");
924 case REJECTED_NONFASTFORWARD
:
925 r
.append("non-fast forward");
928 case REJECTED_CURRENT_BRANCH
:
929 r
.append("branch is currently checked out");
932 case REJECTED_MISSING_OBJECT
:
933 if (cmd
.getMessage() == null)
934 r
.append("missing object(s)");
935 else if (cmd
.getMessage().length() == Constants
.OBJECT_ID_STRING_LENGTH
)
936 r
.append("object " + cmd
.getMessage() + " missing");
938 r
.append(cmd
.getMessage());
941 case REJECTED_OTHER_REASON
:
942 if (cmd
.getMessage() == null)
943 r
.append("unspecified reason");
945 r
.append(cmd
.getMessage());
949 r
.append("failed to lock");
953 // We shouldn't have reached this case (see 'ok' case above).
956 out
.sendString(r
.toString());
960 static abstract class Reporter
{
961 abstract void sendString(String s
) throws IOException
;