1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.transport;
12
13 import static java.nio.charset.StandardCharsets.UTF_8;
14
15 import java.io.IOException;
16 import java.io.OutputStream;
17 import java.io.OutputStreamWriter;
18 import java.io.Writer;
19 import java.text.MessageFormat;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.HashSet;
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.internal.JGitText;
29 import org.eclipse.jgit.internal.storage.pack.CachedPack;
30 import org.eclipse.jgit.internal.storage.pack.PackWriter;
31 import org.eclipse.jgit.lib.AnyObjectId;
32 import org.eclipse.jgit.lib.Constants;
33 import org.eclipse.jgit.lib.ObjectId;
34 import org.eclipse.jgit.lib.ObjectReader;
35 import org.eclipse.jgit.lib.ProgressMonitor;
36 import org.eclipse.jgit.lib.Ref;
37 import org.eclipse.jgit.lib.Repository;
38 import org.eclipse.jgit.revwalk.RevCommit;
39 import org.eclipse.jgit.storage.pack.PackConfig;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class BundleWriter {
59 private final Repository db;
60
61 private final ObjectReader reader;
62
63 private final Map<String, ObjectId> include;
64
65 private final Set<RevCommit> assume;
66
67 private final Set<ObjectId> tagTargets;
68
69 private final List<CachedPack> cachedPacks = new ArrayList<>();
70
71 private PackConfig packConfig;
72
73 private ObjectCountCallback callback;
74
75
76
77
78
79
80
81 public BundleWriter(Repository repo) {
82 db = repo;
83 reader = null;
84 include = new TreeMap<>();
85 assume = new HashSet<>();
86 tagTargets = new HashSet<>();
87 }
88
89
90
91
92
93
94
95
96
97
98 public BundleWriter(ObjectReader or) {
99 db = null;
100 reader = or;
101 include = new TreeMap<>();
102 assume = new HashSet<>();
103 tagTargets = new HashSet<>();
104 }
105
106
107
108
109
110
111
112
113
114 public void setPackConfig(PackConfig pc) {
115 this.packConfig = pc;
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129 public void include(String name, AnyObjectId id) {
130 boolean validRefName = Repository.isValidRefName(name) || Constants.HEAD.equals(name);
131 if (!validRefName)
132 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidRefName, name));
133 if (include.containsKey(name))
134 throw new IllegalStateException(JGitText.get().duplicateRef + name);
135 include.put(name, id.toObjectId());
136 }
137
138
139
140
141
142
143
144
145
146
147 public void include(Ref r) {
148 include(r.getName(), r.getObjectId());
149
150 if (r.getPeeledObjectId() != null)
151 tagTargets.add(r.getPeeledObjectId());
152
153 else if (r.getObjectId() != null
154 && r.getName().startsWith(Constants.R_HEADS))
155 tagTargets.add(r.getObjectId());
156 }
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174 public void addObjectsAsIs(Collection<? extends CachedPack> c) {
175 cachedPacks.addAll(c);
176 }
177
178
179
180
181
182
183
184
185
186
187
188
189
190 public void assume(RevCommit c) {
191 if (c != null)
192 assume.add(c);
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211 public void writeBundle(ProgressMonitor monitor, OutputStream os)
212 throws IOException {
213 try (PackWriter packWriter = newPackWriter()) {
214 packWriter.setObjectCountCallback(callback);
215
216 packWriter.setIndexDisabled(true);
217 packWriter.setDeltaBaseAsOffset(true);
218 packWriter.setReuseValidatingObjects(false);
219 if (cachedPacks.isEmpty()) {
220 HashSet<ObjectId> inc = new HashSet<>();
221 HashSet<ObjectId> exc = new HashSet<>();
222 inc.addAll(include.values());
223 for (RevCommit r : assume) {
224 exc.add(r.getId());
225 }
226 if (exc.isEmpty()) {
227 packWriter.setTagTargets(tagTargets);
228 }
229 packWriter.setThin(!exc.isEmpty());
230 packWriter.preparePack(monitor, inc, exc);
231 } else {
232 packWriter.preparePack(cachedPacks);
233 }
234
235 final Writer w = new OutputStreamWriter(os, UTF_8);
236 w.write(TransportBundle.V2_BUNDLE_SIGNATURE);
237 w.write('\n');
238
239 final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
240 for (RevCommit a : assume) {
241 w.write('-');
242 a.copyTo(tmp, w);
243 if (a.getRawBuffer() != null) {
244 w.write(' ');
245 w.write(a.getShortMessage());
246 }
247 w.write('\n');
248 }
249 for (Map.Entry<String, ObjectId> e : include.entrySet()) {
250 e.getValue().copyTo(tmp, w);
251 w.write(' ');
252 w.write(e.getKey());
253 w.write('\n');
254 }
255
256 w.write('\n');
257 w.flush();
258 packWriter.writePack(monitor, monitor, os);
259 }
260 }
261
262 private PackWriter newPackWriter() {
263 PackConfig pc = packConfig;
264 if (pc == null) {
265 pc = db != null ? new PackConfig(db) : new PackConfig();
266 }
267 return new PackWriter(pc, reader != null ? reader : db.newObjectReader());
268 }
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284 public BundleWriter setObjectCountCallback(ObjectCountCallback callback) {
285 this.callback = callback;
286 return this;
287 }
288 }