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.transport;
45
46 import java.io.IOException;
47 import java.io.OutputStream;
48 import java.text.MessageFormat;
49 import java.util.Collection;
50 import java.util.Collections;
51 import java.util.LinkedHashMap;
52 import java.util.List;
53 import java.util.Map;
54
55 import org.eclipse.jgit.errors.MissingObjectException;
56 import org.eclipse.jgit.errors.NotSupportedException;
57 import org.eclipse.jgit.errors.TransportException;
58 import org.eclipse.jgit.internal.JGitText;
59 import org.eclipse.jgit.lib.ObjectId;
60 import org.eclipse.jgit.lib.ProgressMonitor;
61 import org.eclipse.jgit.lib.Ref;
62 import org.eclipse.jgit.revwalk.RevCommit;
63 import org.eclipse.jgit.revwalk.RevObject;
64 import org.eclipse.jgit.revwalk.RevWalk;
65 import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
66
67
68
69
70
71
72 class PushProcess {
73
74 static final String PROGRESS_OPENING_CONNECTION = JGitText.get().openingConnection;
75
76
77 private final Transport transport;
78
79
80 private PushConnection connection;
81
82
83 private final Map<String, RemoteRefUpdate> toPush;
84
85
86 private final RevWalk walker;
87
88
89 private final OutputStream out;
90
91
92 private List<String> pushOptions;
93
94
95
96
97
98
99
100
101
102
103
104
105 PushProcess(final Transport transport,
106 final Collection<RemoteRefUpdate> toPush) throws TransportException {
107 this(transport, toPush, null);
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121
122 PushProcess(final Transport transport,
123 final Collection<RemoteRefUpdate> toPush, OutputStream out)
124 throws TransportException {
125 this.walker = new RevWalk(transport.local);
126 this.transport = transport;
127 this.toPush = new LinkedHashMap<>();
128 this.out = out;
129 this.pushOptions = transport.getPushOptions();
130 for (RemoteRefUpdate rru : toPush) {
131 if (this.toPush.put(rru.getRemoteName(), rru) != null)
132 throw new TransportException(MessageFormat.format(
133 JGitText.get().duplicateRemoteRefUpdateIsIllegal, rru.getRemoteName()));
134 }
135 }
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153 PushResult execute(ProgressMonitor monitor)
154 throws NotSupportedException, TransportException {
155 try {
156 monitor.beginTask(PROGRESS_OPENING_CONNECTION,
157 ProgressMonitor.UNKNOWN);
158
159 final PushResultshResult.html#PushResult">PushResult res = new PushResult();
160 connection = transport.openPush();
161 try {
162 res.setAdvertisedRefs(transport.getURI(), connection
163 .getRefsMap());
164 res.peerUserAgent = connection.getPeerUserAgent();
165 res.setRemoteUpdates(toPush);
166 monitor.endTask();
167
168 final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates();
169 if (transport.isDryRun())
170 modifyUpdatesForDryRun();
171 else if (!preprocessed.isEmpty())
172 connection.push(monitor, preprocessed, out);
173 } finally {
174 connection.close();
175 res.addMessages(connection.getMessages());
176 }
177 if (!transport.isDryRun())
178 updateTrackingRefs();
179 for (RemoteRefUpdate rru : toPush.values()) {
180 final TrackingRefUpdate tru = rru.getTrackingRefUpdate();
181 if (tru != null)
182 res.add(tru);
183 }
184 return res;
185 } finally {
186 walker.close();
187 }
188 }
189
190 private Map<String, RemoteRefUpdate> prepareRemoteUpdates()
191 throws TransportException {
192 boolean atomic = transport.isPushAtomic();
193 final Map<String, RemoteRefUpdate> result = new LinkedHashMap<>();
194 for (RemoteRefUpdate rru : toPush.values()) {
195 final Ref advertisedRef = connection.getRef(rru.getRemoteName());
196 ObjectId advertisedOld = null;
197 if (advertisedRef != null) {
198 advertisedOld = advertisedRef.getObjectId();
199 }
200 if (advertisedOld == null) {
201 advertisedOld = ObjectId.zeroId();
202 }
203
204 if (rru.getNewObjectId().equals(advertisedOld)) {
205 if (rru.isDelete()) {
206
207 rru.setStatus(Status.NON_EXISTING);
208 } else {
209
210 rru.setStatus(Status.UP_TO_DATE);
211 }
212 continue;
213 }
214
215
216
217 if (rru.isExpectingOldObjectId()
218 && !rru.getExpectedOldObjectId().equals(advertisedOld)) {
219 rru.setStatus(Status.REJECTED_REMOTE_CHANGED);
220 if (atomic) {
221 return rejectAll();
222 }
223 continue;
224 }
225 if (!rru.isExpectingOldObjectId()) {
226 rru.setExpectedOldObjectId(advertisedOld);
227 }
228
229
230
231 if (advertisedOld.equals(ObjectId.zeroId()) || rru.isDelete()) {
232 rru.setFastForward(true);
233 result.put(rru.getRemoteName(), rru);
234 continue;
235 }
236
237
238
239
240
241 boolean fastForward = true;
242 try {
243 RevObject oldRev = walker.parseAny(advertisedOld);
244 final RevObject newRev = walker.parseAny(rru.getNewObjectId());
245 if (!(oldRev instanceof RevCommit)
246 || !(newRev instanceof RevCommit)
247 || !walker.isMergedInto((RevCommit) oldRev,
248 (RevCommit) newRev))
249 fastForward = false;
250 } catch (MissingObjectException x) {
251 fastForward = false;
252 } catch (Exception x) {
253 throw new TransportException(transport.getURI(), MessageFormat.format(
254 JGitText.get().readingObjectsFromLocalRepositoryFailed, x.getMessage()), x);
255 }
256 rru.setFastForward(fastForward);
257 if (!fastForward && !rru.isForceUpdate()) {
258 rru.setStatus(Status.REJECTED_NONFASTFORWARD);
259 if (atomic) {
260 return rejectAll();
261 }
262 } else {
263 result.put(rru.getRemoteName(), rru);
264 }
265 }
266 return result;
267 }
268
269 private Map<String, RemoteRefUpdate> rejectAll() {
270 for (RemoteRefUpdate rru : toPush.values()) {
271 if (rru.getStatus() == Status.NOT_ATTEMPTED) {
272 rru.setStatus(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
273 rru.setMessage(JGitText.get().transactionAborted);
274 }
275 }
276 return Collections.emptyMap();
277 }
278
279 private void modifyUpdatesForDryRun() {
280 for (RemoteRefUpdate rru : toPush.values())
281 if (rru.getStatus() == Status.NOT_ATTEMPTED)
282 rru.setStatus(Status.OK);
283 }
284
285 private void updateTrackingRefs() {
286 for (RemoteRefUpdate rru : toPush.values()) {
287 final Status status = rru.getStatus();
288 if (rru.hasTrackingRefUpdate()
289 && (status == Status.UP_TO_DATE || status == Status.OK)) {
290
291
292
293
294 try {
295 rru.updateTrackingRef(walker);
296 } catch (IOException e) {
297
298 }
299 }
300 }
301 }
302
303
304
305
306
307
308
309 public List<String> getPushOptions() {
310 return pushOptions;
311 }
312 }