1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.internal.transport.connectivity;
12
13 import static java.util.stream.Collectors.toList;
14
15 import java.io.IOException;
16 import java.util.ArrayDeque;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Queue;
22 import java.util.Set;
23 import java.util.stream.Stream;
24
25 import org.eclipse.jgit.errors.MissingObjectException;
26 import org.eclipse.jgit.lib.ObjectId;
27 import org.eclipse.jgit.lib.ProgressMonitor;
28 import org.eclipse.jgit.revwalk.RevCommit;
29 import org.eclipse.jgit.revwalk.RevObject;
30 import org.eclipse.jgit.revwalk.RevWalk;
31 import org.eclipse.jgit.transport.ConnectivityChecker;
32 import org.eclipse.jgit.transport.ReceiveCommand;
33
34
35
36
37
38
39
40
41 public class IterativeConnectivityChecker implements ConnectivityChecker {
42 private static final int MAXIMUM_PARENTS_TO_CHECK = 128;
43
44 private final ConnectivityChecker delegate;
45
46 private Set<ObjectId> forcedHaves = Collections.emptySet();
47
48
49
50
51
52 public IterativeConnectivityChecker(ConnectivityChecker delegate) {
53 this.delegate = delegate;
54 }
55
56 @Override
57 public void checkConnectivity(ConnectivityCheckInfo connectivityCheckInfo,
58 Set<ObjectId> advertisedHaves, ProgressMonitor pm)
59 throws MissingObjectException, IOException {
60 try {
61 Set<ObjectId> newRefs = new HashSet<>();
62 Set<ObjectId> expectedParents = new HashSet<>();
63
64 getAllObjectIds(connectivityCheckInfo.getCommands())
65 .forEach(oid -> {
66 if (advertisedHaves.contains(oid)) {
67 expectedParents.add(oid);
68 } else {
69 newRefs.add(oid);
70 }
71 });
72 if (!newRefs.isEmpty()) {
73 expectedParents.addAll(extractAdvertisedParentCommits(newRefs,
74 advertisedHaves, connectivityCheckInfo.getWalk()));
75 }
76
77 expectedParents.addAll(forcedHaves);
78
79 if (!expectedParents.isEmpty()) {
80 delegate.checkConnectivity(connectivityCheckInfo,
81 expectedParents, pm);
82 return;
83 }
84 } catch (MissingObjectException e) {
85
86 }
87 delegate.checkConnectivity(connectivityCheckInfo, advertisedHaves, pm);
88 }
89
90 private static Stream<ObjectId> getAllObjectIds(
91 List<ReceiveCommand> commands) {
92 return commands.stream().flatMap(cmd -> {
93 if (cmd.getType() == ReceiveCommand.Type.UPDATE || cmd
94 .getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD) {
95 return Stream.of(cmd.getOldId(), cmd.getNewId());
96 } else if (cmd.getType() == ReceiveCommand.Type.CREATE) {
97 return Stream.of(cmd.getNewId());
98 }
99 return Stream.of();
100 });
101 }
102
103
104
105
106
107
108
109 public void setForcedHaves(Set<ObjectId> forcedHaves) {
110 this.forcedHaves = Collections.unmodifiableSet(forcedHaves);
111 }
112
113 private static Set<ObjectId> extractAdvertisedParentCommits(
114 Set<ObjectId> newRefs, Set<ObjectId> advertisedHaves, RevWalk rw)
115 throws MissingObjectException, IOException {
116 Set<ObjectId> advertisedParents = new HashSet<>();
117 for (ObjectId newRef : newRefs) {
118 RevObject object = rw.parseAny(newRef);
119 if (object instanceof RevCommit) {
120 int numberOfParentsToCheck = 0;
121 Queue<RevCommit> parents = new ArrayDeque<>(
122 MAXIMUM_PARENTS_TO_CHECK);
123 parents.addAll(
124 parseParents(((RevCommit) object).getParents(), rw));
125
126
127 while (!parents.isEmpty()) {
128 RevCommit parentCommit = parents.poll();
129 if (advertisedHaves.contains(parentCommit.getId())) {
130 advertisedParents.add(parentCommit.getId());
131 } else if (numberOfParentsToCheck < MAXIMUM_PARENTS_TO_CHECK) {
132 RevCommit[] grandParents = parentCommit.getParents();
133 numberOfParentsToCheck += grandParents.length;
134 parents.addAll(parseParents(grandParents, rw));
135 }
136 }
137 }
138 }
139 return advertisedParents;
140 }
141
142 private static List<RevCommit> parseParents(RevCommit[] parents,
143 RevWalk rw) {
144 return Arrays.stream(parents).map((commit) -> {
145 try {
146 return rw.parseCommit(commit);
147 } catch (Exception e) {
148 throw new RuntimeException(e);
149 }
150 }).collect(toList());
151 }
152 }