1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.internal.ketch;
45
46 import static org.eclipse.jgit.internal.ketch.KetchReplica.State.AHEAD;
47 import static org.eclipse.jgit.internal.ketch.KetchReplica.State.DIVERGENT;
48 import static org.eclipse.jgit.internal.ketch.KetchReplica.State.LAGGING;
49 import static org.eclipse.jgit.internal.ketch.KetchReplica.State.UNKNOWN;
50 import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
51
52 import java.io.IOException;
53 import java.util.Collections;
54 import java.util.Map;
55
56 import org.eclipse.jgit.errors.MissingObjectException;
57 import org.eclipse.jgit.lib.AnyObjectId;
58 import org.eclipse.jgit.lib.ObjectId;
59 import org.eclipse.jgit.lib.Ref;
60 import org.eclipse.jgit.lib.Repository;
61 import org.eclipse.jgit.revwalk.RevCommit;
62 import org.eclipse.jgit.revwalk.RevWalk;
63 import org.eclipse.jgit.transport.ReceiveCommand;
64
65
66
67
68 class LagCheck implements AutoCloseable {
69 private final KetchReplica replica;
70 private final Repository repo;
71 private RevWalk rw;
72 private ObjectId remoteId;
73
74 LagCheck(KetchReplica replica, Repository repo) {
75 this.replica = replica;
76 this.repo = repo;
77 initRevWalk();
78 }
79
80 private void initRevWalk() {
81 if (rw != null) {
82 rw.close();
83 }
84
85 rw = new RevWalk(repo);
86 rw.setRetainBody(false);
87 }
88
89
90 @Override
91 public void close() {
92 if (rw != null) {
93 rw.close();
94 rw = null;
95 }
96 }
97
98 ObjectId getRemoteId() {
99 return remoteId;
100 }
101
102 KetchReplica.State check(ObjectId acceptId, ReceiveCommand acceptCmd) {
103 remoteId = acceptId;
104 if (remoteId == null) {
105
106 return UNKNOWN;
107 }
108
109 if (AnyObjectId.equals(remoteId, ObjectId.zeroId())) {
110
111 return LAGGING;
112 }
113
114 try {
115 RevCommit remote;
116 try {
117 remote = parseRemoteCommit(acceptCmd.getRefName());
118 } catch (RefGoneException gone) {
119
120 return LAGGING;
121 } catch (MissingObjectException notFound) {
122
123
124 return DIVERGENT;
125 }
126
127 RevCommit head = rw.parseCommit(acceptCmd.getNewId());
128 if (rw.isMergedInto(remote, head)) {
129 return LAGGING;
130 }
131
132
133 if (rw.isMergedInto(head, remote)) {
134 return AHEAD;
135 } else {
136 return DIVERGENT;
137 }
138 } catch (IOException err) {
139 KetchReplica.log.error(String.format(
140 "Cannot compare %s",
141 acceptCmd.getRefName()), err);
142 return UNKNOWN;
143 }
144 }
145
146 private RevCommit parseRemoteCommit(String refName)
147 throws IOException, MissingObjectException, RefGoneException {
148 try {
149 return rw.parseCommit(remoteId);
150 } catch (MissingObjectException notLocal) {
151
152 }
153
154 ReplicaFetchRequest fetch = new ReplicaFetchRequest(
155 Collections.singleton(refName),
156 Collections.<ObjectId> emptySet());
157 try {
158 replica.blockingFetch(repo, fetch);
159 } catch (IOException fetchErr) {
160 KetchReplica.log.error(String.format(
161 "Cannot fetch %s (%s) from %s",
162 remoteId.abbreviate(8).name(), refName,
163 replica.describeForLog()), fetchErr);
164 throw new MissingObjectException(remoteId, OBJ_COMMIT);
165 }
166
167 Map<String, Ref> adv = fetch.getRefs();
168 if (adv == null) {
169 throw new MissingObjectException(remoteId, OBJ_COMMIT);
170 }
171
172 Ref ref = adv.get(refName);
173 if (ref == null || ref.getObjectId() == null) {
174 throw new RefGoneException();
175 }
176
177 initRevWalk();
178 remoteId = ref.getObjectId();
179 return rw.parseCommit(remoteId);
180 }
181
182 private static class RefGoneException extends Exception {
183 private static final long serialVersionUID = 1L;
184 }
185 }