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>tags/v3.2.0.201312181205-r
@@ -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")); // | |||
} | |||
} |
@@ -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. |
@@ -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(); |