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 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 }