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 @Override
90 public void close() {
91 if (rw != null) {
92 rw.close();
93 rw = null;
94 }
95 }
96
97 ObjectId getRemoteId() {
98 return remoteId;
99 }
100
101 KetchReplica.State check(ObjectId acceptId, ReceiveCommand acceptCmd) {
102 remoteId = acceptId;
103 if (remoteId == null) {
104
105 return UNKNOWN;
106 }
107
108 if (AnyObjectId.equals(remoteId, ObjectId.zeroId())) {
109
110 return LAGGING;
111 }
112
113 try {
114 RevCommit remote;
115 try {
116 remote = parseRemoteCommit(acceptCmd.getRefName());
117 } catch (RefGoneException gone) {
118
119 return LAGGING;
120 } catch (MissingObjectException notFound) {
121
122
123 return DIVERGENT;
124 }
125
126 RevCommit head = rw.parseCommit(acceptCmd.getNewId());
127 if (rw.isMergedInto(remote, head)) {
128 return LAGGING;
129 }
130
131
132 if (rw.isMergedInto(head, remote)) {
133 return AHEAD;
134 } else {
135 return DIVERGENT;
136 }
137 } catch (IOException err) {
138 KetchReplica.log.error(String.format(
139 "Cannot compare %s",
140 acceptCmd.getRefName()), err);
141 return UNKNOWN;
142 }
143 }
144
145 private RevCommit parseRemoteCommit(String refName)
146 throws IOException, MissingObjectException, RefGoneException {
147 try {
148 return rw.parseCommit(remoteId);
149 } catch (MissingObjectException notLocal) {
150
151 }
152
153 ReplicaFetchRequest fetch = new ReplicaFetchRequest(
154 Collections.singleton(refName),
155 Collections.<ObjectId> emptySet());
156 try {
157 replica.blockingFetch(repo, fetch);
158 } catch (IOException fetchErr) {
159 KetchReplica.log.error(String.format(
160 "Cannot fetch %s (%s) from %s",
161 remoteId.abbreviate(8).name(), refName,
162 replica.describeForLog()), fetchErr);
163 throw new MissingObjectException(remoteId, OBJ_COMMIT);
164 }
165
166 Map<String, Ref> adv = fetch.getRefs();
167 if (adv == null) {
168 throw new MissingObjectException(remoteId, OBJ_COMMIT);
169 }
170
171 Ref ref = adv.get(refName);
172 if (ref == null || ref.getObjectId() == null) {
173 throw new RefGoneException();
174 }
175
176 initRevWalk();
177 remoteId = ref.getObjectId();
178 return rw.parseCommit(remoteId);
179 }
180
181 private static class RefGoneException extends Exception {
182 private static final long serialVersionUID = 1L;
183 }
184 }