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