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