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 static org.eclipse.jgit.transport.WalkRemoteObjectDatabase.ROOT_DIR;
47
48 import java.io.BufferedOutputStream;
49 import java.io.IOException;
50 import java.io.OutputStream;
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.HashSet;
54 import java.util.LinkedHashMap;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.Set;
58 import java.util.TreeMap;
59
60 import org.eclipse.jgit.errors.TransportException;
61 import org.eclipse.jgit.internal.JGitText;
62 import org.eclipse.jgit.internal.storage.pack.PackWriter;
63 import org.eclipse.jgit.lib.AnyObjectId;
64 import org.eclipse.jgit.lib.Constants;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.ObjectIdRef;
67 import org.eclipse.jgit.lib.ProgressMonitor;
68 import org.eclipse.jgit.lib.Ref;
69 import org.eclipse.jgit.lib.Ref.Storage;
70 import org.eclipse.jgit.lib.RefWriter;
71 import org.eclipse.jgit.lib.Repository;
72 import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 class WalkPushConnection extends BaseConnection implements PushConnection {
99
100 private final Repository local;
101
102
103 private final URIish uri;
104
105
106 final WalkRemoteObjectDatabase dest;
107
108
109 private final Transport transport;
110
111
112
113
114
115
116 private LinkedHashMap<String, String> packNames;
117
118
119 private Map<String, Ref> newRefs;
120
121
122
123
124
125
126
127 private Collection<RemoteRefUpdate> packedRefUpdates;
128
129 WalkPushConnection(final WalkTransport walkTransport,
130 final WalkRemoteObjectDatabase w) {
131 transport = (Transport) walkTransport;
132 local = transport.local;
133 uri = transport.getURI();
134 dest = w;
135 }
136
137
138 @Override
139 public void push(final ProgressMonitor monitor,
140 final Map<String, RemoteRefUpdate> refUpdates)
141 throws TransportException {
142 push(monitor, refUpdates, null);
143 }
144
145
146 @Override
147 public void push(final ProgressMonitor monitor,
148 final Map<String, RemoteRefUpdate> refUpdates, OutputStream out)
149 throws TransportException {
150 markStartedOperation();
151 packNames = null;
152 newRefs = new TreeMap<>(getRefsMap());
153 packedRefUpdates = new ArrayList<>(refUpdates.size());
154
155
156
157
158
159 final List<RemoteRefUpdate> updates = new ArrayList<>();
160 for (RemoteRefUpdate u : refUpdates.values()) {
161 final String n = u.getRemoteName();
162 if (!n.startsWith("refs/") || !Repository.isValidRefName(n)) {
163 u.setStatus(Status.REJECTED_OTHER_REASON);
164 u.setMessage(JGitText.get().funnyRefname);
165 continue;
166 }
167
168 if (AnyObjectId.equals(ObjectId.zeroId(), u.getNewObjectId()))
169 deleteCommand(u);
170 else
171 updates.add(u);
172 }
173
174
175
176
177
178 if (!updates.isEmpty())
179 sendpack(updates, monitor);
180 for (RemoteRefUpdate u : updates)
181 updateCommand(u);
182
183
184
185
186 if (!updates.isEmpty() && isNewRepository())
187 createNewRepository(updates);
188
189 RefWriter refWriter = new RefWriter(newRefs.values()) {
190 @Override
191 protected void writeFile(String file, byte[] content)
192 throws IOException {
193 dest.writeFile(ROOT_DIR + file, content);
194 }
195 };
196 if (!packedRefUpdates.isEmpty()) {
197 try {
198 refWriter.writePackedRefs();
199 for (RemoteRefUpdate u : packedRefUpdates)
200 u.setStatus(Status.OK);
201 } catch (IOException err) {
202 for (RemoteRefUpdate u : packedRefUpdates) {
203 u.setStatus(Status.REJECTED_OTHER_REASON);
204 u.setMessage(err.getMessage());
205 }
206 throw new TransportException(uri, JGitText.get().failedUpdatingRefs, err);
207 }
208 }
209
210 try {
211 refWriter.writeInfoRefs();
212 } catch (IOException err) {
213 throw new TransportException(uri, JGitText.get().failedUpdatingRefs, err);
214 }
215 }
216
217
218 @Override
219 public void close() {
220 dest.close();
221 }
222
223 private void sendpack(final List<RemoteRefUpdate> updates,
224 final ProgressMonitor monitor) throws TransportException {
225 String pathPack = null;
226 String pathIdx = null;
227
228 try (PackWriter writer = new PackWriter(transport.getPackConfig(),
229 local.newObjectReader())) {
230
231 final Set<ObjectId> need = new HashSet<>();
232 final Set<ObjectId> have = new HashSet<>();
233 for (RemoteRefUpdate r : updates)
234 need.add(r.getNewObjectId());
235 for (Ref r : getRefs()) {
236 have.add(r.getObjectId());
237 if (r.getPeeledObjectId() != null)
238 have.add(r.getPeeledObjectId());
239 }
240 writer.preparePack(monitor, need, have);
241
242
243
244
245
246 if (writer.getObjectCount() == 0)
247 return;
248
249 packNames = new LinkedHashMap<>();
250 for (String n : dest.getPackNames())
251 packNames.put(n, n);
252
253 final String base = "pack-" + writer.computeName().name();
254 final String packName = base + ".pack";
255 pathPack = "pack/" + packName;
256 pathIdx = "pack/" + base + ".idx";
257
258 if (packNames.remove(packName) != null) {
259
260
261
262
263 dest.writeInfoPacks(packNames.keySet());
264 dest.deleteFile(pathIdx);
265 }
266
267
268
269
270 String wt = "Put " + base.substring(0, 12);
271 try (OutputStream os = new BufferedOutputStream(
272 dest.writeFile(pathPack, monitor, wt + "..pack"))) {
273 writer.writePack(monitor, monitor, os);
274 }
275
276 try (OutputStream os = new BufferedOutputStream(
277 dest.writeFile(pathIdx, monitor, wt + "..idx"))) {
278 writer.writeIndex(os);
279 }
280
281
282
283
284
285 final ArrayList<String> infoPacks = new ArrayList<>();
286 infoPacks.add(packName);
287 infoPacks.addAll(packNames.keySet());
288 dest.writeInfoPacks(infoPacks);
289
290 } catch (IOException err) {
291 safeDelete(pathIdx);
292 safeDelete(pathPack);
293
294 throw new TransportException(uri, JGitText.get().cannotStoreObjects, err);
295 }
296 }
297
298 private void safeDelete(String path) {
299 if (path != null) {
300 try {
301 dest.deleteFile(path);
302 } catch (IOException cleanupFailure) {
303
304
305
306 }
307 }
308 }
309
310 private void deleteCommand(RemoteRefUpdate u) {
311 final Ref r = newRefs.remove(u.getRemoteName());
312 if (r == null) {
313
314
315 u.setStatus(Status.OK);
316 return;
317 }
318
319 if (r.getStorage().isPacked())
320 packedRefUpdates.add(u);
321
322 if (r.getStorage().isLoose()) {
323 try {
324 dest.deleteRef(u.getRemoteName());
325 u.setStatus(Status.OK);
326 } catch (IOException e) {
327 u.setStatus(Status.REJECTED_OTHER_REASON);
328 u.setMessage(e.getMessage());
329 }
330 }
331
332 try {
333 dest.deleteRefLog(u.getRemoteName());
334 } catch (IOException e) {
335 u.setStatus(Status.REJECTED_OTHER_REASON);
336 u.setMessage(e.getMessage());
337 }
338 }
339
340 private void updateCommand(RemoteRefUpdate u) {
341 try {
342 dest.writeRef(u.getRemoteName(), u.getNewObjectId());
343 newRefs.put(u.getRemoteName(), new ObjectIdRef.Unpeeled(
344 Storage.LOOSE, u.getRemoteName(), u.getNewObjectId()));
345 u.setStatus(Status.OK);
346 } catch (IOException e) {
347 u.setStatus(Status.REJECTED_OTHER_REASON);
348 u.setMessage(e.getMessage());
349 }
350 }
351
352 private boolean isNewRepository() {
353 return getRefsMap().isEmpty() && packNames != null
354 && packNames.isEmpty();
355 }
356
357 private void createNewRepository(List<RemoteRefUpdate> updates)
358 throws TransportException {
359 try {
360 final String ref = "ref: " + pickHEAD(updates) + "\n";
361 final byte[] bytes = Constants.encode(ref);
362 dest.writeFile(ROOT_DIR + Constants.HEAD, bytes);
363 } catch (IOException e) {
364 throw new TransportException(uri, JGitText.get().cannotCreateHEAD, e);
365 }
366
367 try {
368 final String config = "[core]\n"
369 + "\trepositoryformatversion = 0\n";
370 final byte[] bytes = Constants.encode(config);
371 dest.writeFile(ROOT_DIR + Constants.CONFIG, bytes);
372 } catch (IOException e) {
373 throw new TransportException(uri, JGitText.get().cannotCreateConfig, e);
374 }
375 }
376
377 private static String pickHEAD(List<RemoteRefUpdate> updates) {
378
379
380
381
382 for (RemoteRefUpdate u : updates) {
383 final String n = u.getRemoteName();
384 if (n.equals(Constants.R_HEADS + Constants.MASTER))
385 return n;
386 }
387
388
389
390
391 for (RemoteRefUpdate u : updates) {
392 final String n = u.getRemoteName();
393 if (n.startsWith(Constants.R_HEADS))
394 return n;
395 }
396 return updates.get(0).getRemoteName();
397 }
398 }