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