/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.cli.svn; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; import org.tmatesoft.svn.cli.SVNCommandUtil; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.internal.util.SVNDate; import org.tmatesoft.svn.core.internal.util.SVNXMLUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc.SVNPath; import org.tmatesoft.svn.core.internal.wc17.SVNWCContext.ConflictInfo; import org.tmatesoft.svn.core.wc.ISVNStatusHandler; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNStatus; import org.tmatesoft.svn.core.wc.SVNStatusClient; import org.tmatesoft.svn.core.wc.SVNStatusType; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNStatusCommand extends SVNXMLCommand implements ISVNStatusHandler { private SVNStatusPrinter myStatusPrinter; private Map myStatusCache; private int textConflicts; private int propConflicts; private int treeConflicts; public SVNStatusCommand() { super("status", new String[] {"stat", "st"}); } protected Collection createSupportedOptions() { Collection options = new ArrayList(); options.add(SVNOption.UPDATE); options.add(SVNOption.VERBOSE); options.add(SVNOption.NON_RECURSIVE); options.add(SVNOption.DEPTH); options.add(SVNOption.QUIET); options.add(SVNOption.NO_IGNORE); options.add(SVNOption.INCREMENTAL); options.add(SVNOption.XML); options.add(SVNOption.IGNORE_EXTERNALS); options.add(SVNOption.CHANGELIST); return options; } public void run() throws SVNException { Collection targets = new ArrayList(); targets = getSVNEnvironment().combineTargets(targets, true); if (targets.isEmpty()) { targets.add(""); } myStatusPrinter = new SVNStatusPrinter(getSVNEnvironment()); SVNStatusClient client = getSVNEnvironment().getClientManager().getStatusClient(); if (!getSVNEnvironment().isXML()) { client.setEventHandler(new SVNNotifyPrinter(getSVNEnvironment())); } if (getSVNEnvironment().isXML()) { if (!getSVNEnvironment().isIncremental()) { printXMLHeader("status"); } } else if (getSVNEnvironment().isIncremental()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "'incremental' option only valid in XML mode"); SVNErrorManager.error(err, SVNLogType.CLIENT); } Collection changeLists = getSVNEnvironment().getChangelistsCollection(); for (Iterator ts = targets.iterator(); ts.hasNext();) { String target = (String) ts.next(); SVNPath commandTarget = new SVNPath(target); if (getSVNEnvironment().isXML()) { StringBuffer xmlBuffer = openXMLTag("target", SVNXMLUtil.XML_STYLE_NORMAL, "path", SVNCommandUtil.getLocalPath(target), null); getSVNEnvironment().getOut().print(xmlBuffer); } try { long rev = client.doStatus(commandTarget.getFile(), SVNRevision.HEAD, getSVNEnvironment().getDepth(), getSVNEnvironment().isUpdate(), getSVNEnvironment().isVerbose(), getSVNEnvironment().isNoIgnore(), false, this, changeLists); if (getSVNEnvironment().isXML()) { StringBuffer xmlBuffer = new StringBuffer(); if (rev >= 0) { xmlBuffer = openXMLTag("against", SVNXMLUtil.XML_STYLE_SELF_CLOSING, "revision", Long.toString(rev), xmlBuffer); } xmlBuffer = closeXMLTag("target", xmlBuffer); getSVNEnvironment().getOut().print(xmlBuffer); } } catch (SVNException e) { getSVNEnvironment().handleWarning(e.getErrorMessage(), new SVNErrorCode[] {SVNErrorCode.WC_NOT_DIRECTORY}, getSVNEnvironment().isQuiet()); } } if (myStatusCache != null) { for (Iterator changelists = myStatusCache.keySet().iterator(); changelists.hasNext();) { String changelist = (String) changelists.next(); Map statuses = (Map) myStatusCache.get(changelist); getSVNEnvironment().getOut().println("\n--- Changelist '" + changelist + "':"); for (Iterator paths = statuses.keySet().iterator(); paths.hasNext();) { String path = (String) paths.next(); SVNStatus status = (SVNStatus) statuses.get(path); myStatusPrinter.printStatus(path, status, getSVNEnvironment().isVerbose() || getSVNEnvironment().isUpdate(), getSVNEnvironment().isVerbose(), getSVNEnvironment().isQuiet(), getSVNEnvironment().isUpdate()); } } } if (getSVNEnvironment().isXML() && !getSVNEnvironment().isIncremental()) { printXMLFooter("status"); } if (! getSVNEnvironment().isQuiet() && ! getSVNEnvironment().isXML()) { printConflictStats(); } } public void handleStatus(SVNStatus status) throws SVNException { countConflicts(status); String path = getSVNEnvironment().getRelativePath(status.getFile()); path = SVNCommandUtil.getLocalPath(path); if (status != null && status.getChangelistName() != null) { if (myStatusCache == null) { myStatusCache = new TreeMap(); } if (!myStatusCache.containsKey(status.getChangelistName())) { myStatusCache.put(status.getChangelistName(), new LinkedHashMap()); } ((Map) myStatusCache.get(status.getChangelistName())).put(path, status); return; } if (getSVNEnvironment().isXML()) { if (SVNStatusPrinter.combineStatus(status) == SVNStatusType.STATUS_NONE && status.getRemoteContentsStatus() == SVNStatusType.STATUS_NONE) { return; } StringBuffer xmlBuffer = printXMLStatus(status, path); getSVNEnvironment().getOut().print(xmlBuffer); } else { myStatusPrinter.printStatus(path, status, getSVNEnvironment().isVerbose() || getSVNEnvironment().isUpdate(), getSVNEnvironment().isVerbose(), getSVNEnvironment().isQuiet(), getSVNEnvironment().isUpdate()); } } private void countConflicts(SVNStatus status) throws SVNException { if (status.isConflicted()) { if (status.getPropRejectFile() != null) { propConflicts++; } if (status.getConflictWrkFile() != null || status.getConflictOldFile() != null || status.getConflictNewFile() != null) { textConflicts++; } if (status.getTreeConflict() != null) { treeConflicts++; } } } protected StringBuffer printXMLStatus(SVNStatus status, String path) { StringBuffer xmlBuffer = openXMLTag("entry", SVNXMLUtil.XML_STYLE_NORMAL, "path", path, null); Map xmlMap = new LinkedHashMap(); xmlMap.put("props", status.getNodeStatus() != SVNStatusType.STATUS_DELETED ? status.getPropertiesStatus().toString() : SVNStatusType.STATUS_NONE.toString()); xmlMap.put("item", status.getCombinedNodeAndContentsStatus().toString()); if (status.isLocked()) { xmlMap.put("wc-locked", "true"); } if (status.isCopied()) { xmlMap.put("copied", "true"); } if (status.isSwitched()) { xmlMap.put("switched", "true"); } if (status.isFileExternal()) { xmlMap.put("file-external", "true"); } if (status.isVersioned() && !status.isCopied()) { xmlMap.put("revision", status.getRevision().toString()); } if (status.getTreeConflict() != null) { xmlMap.put("tree-conflicted", "true"); } xmlBuffer = openXMLTag("wc-status", SVNXMLUtil.XML_STYLE_NORMAL, xmlMap, xmlBuffer); if (status.isVersioned() && status.getCommittedRevision().isValid()) { xmlBuffer = openXMLTag("commit", SVNXMLUtil.XML_STYLE_NORMAL, "revision", status.getCommittedRevision().toString(), xmlBuffer); xmlBuffer = openCDataTag("author", status.getAuthor(), xmlBuffer); if (status.getCommittedDate() != null) { xmlBuffer = openCDataTag("date", SVNDate.formatDate(status.getCommittedDate()), xmlBuffer); } xmlBuffer = closeXMLTag("commit", xmlBuffer); } if (status.isVersioned() && status.getLocalLock() != null) { xmlBuffer = openXMLTag("lock", SVNXMLUtil.XML_STYLE_NORMAL, null, xmlBuffer); xmlBuffer = openCDataTag("token", status.getLocalLock().getID(), xmlBuffer); xmlBuffer = openCDataTag("owner", status.getLocalLock().getOwner(), xmlBuffer); xmlBuffer = openCDataTag("comment", status.getLocalLock().getComment(), xmlBuffer); xmlBuffer = openCDataTag("created", SVNDate.formatDate(status.getLocalLock().getCreationDate()), xmlBuffer); xmlBuffer = closeXMLTag("lock", xmlBuffer); } xmlBuffer = closeXMLTag("wc-status", xmlBuffer); if (status.getRemoteNodeStatus() != SVNStatusType.STATUS_NONE || status.getRemotePropertiesStatus() != SVNStatusType.STATUS_NONE || status.getRemoteLock() != null) { xmlMap.put("props", status.getRemotePropertiesStatus().toString()); xmlMap.put("item", status.getCombinedRemoteNodeAndContentsStatus().toString()); xmlBuffer = openXMLTag("repos-status", SVNXMLUtil.XML_STYLE_NORMAL, xmlMap, xmlBuffer); if (status.getRemoteLock() != null) { xmlBuffer = openXMLTag("lock", SVNXMLUtil.XML_STYLE_NORMAL, null, xmlBuffer); xmlBuffer = openCDataTag("token", status.getRemoteLock().getID(), xmlBuffer); xmlBuffer = openCDataTag("owner", status.getRemoteLock().getOwner(), xmlBuffer); xmlBuffer = openCDataTag("comment", status.getRemoteLock().getComment(), xmlBuffer); xmlBuffer = openCDataTag("created", SVNDate.formatDate(status.getRemoteLock().getCreationDate()), xmlBuffer); if (status.getRemoteLock().getExpirationDate() != null) { xmlBuffer = openCDataTag("expires", SVNDate.formatDate(status.getRemoteLock().getExpirationDate()), xmlBuffer); } xmlBuffer = closeXMLTag("lock", xmlBuffer); } xmlBuffer = closeXMLTag("repos-status", xmlBuffer); } xmlBuffer = closeXMLTag("entry", xmlBuffer); return xmlBuffer; } private void printConflictStats() { if (textConflicts > 0 || propConflicts > 0 || treeConflicts > 0) getEnvironment().getOut().println("Summary of conflicts:"); if (textConflicts > 0) getEnvironment().getOut().println( " Text conflicts: " + textConflicts); if (propConflicts > 0) getEnvironment().getOut().println( " Property conflicts: " + propConflicts); if (treeConflicts > 0) getEnvironment().getOut().println( " Tree conflicts: " + treeConflicts); } }