]> source.dussan.org Git - jgit.git/commitdiff
CLI status should support --porcelain 90/17590/3
authorKaloyan Raev <kaloyan.r@zend.com>
Thu, 10 Oct 2013 20:08:14 +0000 (23:08 +0300)
committerMatthias Sohn <matthias.sohn@sap.com>
Wed, 4 Dec 2013 10:13:27 +0000 (11:13 +0100)
Add support for the machine-readable output format along with the
existing default long format.

Bug: 419968
Change-Id: I37fe5121b4c9dbae1106b1d18e9fdc134070a9dd
Signed-off-by: Kaloyan Raev <kaloyan.r@zend.com>
org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java

index 73ae598a8c2651e558900ab4f889a4334fa92639..acc2be6b43462a3e8c0e64d953c9a8995cbcf867 100644 (file)
@@ -213,4 +213,122 @@ public class StatusTest extends CLIRepositoryTestCase {
                                                "" //
                                }, execute("git status")); //
        }
+
+       @Test
+       public void testStatusPorcelain() throws Exception {
+               Git git = new Git(db);
+               // Write all files
+               writeTrashFile("tracked", "tracked");
+               writeTrashFile("stagedNew", "stagedNew");
+               writeTrashFile("stagedModified", "stagedModified");
+               writeTrashFile("stagedDeleted", "stagedDeleted");
+               writeTrashFile("trackedModified", "trackedModified");
+               writeTrashFile("trackedDeleted", "trackedDeleted");
+               writeTrashFile("untracked", "untracked");
+               // Test untracked
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "?? stagedDeleted", //
+                                               "?? stagedModified", //
+                                               "?? stagedNew", //
+                                               "?? tracked", //
+                                               "?? trackedDeleted", //
+                                               "?? trackedModified", //
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+               // Add to index
+               git.add().addFilepattern("tracked").call();
+               git.add().addFilepattern("stagedModified").call();
+               git.add().addFilepattern("stagedDeleted").call();
+               git.add().addFilepattern("trackedModified").call();
+               git.add().addFilepattern("trackedDeleted").call();
+               // Test staged count
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "A  stagedDeleted", //
+                                               "A  stagedModified", //
+                                               "A  tracked", //
+                                               "A  trackedDeleted", //
+                                               "A  trackedModified", //
+                                               "?? stagedNew", //
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+               // Commit
+               git.commit().setMessage("initial commit").call();
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "?? stagedNew", //
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+               // Make some changes and stage them
+               writeTrashFile("stagedModified", "stagedModified modified");
+               deleteTrashFile("stagedDeleted");
+               writeTrashFile("trackedModified", "trackedModified modified");
+               deleteTrashFile("trackedDeleted");
+               git.add().addFilepattern("stagedModified").call();
+               git.rm().addFilepattern("stagedDeleted").call();
+               git.add().addFilepattern("stagedNew").call();
+               // Test staged/not-staged status
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "D  stagedDeleted", //
+                                               "M  stagedModified", //
+                                               "A  stagedNew", //
+                                               " D trackedDeleted", //
+                                               " M trackedModified", //
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+               // Create unmerged file
+               writeTrashFile("unmerged", "unmerged");
+               git.add().addFilepattern("unmerged").call();
+               // Commit pending changes
+               git.add().addFilepattern("trackedModified").call();
+               git.rm().addFilepattern("trackedDeleted").call();
+               git.commit().setMessage("commit before branching").call();
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+               // Checkout new branch
+               git.checkout().setCreateBranch(true).setName("test").call();
+               // Test branch status
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+               // Commit change and checkout master again
+               writeTrashFile("unmerged", "changed in test branch");
+               git.add().addFilepattern("unmerged").call();
+               RevCommit testBranch = git.commit()
+                               .setMessage("changed unmerged in test branch").call();
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+               git.checkout().setName("master").call();
+               // Change the same file and commit
+               writeTrashFile("unmerged", "changed in master branch");
+               git.add().addFilepattern("unmerged").call();
+               git.commit().setMessage("changed unmerged in master branch").call();
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+               // Merge test branch into master
+               git.merge().include(testBranch.getId()).call();
+               // Test unmerged status
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "UU unmerged", //
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+               // Test detached head
+               String commitId = db.getRef(Constants.MASTER).getObjectId().name();
+               git.checkout().setName(commitId).call();
+               assertArrayOfLinesEquals(new String[] { // git status output
+                                               "UU unmerged", //
+                                               "?? untracked", //
+                                               "" //
+                               }, execute("git status --porcelain")); //
+       }
 }
index fc83b956b904d6cd3a121ee483a6410fdb1be92a..d23f37899344c2713674951994755718cf8dd44e 100644 (file)
@@ -282,6 +282,7 @@ usage_inputOutputFile=Input/output file
 usage_listBothRemoteTrackingAndLocalBranches=list both remote-tracking and local branches
 usage_listCreateOrDeleteBranches=List, create, or delete branches
 usage_logAllPretty=format:%H %ct %P' output=log --all '--pretty=format:%H %ct %P' output
+usage_machineReadableOutput=machine-readable output
 usage_manageReflogInformation=Manage reflog information
 usage_mergeFf=When the merge resolves as a fast-forward, only update the branch pointer, without creating a merge commit.
 usage_mergeNoFf=Create a merge commit even when the merge resolves as a fast-forward.
index 0214ed00e0e03ec827c65979f58f1e2f030de74e..2ae950bdc559f36e5d26c3cb62eb1878f36cee52 100644 (file)
@@ -50,6 +50,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeSet;
 
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.StatusCommand;
@@ -71,26 +72,134 @@ class Status extends TextBuiltin {
 
        protected final String statusFileListFormatUnmerged = CLIText.get().statusFileListFormatUnmerged;
 
+       @Option(name = "--porcelain", usage = "usage_machineReadableOutput")
+       protected boolean porcelain;
+
        @Option(name = "--", metaVar = "metaVar_path", multiValued = true)
        protected List<String> filterPaths;
 
        @Override
        protected void run() throws Exception {
+               StatusCommand statusCommand = new Git(db).status();
+               if (filterPaths != null && filterPaths.size() > 0)
+                       for (String path : filterPaths)
+                               statusCommand.addPath(path);
+               org.eclipse.jgit.api.Status status = statusCommand.call();
+               printStatus(status);
+       }
+
+       private void printStatus(org.eclipse.jgit.api.Status status)
+                       throws IOException {
+               if (porcelain)
+                       printPorcelainStatus(status);
+               else
+                       printLongStatus(status);
+       }
+
+       private void printPorcelainStatus(org.eclipse.jgit.api.Status status)
+                       throws IOException {
+
+               Collection<String> added = status.getAdded();
+               Collection<String> changed = status.getChanged();
+               Collection<String> removed = status.getRemoved();
+               Collection<String> modified = status.getModified();
+               Collection<String> missing = status.getMissing();
+               Map<String, StageState> conflicting = status.getConflictingStageState();
+
+               // build a sorted list of all paths except untracked and ignored
+               TreeSet<String> sorted = new TreeSet<String>();
+               sorted.addAll(added);
+               sorted.addAll(changed);
+               sorted.addAll(removed);
+               sorted.addAll(modified);
+               sorted.addAll(missing);
+               sorted.addAll(conflicting.keySet());
+
+               // list each path
+               for (String path : sorted) {
+                       char x = ' ';
+                       char y = ' ';
+
+                       if (added.contains(path))
+                               x = 'A';
+                       else if (changed.contains(path))
+                               x = 'M';
+                       else if (removed.contains(path))
+                               x = 'D';
+
+                       if (modified.contains(path))
+                               y = 'M';
+                       else if (missing.contains(path))
+                               y = 'D';
+
+                       if (conflicting.containsKey(path)) {
+                               StageState stageState = conflicting.get(path);
+
+                               switch (stageState) {
+                               case BOTH_DELETED:
+                                       x = 'D';
+                                       y = 'D';
+                                       break;
+                               case ADDED_BY_US:
+                                       x = 'A';
+                                       y = 'U';
+                                       break;
+                               case DELETED_BY_THEM:
+                                       x = 'U';
+                                       y = 'D';
+                                       break;
+                               case ADDED_BY_THEM:
+                                       x = 'U';
+                                       y = 'A';
+                                       break;
+                               case DELETED_BY_US:
+                                       x = 'D';
+                                       y = 'U';
+                                       break;
+                               case BOTH_ADDED:
+                                       x = 'A';
+                                       y = 'A';
+                                       break;
+                               case BOTH_MODIFIED:
+                                       x = 'U';
+                                       y = 'U';
+                                       break;
+                               default:
+                                       throw new IllegalArgumentException("Unknown StageState: " //$NON-NLS-1$
+                                                       + stageState);
+                               }
+                       }
+
+                       printPorcelainLine(x, y, path);
+               }
+
+               // untracked are always at the end of the list
+               TreeSet<String> untracked = new TreeSet<String>(status.getUntracked());
+               for (String path : untracked)
+                       printPorcelainLine('?', '?', path);
+       }
+
+       private void printPorcelainLine(char x, char y, String path)
+                       throws IOException {
+               StringBuilder lineBuilder = new StringBuilder();
+               lineBuilder.append(x).append(y).append(' ').append(path);
+               outw.println(lineBuilder.toString());
+       }
+
+       private void printLongStatus(org.eclipse.jgit.api.Status status)
+                       throws IOException {
                // Print current branch name
                final Ref head = db.getRef(Constants.HEAD);
-               boolean firstHeader = true;
                if (head != null && head.isSymbolic()) {
                        String branch = Repository.shortenRefName(head.getLeaf().getName());
-                       outw.println(CLIText.formatLine(
-                                       MessageFormat.format(CLIText.get().onBranch, branch)));
+                       outw.println(CLIText.formatLine(MessageFormat.format(
+                                       CLIText.get().onBranch, branch)));
                } else
                        outw.println(CLIText.formatLine(CLIText.get().notOnAnyBranch));
+
                // List changes
-               StatusCommand statusCommand = new Git(db).status();
-               if (filterPaths != null && filterPaths.size() > 0)
-                       for (String path : filterPaths)
-                               statusCommand.addPath(path);
-               org.eclipse.jgit.api.Status status = statusCommand.call();
+               boolean firstHeader = true;
+
                Collection<String> added = status.getAdded();
                Collection<String> changed = status.getChanged();
                Collection<String> removed = status.getRemoved();