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