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