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.junit.Assert.assertEquals;
47 import static org.junit.Assert.assertNull;
48 import static org.junit.Assert.assertSame;
49 import static org.junit.Assert.fail;
50
51 import java.io.IOException;
52 import java.net.URISyntaxException;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.List;
56
57 import org.eclipse.jgit.api.Git;
58 import org.eclipse.jgit.api.PushCommand;
59 import org.eclipse.jgit.api.errors.GitAPIException;
60 import org.eclipse.jgit.api.errors.NoFilepatternException;
61 import org.eclipse.jgit.api.errors.TransportException;
62 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
63 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
64 import org.eclipse.jgit.junit.RepositoryTestCase;
65 import org.eclipse.jgit.lib.Constants;
66 import org.eclipse.jgit.lib.NullProgressMonitor;
67 import org.eclipse.jgit.lib.ObjectId;
68 import org.eclipse.jgit.lib.ObjectInserter;
69 import org.eclipse.jgit.lib.Repository;
70 import org.eclipse.jgit.lib.StoredConfig;
71 import org.eclipse.jgit.revwalk.RevCommit;
72 import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
73 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
74 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
75 import org.junit.After;
76 import org.junit.Before;
77 import org.junit.Test;
78
79 public class PushOptionsTest extends RepositoryTestCase {
80 private URIish uri;
81 private TestProtocol<Object> testProtocol;
82 private Object ctx = new Object();
83 private InMemoryRepository server;
84 private InMemoryRepository client;
85 private ObjectId obj1;
86 private ObjectId obj2;
87 private ReceivePack receivePack;
88
89 @Override
90 @Before
91 public void setUp() throws Exception {
92 super.setUp();
93
94 server = newRepo("server");
95 client = newRepo("client");
96
97 testProtocol = new TestProtocol<>(null,
98 new ReceivePackFactory<Object>() {
99 @Override
100 public ReceivePack create(Object req, Repository git)
101 throws ServiceNotEnabledException,
102 ServiceNotAuthorizedException {
103 receivePack = new ReceivePack(git);
104 receivePack.setAllowPushOptions(true);
105 receivePack.setAtomic(true);
106 return receivePack;
107 }
108 });
109
110 uri = testProtocol.register(ctx, server);
111
112 try (ObjectInserter ins = client.newObjectInserter()) {
113 obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
114 obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
115 ins.flush();
116 }
117 }
118
119 @Override
120 @After
121 public void tearDown() {
122 Transport.unregister(testProtocol);
123 }
124
125 private static InMemoryRepository newRepo(String name) {
126 return new InMemoryRepository(new DfsRepositoryDescription(name));
127 }
128
129 private List<RemoteRefUpdate> commands(boolean atomicSafe)
130 throws IOException {
131 List<RemoteRefUpdate> cmds = new ArrayList<>();
132 cmds.add(new RemoteRefUpdate(null, null, obj1, "refs/heads/one",
133 true , null ,
134 ObjectId.zeroId()));
135 cmds.add(new RemoteRefUpdate(null, null, obj2, "refs/heads/two",
136 true , null ,
137 atomicSafe ? ObjectId.zeroId() : obj1));
138 return cmds;
139 }
140
141 private void connectLocalToRemote(Git local, Git remote)
142 throws URISyntaxException, IOException {
143 StoredConfig config = local.getRepository().getConfig();
144 RemoteConfig remoteConfig = new RemoteConfig(config, "test");
145 remoteConfig.addURI(new URIish(
146 remote.getRepository().getDirectory().toURI().toURL()));
147 remoteConfig.addFetchRefSpec(
148 new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
149 remoteConfig.update(config);
150 config.save();
151 }
152
153 private RevCommit addCommit(Git git)
154 throws IOException, NoFilepatternException, GitAPIException {
155 writeTrashFile("f", "content of f");
156 git.add().addFilepattern("f").call();
157 return git.commit().setMessage("adding f").call();
158 }
159
160 @Test
161 public void testNonAtomicPushWithOptions() throws Exception {
162 PushResult r;
163 server.setPerformsAtomicTransactions(false);
164 List<String> pushOptions = Arrays.asList("Hello", "World!");
165
166 try (Transport tn = testProtocol.open(uri, client, "server")) {
167 tn.setPushAtomic(false);
168 tn.setPushOptions(pushOptions);
169
170 r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
171 }
172
173 RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
174 RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
175
176 assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
177 assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
178 two.getStatus());
179 assertEquals(pushOptions, receivePack.getPushOptions());
180 }
181
182 @Test
183 public void testAtomicPushWithOptions() throws Exception {
184 PushResult r;
185 server.setPerformsAtomicTransactions(true);
186 List<String> pushOptions = Arrays.asList("Hello", "World!");
187
188 try (Transport tn = testProtocol.open(uri, client, "server")) {
189 tn.setPushAtomic(true);
190 tn.setPushOptions(pushOptions);
191
192 r = tn.push(NullProgressMonitor.INSTANCE, commands(true));
193 }
194
195 RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
196 RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
197
198 assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
199 assertSame(RemoteRefUpdate.Status.OK, two.getStatus());
200 assertEquals(pushOptions, receivePack.getPushOptions());
201 }
202
203 @Test
204 public void testFailedAtomicPushWithOptions() throws Exception {
205 PushResult r;
206 server.setPerformsAtomicTransactions(true);
207 List<String> pushOptions = Arrays.asList("Hello", "World!");
208
209 try (Transport tn = testProtocol.open(uri, client, "server")) {
210 tn.setPushAtomic(true);
211 tn.setPushOptions(pushOptions);
212
213 r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
214 }
215
216 RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
217 RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
218
219 assertSame(RemoteRefUpdate.Status.REJECTED_OTHER_REASON,
220 one.getStatus());
221 assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
222 two.getStatus());
223 assertNull(receivePack.getPushOptions());
224 }
225
226 @Test
227 public void testThinPushWithOptions() throws Exception {
228 PushResult r;
229 List<String> pushOptions = Arrays.asList("Hello", "World!");
230
231 try (Transport tn = testProtocol.open(uri, client, "server")) {
232 tn.setPushThin(true);
233 tn.setPushOptions(pushOptions);
234
235 r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
236 }
237
238 RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
239 RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
240
241 assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
242 assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
243 two.getStatus());
244 assertEquals(pushOptions, receivePack.getPushOptions());
245 }
246
247 @Test
248 public void testPushWithoutOptions() throws Exception {
249 try (Git local = new Git(db);
250 Git remote = new Git(createBareRepository())) {
251 connectLocalToRemote(local, remote);
252
253 final StoredConfig config2 = remote.getRepository().getConfig();
254 config2.setBoolean("receive", null, "pushoptions", true);
255 config2.save();
256
257 RevCommit commit = addCommit(local);
258
259 local.checkout().setName("not-pushed").setCreateBranch(true).call();
260 local.checkout().setName("branchtopush").setCreateBranch(true).call();
261
262 assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
263 assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
264 assertNull(remote.getRepository().resolve("refs/heads/master"));
265
266 PushCommand pushCommand = local.push().setRemote("test");
267 pushCommand.call();
268
269 assertEquals(commit.getId(),
270 remote.getRepository().resolve("refs/heads/branchtopush"));
271 assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
272 assertNull(remote.getRepository().resolve("refs/heads/master"));
273 }
274 }
275
276 @Test
277 public void testPushWithEmptyOptions() throws Exception {
278 try (Git local = new Git(db);
279 Git remote = new Git(createBareRepository())) {
280 connectLocalToRemote(local, remote);
281
282 final StoredConfig config2 = remote.getRepository().getConfig();
283 config2.setBoolean("receive", null, "pushoptions", true);
284 config2.save();
285
286 RevCommit commit = addCommit(local);
287
288 local.checkout().setName("not-pushed").setCreateBranch(true).call();
289 local.checkout().setName("branchtopush").setCreateBranch(true).call();
290 assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
291 assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
292 assertNull(remote.getRepository().resolve("refs/heads/master"));
293
294 List<String> pushOptions = new ArrayList<>();
295 PushCommand pushCommand = local.push().setRemote("test")
296 .setPushOptions(pushOptions);
297 pushCommand.call();
298
299 assertEquals(commit.getId(),
300 remote.getRepository().resolve("refs/heads/branchtopush"));
301 assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
302 assertNull(remote.getRepository().resolve("refs/heads/master"));
303 }
304 }
305
306 @Test
307 public void testAdvertisedButUnusedPushOptions() throws Exception {
308 try (Git local = new Git(db);
309 Git remote = new Git(createBareRepository())) {
310 connectLocalToRemote(local, remote);
311
312 final StoredConfig config2 = remote.getRepository().getConfig();
313 config2.setBoolean("receive", null, "pushoptions", true);
314 config2.save();
315
316 RevCommit commit = addCommit(local);
317
318 local.checkout().setName("not-pushed").setCreateBranch(true).call();
319 local.checkout().setName("branchtopush").setCreateBranch(true).call();
320
321 assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
322 assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
323 assertNull(remote.getRepository().resolve("refs/heads/master"));
324
325 PushCommand pushCommand = local.push().setRemote("test")
326 .setPushOptions(null);
327 pushCommand.call();
328
329 assertEquals(commit.getId(),
330 remote.getRepository().resolve("refs/heads/branchtopush"));
331 assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
332 assertNull(remote.getRepository().resolve("refs/heads/master"));
333 }
334 }
335
336 @Test(expected = TransportException.class)
337 public void testPushOptionsNotSupported() throws Exception {
338 try (Git local = new Git(db);
339 Git remote = new Git(createBareRepository())) {
340 connectLocalToRemote(local, remote);
341
342 final StoredConfig config2 = remote.getRepository().getConfig();
343 config2.setBoolean("receive", null, "pushoptions", false);
344 config2.save();
345
346 addCommit(local);
347
348 local.checkout().setName("not-pushed").setCreateBranch(true).call();
349 local.checkout().setName("branchtopush").setCreateBranch(true).call();
350
351 assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
352 assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
353 assertNull(remote.getRepository().resolve("refs/heads/master"));
354
355 List<String> pushOptions = new ArrayList<>();
356 PushCommand pushCommand = local.push().setRemote("test")
357 .setPushOptions(pushOptions);
358 pushCommand.call();
359
360 fail("should already have thrown TransportException");
361 }
362 }
363 }