View Javadoc
1   /*
2    * Copyright (C) 2012, 2015 François Rey <eclipse.org_@_francois_._rey_._name> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.pgm;
11  
12  import static org.eclipse.jgit.lib.Constants.MASTER;
13  import static org.eclipse.jgit.lib.Constants.R_HEADS;
14  import static org.junit.Assert.assertTrue;
15  
16  import java.io.IOException;
17  
18  import org.eclipse.jgit.api.Git;
19  import org.eclipse.jgit.api.errors.GitAPIException;
20  import org.eclipse.jgit.lib.CLIRepositoryTestCase;
21  import org.eclipse.jgit.revwalk.RevCommit;
22  import org.junit.Test;
23  
24  public class StatusTest extends CLIRepositoryTestCase {
25  
26  	@Test
27  	public void testPathOptionHelp() throws Exception {
28  		String[] result = execute("git status -h");
29  		assertTrue("Unexpected argument: " + result[1],
30  				result[1].endsWith("[-- path ...]"));
31  	}
32  
33  	@Test
34  	public void testStatusDefault() throws Exception {
35  		executeTest("git status", false, true);
36  	}
37  
38  	@Test
39  	public void testStatusU() throws Exception {
40  		executeTest("git status -u", false, true);
41  	}
42  
43  	@Test
44  	public void testStatusUno() throws Exception {
45  		executeTest("git status -uno", false, false);
46  	}
47  
48  	@Test
49  	public void testStatusUall() throws Exception {
50  		executeTest("git status -uall", false, true);
51  	}
52  
53  	@Test
54  	public void testStatusUntrackedFiles() throws Exception {
55  		executeTest("git status --untracked-files", false, true);
56  	}
57  
58  	@Test
59  	public void testStatusUntrackedFilesNo() throws Exception {
60  		executeTest("git status --untracked-files=no", false, false);
61  	}
62  
63  	@Test
64  	public void testStatusUntrackedFilesAll() throws Exception {
65  		executeTest("git status --untracked-files=all", false, true);
66  	}
67  
68  	@Test
69  	public void testStatusPorcelain() throws Exception {
70  		executeTest("git status --porcelain", true, true);
71  	}
72  
73  	@Test
74  	public void testStatusPorcelainU() throws Exception {
75  		executeTest("git status --porcelain -u", true, true);
76  	}
77  
78  	@Test
79  	public void testStatusPorcelainUno() throws Exception {
80  		executeTest("git status --porcelain -uno", true, false);
81  	}
82  
83  	@Test
84  	public void testStatusPorcelainUall() throws Exception {
85  		executeTest("git status --porcelain -uall", true, true);
86  	}
87  
88  	@Test
89  	public void testStatusPorcelainUntrackedFiles() throws Exception {
90  		executeTest("git status --porcelain --untracked-files", true, true);
91  	}
92  
93  	@Test
94  	public void testStatusPorcelainUntrackedFilesNo() throws Exception {
95  		executeTest("git status --porcelain --untracked-files=no", true, false);
96  	}
97  
98  	@Test
99  	public void testStatusPorcelainUntrackedFilesAll() throws Exception {
100 		executeTest("git status --porcelain --untracked-files=all", true, true);
101 	}
102 
103 	/**
104 	 * Executes the test sequence.
105 	 *
106 	 * @param command
107 	 *            full git command and parameters to be used
108 	 * @param porcelain
109 	 *            indicates that porcelain format is expected in the output
110 	 * @param untrackedFiles
111 	 *            indicates that untracked files are expected in the output
112 	 *
113 	 * @throws Exception
114 	 *             if error during test execution
115 	 */
116 	private void executeTest(String command, boolean porcelain,
117 			boolean untrackedFiles) throws Exception {
118 		Git git = new Git(db);
119 		// Write all files
120 		writeAllFiles();
121 		// Test untracked
122 		assertUntrackedFiles(command, porcelain, untrackedFiles);
123 		// Add to index
124 		addFilesToIndex(git);
125 		// Test staged count
126 		assertStagedFiles(command, porcelain, untrackedFiles);
127 		// Commit
128 		makeInitialCommit(git);
129 		assertAfterInitialCommit(command, porcelain, untrackedFiles);
130 		// Make some changes and stage them
131 		makeSomeChangesAndStageThem(git);
132 		// Test staged/not-staged status
133 		assertStagedStatus(command, porcelain, untrackedFiles);
134 		// Create unmerged file
135 		createUnmergedFile(git);
136 		// Commit pending changes
137 		commitPendingChanges(git);
138 		assertUntracked(command, porcelain, untrackedFiles, "master");
139 		// Checkout new branch
140 		checkoutTestBranch(git);
141 		// Test branch status
142 		assertUntracked(command, porcelain, untrackedFiles, "test");
143 		// Commit change and checkout master again
144 		RevCommit testBranch = commitChangesInTestBranch(git);
145 		assertUntracked(command, porcelain, untrackedFiles, "test");
146 		checkoutMasterBranch(git);
147 		// Change the same file and commit
148 		changeUnmergedFileAndCommit(git);
149 		assertUntracked(command, porcelain, untrackedFiles, "master");
150 		// Merge test branch into master
151 		mergeTestBranchInMaster(git, testBranch);
152 		// Test unmerged status
153 		assertUntrackedAndUnmerged(command, porcelain, untrackedFiles, "master");
154 		// Test detached head
155 		detachHead(git);
156 		assertUntrackedAndUnmerged(command, porcelain, untrackedFiles, null);
157 	}
158 
159 	private void writeAllFiles() throws IOException {
160 		writeTrashFile("tracked", "tracked");
161 		writeTrashFile("stagedNew", "stagedNew");
162 		writeTrashFile("stagedModified", "stagedModified");
163 		writeTrashFile("stagedDeleted", "stagedDeleted");
164 		writeTrashFile("trackedModified", "trackedModified");
165 		writeTrashFile("trackedDeleted", "trackedDeleted");
166 		writeTrashFile("untracked", "untracked");
167 	}
168 
169 	private void addFilesToIndex(Git git) throws GitAPIException {
170 		git.add().addFilepattern("tracked").call();
171 		git.add().addFilepattern("stagedModified").call();
172 		git.add().addFilepattern("stagedDeleted").call();
173 		git.add().addFilepattern("trackedModified").call();
174 		git.add().addFilepattern("trackedDeleted").call();
175 	}
176 
177 	private void makeInitialCommit(Git git) throws GitAPIException {
178 		git.commit().setMessage("initial commit").call();
179 	}
180 
181 	private void makeSomeChangesAndStageThem(Git git) throws IOException,
182 			GitAPIException {
183 		writeTrashFile("stagedModified", "stagedModified modified");
184 		deleteTrashFile("stagedDeleted");
185 		writeTrashFile("trackedModified", "trackedModified modified");
186 		deleteTrashFile("trackedDeleted");
187 		git.add().addFilepattern("stagedModified").call();
188 		git.rm().addFilepattern("stagedDeleted").call();
189 		git.add().addFilepattern("stagedNew").call();
190 	}
191 
192 	private void createUnmergedFile(Git git) throws IOException,
193 			GitAPIException {
194 		writeTrashFile("unmerged", "unmerged");
195 		git.add().addFilepattern("unmerged").call();
196 	}
197 
198 	private void commitPendingChanges(Git git) throws GitAPIException {
199 		git.add().addFilepattern("trackedModified").call();
200 		git.rm().addFilepattern("trackedDeleted").call();
201 		git.commit().setMessage("commit before branching").call();
202 	}
203 
204 	private void checkoutTestBranch(Git git) throws GitAPIException {
205 		git.checkout().setCreateBranch(true).setName("test").call();
206 	}
207 
208 	private RevCommit commitChangesInTestBranch(Git git) throws IOException,
209 			GitAPIException {
210 		writeTrashFile("unmerged", "changed in test branch");
211 		git.add().addFilepattern("unmerged").call();
212 		return git.commit()
213 				.setMessage("changed unmerged in test branch").call();
214 	}
215 
216 	private void checkoutMasterBranch(Git git) throws GitAPIException {
217 		git.checkout().setName("master").call();
218 	}
219 
220 	private void changeUnmergedFileAndCommit(Git git) throws IOException,
221 			GitAPIException {
222 		writeTrashFile("unmerged", "changed in master branch");
223 		git.add().addFilepattern("unmerged").call();
224 		git.commit().setMessage("changed unmerged in master branch").call();
225 	}
226 
227 	private void mergeTestBranchInMaster(Git git, RevCommit aCommit)
228 			throws GitAPIException {
229 		git.merge().include(aCommit.getId()).call();
230 	}
231 
232 	private void detachHead(Git git) throws IOException, GitAPIException {
233 		String commitId = db.exactRef(R_HEADS + MASTER).getObjectId().name();
234 		git.checkout().setName(commitId).call();
235 	}
236 
237 	private void assertUntrackedFiles(String command, boolean porcelain,
238 			boolean untrackedFiles) throws Exception {
239 		String[] output = new String[0];
240 
241 		if (porcelain) {
242 			if (untrackedFiles) {
243 				output = new String[] { //
244 						"?? stagedDeleted", //
245 						"?? stagedModified", //
246 						"?? stagedNew", //
247 						"?? tracked", //
248 						"?? trackedDeleted", //
249 						"?? trackedModified", //
250 						"?? untracked", //
251 						"" //
252 				};
253 			} else {
254 				output = new String[] { //
255 						"" //
256 				};
257 			}
258 		} else {
259 			if (untrackedFiles) {
260 				output = new String[] { //
261 						"On branch master", //
262 						"Untracked files:", //
263 						"",//
264 						"\tstagedDeleted", //
265 						"\tstagedModified", //
266 						"\tstagedNew", //
267 						"\ttracked", //
268 						"\ttrackedDeleted", //
269 						"\ttrackedModified", //
270 						"\tuntracked", //
271 						"" //
272 				};
273 			} else {
274 				output = new String[] { //
275 						"On branch master", //
276 						"" //
277 				};
278 			}
279 		}
280 
281 		assertArrayOfLinesEquals(output, execute(command));
282 	}
283 
284 	private void assertStagedFiles(String command, boolean porcelain,
285 			boolean untrackedFiles) throws Exception {
286 		String[] output = new String[0];
287 
288 		if (porcelain) {
289 			if (untrackedFiles) {
290 				output = new String[] { //
291 						"A  stagedDeleted", //
292 						"A  stagedModified", //
293 						"A  tracked", //
294 						"A  trackedDeleted", //
295 						"A  trackedModified", //
296 						"?? stagedNew", //
297 						"?? untracked", //
298 						"" //
299 				};
300 			} else {
301 				output = new String[] { //
302 						"A  stagedDeleted", //
303 						"A  stagedModified", //
304 						"A  tracked", //
305 						"A  trackedDeleted", //
306 						"A  trackedModified", //
307 						"" //
308 				};
309 			}
310 		} else {
311 			if (untrackedFiles) {
312 				output = new String[] { //
313 						"On branch master", //
314 						"Changes to be committed:", //
315 						"", //
316 						"\tnew file:   stagedDeleted", //
317 						"\tnew file:   stagedModified", //
318 						"\tnew file:   tracked", //
319 						"\tnew file:   trackedDeleted", //
320 						"\tnew file:   trackedModified", //
321 						"", //
322 						"Untracked files:", //
323 						"", //
324 						"\tstagedNew", //
325 						"\tuntracked", //
326 						"" //
327 				};
328 			} else {
329 				output = new String[] { //
330 						"On branch master", //
331 						"Changes to be committed:", //
332 						"", //
333 						"\tnew file:   stagedDeleted", //
334 						"\tnew file:   stagedModified", //
335 						"\tnew file:   tracked", //
336 						"\tnew file:   trackedDeleted", //
337 						"\tnew file:   trackedModified", //
338 						"" //
339 				};
340 			}
341 		}
342 
343 		assertArrayOfLinesEquals(output, execute(command));
344 	}
345 
346 	private void assertAfterInitialCommit(String command, boolean porcelain,
347 			boolean untrackedFiles) throws Exception {
348 		String[] output = new String[0];
349 
350 		if (porcelain) {
351 			if (untrackedFiles) {
352 				output = new String[] { //
353 						"?? stagedNew", //
354 						"?? untracked", //
355 						"" //
356 				};
357 			} else {
358 				output = new String[] { //
359 						"" //
360 				};
361 			}
362 		} else {
363 			if (untrackedFiles) {
364 				output = new String[] { //
365 						"On branch master", //
366 						"Untracked files:", //
367 						"", //
368 						"\tstagedNew", //
369 						"\tuntracked", //
370 						"" //
371 				};
372 			} else {
373 				output = new String[] { //
374 						"On branch master", //
375 						"" //
376 				};
377 			}
378 		}
379 
380 		assertArrayOfLinesEquals(output, execute(command));
381 	}
382 
383 	private void assertStagedStatus(String command, boolean porcelain,
384 			boolean untrackedFiles) throws Exception {
385 		String[] output = new String[0];
386 
387 		if (porcelain) {
388 			if (untrackedFiles) {
389 				output = new String[] { //
390 						"D  stagedDeleted", //
391 						"M  stagedModified", //
392 						"A  stagedNew", //
393 						" D trackedDeleted", //
394 						" M trackedModified", //
395 						"?? untracked", //
396 						"" //
397 				};
398 			} else {
399 				output = new String[] { //
400 						"D  stagedDeleted", //
401 						"M  stagedModified", //
402 						"A  stagedNew", //
403 						" D trackedDeleted", //
404 						" M trackedModified", //
405 						"" //
406 				};
407 			}
408 		} else {
409 			if (untrackedFiles) {
410 				output = new String[] { //
411 						"On branch master", //
412 						"Changes to be committed:", //
413 						"", //
414 						"\tdeleted:    stagedDeleted", //
415 						"\tmodified:   stagedModified", //
416 						"\tnew file:   stagedNew", //
417 						"", //
418 						"Changes not staged for commit:", //
419 						"", //
420 						"\tdeleted:    trackedDeleted", //
421 						"\tmodified:   trackedModified", //
422 						"", //
423 						"Untracked files:", //
424 						"", //
425 						"\tuntracked", //
426 						"" //
427 				};
428 			} else {
429 				output = new String[] { //
430 						"On branch master", //
431 						"Changes to be committed:", //
432 						"", //
433 						"\tdeleted:    stagedDeleted", //
434 						"\tmodified:   stagedModified", //
435 						"\tnew file:   stagedNew", //
436 						"", //
437 						"Changes not staged for commit:", //
438 						"", //
439 						"\tdeleted:    trackedDeleted", //
440 						"\tmodified:   trackedModified", //
441 						"", //
442 				};
443 			}
444 		}
445 
446 		assertArrayOfLinesEquals(output, execute(command));
447 	}
448 
449 	private void assertUntracked(String command,
450 			boolean porcelain,
451 			boolean untrackedFiles, String branch) throws Exception {
452 		String[] output = new String[0];
453 		String branchHeader = "On branch " + branch;
454 
455 		if (porcelain) {
456 			if (untrackedFiles) {
457 				output = new String[] { //
458 						"?? untracked", //
459 						"" //
460 				};
461 			} else {
462 				output = new String[] { //
463 						"" //
464 				};
465 			}
466 		} else {
467 			if (untrackedFiles) {
468 				output = new String[] { //
469 						branchHeader, //
470 						"Untracked files:", //
471 						"", //
472 						"\tuntracked", //
473 						"" //
474 				};
475 			} else {
476 				output = new String[] { //
477 						branchHeader, //
478 						"" //
479 				};
480 			}
481 		}
482 
483 		assertArrayOfLinesEquals(output, execute(command));
484 	}
485 
486 	private void assertUntrackedAndUnmerged(String command, boolean porcelain,
487 			boolean untrackedFiles, String branch) throws Exception {
488 		String[] output = new String[0];
489 		String branchHeader = (branch == null) //
490 				? "Not currently on any branch." //
491 				: "On branch " + branch;
492 
493 		if (porcelain) {
494 			if (untrackedFiles) {
495 				output = new String[] { //
496 						"UU unmerged", //
497 						"?? untracked", //
498 						"" //
499 				};
500 			} else {
501 				output = new String[] { //
502 						"UU unmerged", //
503 						"" //
504 				};
505 			}
506 		} else {
507 			if (untrackedFiles) {
508 				output = new String[] { //
509 						branchHeader, //
510 						"Unmerged paths:", //
511 						"", //
512 						"\tboth modified:      unmerged", //
513 						"", //
514 						"Untracked files:", //
515 						"", //
516 						"\tuntracked", //
517 						"" //
518 				};
519 			} else {
520 				output = new String[] { //
521 						branchHeader, //
522 						"Unmerged paths:", //
523 						"", //
524 						"\tboth modified:      unmerged", //
525 						"" //
526 				};
527 			}
528 		}
529 
530 		assertArrayOfLinesEquals(output, execute(command));
531 	}
532 }