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