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 @Override
138 public void push(final ProgressMonitor monitor,
139 final Map<String, RemoteRefUpdate> refUpdates)
140 throws TransportException {
141 push(monitor, refUpdates, null);
142 }
143
144 @Override
145 public void push(final ProgressMonitor monitor,
146 final Map<String, RemoteRefUpdate> refUpdates, OutputStream out)
147 throws TransportException {
148 markStartedOperation();
149 packNames = null;
150 newRefs = new TreeMap<>(getRefsMap());
151 packedRefUpdates = new ArrayList<>(refUpdates.size());
152
153
154
155
156
157 final List<RemoteRefUpdate> updates = new ArrayList<>();
158 for (final RemoteRefUpdate u : refUpdates.values()) {
159 final String n = u.getRemoteName();
160 if (!n.startsWith("refs/") || !Repository.isValidRefName(n)) {
161 u.setStatus(Status.REJECTED_OTHER_REASON);
162 u.setMessage(JGitText.get().funnyRefname);
163 continue;
164 }
165
166 if (AnyObjectId.equals(ObjectId.zeroId(), u.getNewObjectId()))
167 deleteCommand(u);
168 else
169 updates.add(u);
170 }
171
172
173
174
175
176 if (!updates.isEmpty())
177 sendpack(updates, monitor);
178 for (final RemoteRefUpdate u : updates)
179 updateCommand(u);
180
181
182
183
184 if (!updates.isEmpty() && isNewRepository())
185 createNewRepository(updates);
186
187 RefWriter refWriter = new RefWriter(newRefs.values()) {
188 @Override
189 protected void writeFile(String file, byte[] content)
190 throws IOException {
191 dest.writeFile(ROOT_DIR + file, content);
192 }
193 };
194 if (!packedRefUpdates.isEmpty()) {
195 try {
196 refWriter.writePackedRefs();
197 for (final RemoteRefUpdate u : packedRefUpdates)
198 u.setStatus(Status.OK);
199 } catch (IOException err) {
200 for (final RemoteRefUpdate u : packedRefUpdates) {
201 u.setStatus(Status.REJECTED_OTHER_REASON);
202 u.setMessage(err.getMessage());
203 }
204 throw new TransportException(uri, JGitText.get().failedUpdatingRefs, err);
205 }
206 }
207
208 try {
209 refWriter.writeInfoRefs();
210 } catch (IOException err) {
211 throw new TransportException(uri, JGitText.get().failedUpdatingRefs, err);
212 }
213 }
214
215 @Override
216 public void close() {
217 dest.close();
218 }
219
220 private void sendpack(final List<RemoteRefUpdate> updates,
221 final ProgressMonitor monitor) throws TransportException {
222 String pathPack = null;
223 String pathIdx = null;
224
225 try (final PackWriter writer = new PackWriter(transport.getPackConfig(),
226 local.newObjectReader())) {
227
228 final Set<ObjectId> need = new HashSet<>();
229 final Set<ObjectId> have = new HashSet<>();
230 for (final RemoteRefUpdate r : updates)
231 need.add(r.getNewObjectId());
232 for (final Ref r : getRefs()) {
233 have.add(r.getObjectId());
234 if (r.getPeeledObjectId() != null)
235 have.add(r.getPeeledObjectId());
236 }
237 writer.preparePack(monitor, need, have);
238
239
240
241
242
243 if (writer.getObjectCount() == 0)
244 return;
245
246 packNames = new LinkedHashMap<>();
247 for (final String n : dest.getPackNames())
248 packNames.put(n, n);
249
250 final String base = "pack-" + writer.computeName().name();
251 final String packName = base + ".pack";
252 pathPack = "pack/" + packName;
253 pathIdx = "pack/" + base + ".idx";
254
255 if (packNames.remove(packName) != null) {
256
257
258
259
260 dest.writeInfoPacks(packNames.keySet());
261 dest.deleteFile(pathIdx);
262 }
263
264
265
266
267 String wt = "Put " + base.substring(0, 12);
268 try (OutputStream os = new BufferedOutputStream(
269 dest.writeFile(pathPack, monitor, wt + "..pack"))) {
270 writer.writePack(monitor, monitor, os);
271 }
272
273 try (OutputStream os = new BufferedOutputStream(
274 dest.writeFile(pathIdx, monitor, wt + "..idx"))) {
275 writer.writeIndex(os);
276 }
277
278
279
280
281
282 final ArrayList<String> infoPacks = new ArrayList<>();
283 infoPacks.add(packName);
284 infoPacks.addAll(packNames.keySet());
285 dest.writeInfoPacks(infoPacks);
286
287 } catch (IOException err) {
288 safeDelete(pathIdx);
289 safeDelete(pathPack);
290
291 throw new TransportException(uri, JGitText.get().cannotStoreObjects, err);
292 }
293 }
294
295 private void safeDelete(final String path) {
296 if (path != null) {
297 try {
298 dest.deleteFile(path);
299 } catch (IOException cleanupFailure) {
300
301
302
303 }
304 }
305 }
306
307 private void deleteCommand(final RemoteRefUpdate u) {
308 final Ref r = newRefs.remove(u.getRemoteName());
309 if (r == null) {
310
311
312 u.setStatus(Status.OK);
313 return;
314 }
315
316 if (r.getStorage().isPacked())
317 packedRefUpdates.add(u);
318
319 if (r.getStorage().isLoose()) {
320 try {
321 dest.deleteRef(u.getRemoteName());
322 u.setStatus(Status.OK);
323 } catch (IOException e) {
324 u.setStatus(Status.REJECTED_OTHER_REASON);
325 u.setMessage(e.getMessage());
326 }
327 }
328
329 try {
330 dest.deleteRefLog(u.getRemoteName());
331 } catch (IOException e) {
332 u.setStatus(Status.REJECTED_OTHER_REASON);
333 u.setMessage(e.getMessage());
334 }
335 }
336
337 private void updateCommand(final RemoteRefUpdate u) {
338 try {
339 dest.writeRef(u.getRemoteName(), u.getNewObjectId());
340 newRefs.put(u.getRemoteName(), new ObjectIdRef.Unpeeled(
341 Storage.LOOSE, u.getRemoteName(), u.getNewObjectId()));
342 u.setStatus(Status.OK);
343 } catch (IOException e) {
344 u.setStatus(Status.REJECTED_OTHER_REASON);
345 u.setMessage(e.getMessage());
346 }
347 }
348
349 private boolean isNewRepository() {
350 return getRefsMap().isEmpty() && packNames != null
351 && packNames.isEmpty();
352 }
353
354 private void createNewRepository(final List<RemoteRefUpdate> updates)
355 throws TransportException {
356 try {
357 final String ref = "ref: " + pickHEAD(updates) + "\n";
358 final byte[] bytes = Constants.encode(ref);
359 dest.writeFile(ROOT_DIR + Constants.HEAD, bytes);
360 } catch (IOException e) {
361 throw new TransportException(uri, JGitText.get().cannotCreateHEAD, e);
362 }
363
364 try {
365 final String config = "[core]\n"
366 + "\trepositoryformatversion = 0\n";
367 final byte[] bytes = Constants.encode(config);
368 dest.writeFile(ROOT_DIR + Constants.CONFIG, bytes);
369 } catch (IOException e) {
370 throw new TransportException(uri, JGitText.get().cannotCreateConfig, e);
371 }
372 }
373
374 private static String pickHEAD(final List<RemoteRefUpdate> updates) {
375
376
377
378
379 for (final RemoteRefUpdate u : updates) {
380 final String n = u.getRemoteName();
381 if (n.equals(Constants.R_HEADS + Constants.MASTER))
382 return n;
383 }
384
385
386
387
388 for (final RemoteRefUpdate u : updates) {
389 final String n = u.getRemoteName();
390 if (n.startsWith(Constants.R_HEADS))
391 return n;
392 }
393 return updates.get(0).getRemoteName();
394 }
395 }