0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.mercurial.util;
0043:
0044: import java.awt.EventQueue;
0045: import java.io.BufferedReader;
0046: import java.io.BufferedWriter;
0047: import java.io.FileWriter;
0048: import java.io.File;
0049: import java.io.IOException;
0050: import java.io.InterruptedIOException;
0051: import java.io.InputStreamReader;
0052: import java.io.PrintWriter;
0053: import java.text.ParseException;
0054: import java.text.SimpleDateFormat;
0055: import java.util.ArrayList;
0056: import java.util.Date;
0057: import java.util.HashMap;
0058: import java.util.Iterator;
0059: import java.util.LinkedList;
0060: import java.util.List;
0061: import java.util.Map;
0062: import java.util.Set;
0063: import java.util.logging.Level;
0064: import org.netbeans.api.options.OptionsDisplayer;
0065: import org.openide.util.NbBundle;
0066: import org.netbeans.modules.mercurial.FileInformation;
0067: import org.netbeans.modules.mercurial.FileStatus;
0068: import org.netbeans.modules.mercurial.HgException;
0069: import org.netbeans.modules.mercurial.Mercurial;
0070: import org.netbeans.modules.mercurial.OutputLogger;
0071: import org.netbeans.api.queries.SharabilityQuery;
0072: import org.netbeans.modules.mercurial.HgModuleConfig;
0073: import org.netbeans.modules.mercurial.config.HgConfigFiles;
0074: import org.netbeans.modules.mercurial.ui.log.HgLogMessage;
0075: import org.openide.DialogDisplayer;
0076: import org.openide.NotifyDescriptor;
0077: import org.openide.util.Utilities;
0078:
0079: /**
0080: *
0081: * @author jrice
0082: */
0083: public class HgCommand {
0084: public static final String HG_COMMAND = "hg"; // NOI18N
0085: public static final String HG_WINDOWS_EXE = ".exe"; // NOI18N
0086: public static final String HG_WINDOWS_CMD = ".cmd"; // NOI18N
0087: public static final String HGK_COMMAND = "hgk"; // NOI18N
0088:
0089: private static final String HG_STATUS_CMD = "status"; // NOI18N // need -A to see ignored files, specified in .hgignore, see man hgignore for details
0090: private static final String HG_OPT_REPOSITORY = "--repository"; // NOI18N
0091: private static final String HG_OPT_BUNDLE = "--bundle"; // NOI18N
0092: private static final String HG_OPT_CWD_CMD = "--cwd"; // NOI18N
0093: private static final String HG_OPT_USERNAME = "--user"; // NOI18N
0094:
0095: private static final String HG_OPT_FOLLOW = "--follow"; // NOI18N
0096: private static final String HG_STATUS_FLAG_ALL_CMD = "-marduicC"; // NOI18N
0097: private static final String HG_FLAG_REV_CMD = "--rev"; // NOI18N
0098: private static final String HG_STATUS_FLAG_TIP_CMD = "tip"; // NOI18N
0099: private static final String HG_STATUS_FLAG_REM_DEL_CMD = "-rd"; // NOI18N
0100: private static final String HG_STATUS_FLAG_INCLUDE_CMD = "-I"; // NOI18N
0101: private static final String HG_STATUS_FLAG_INCLUDE_GLOB_CMD = "glob:"; // NOI18N
0102: private static final String HG_STATUS_FLAG_INCLUDE_END_CMD = "*"; // NOI18N
0103: private static final String HG_STATUS_FLAG_INTERESTING_CMD = "-marduC"; // NOI18N
0104: private static final String HG_STATUS_FLAG_UNKNOWN_CMD = "-u"; // NOI18N
0105: private static final String HG_HEAD_STR = "HEAD"; // NOI18N
0106: private static final String HG_FLAG_DATE_CMD = "--date"; // NOI18N
0107:
0108: private static final String HG_COMMIT_CMD = "commit"; // NOI18N
0109: private static final String HG_COMMIT_OPT_LOGFILE_CMD = "--logfile"; // NOI18N
0110: private static final String HG_COMMIT_TEMPNAME = "hgcommit"; // NOI18N
0111: private static final String HG_COMMIT_TEMPNAME_SUFFIX = ".hgm"; // NOI18N
0112: private static final String HG_COMMIT_DEFAULT_MESSAGE = "[no commit message]"; // NOI18N
0113:
0114: private static final String HG_REVERT_CMD = "revert"; // NOI18N
0115: private static final String HG_REVERT_NOBACKUP_CMD = "--no-backup"; // NOI18N
0116: private static final String HG_ADD_CMD = "add"; // NOI18N
0117:
0118: private static final String HG_BRANCH_CMD = "branch"; // NOI18N
0119: private static final String HG_BRANCH_REV_CMD = "tip"; // NOI18N
0120: private static final String HG_BRANCH_REV_TEMPLATE_CMD = "--template={rev}\\n"; // NOI18N
0121: private static final String HG_BRANCH_SHORT_CS_TEMPLATE_CMD = "--template={node|short}\\n"; // NOI18N
0122: private static final String HG_BRANCH_INFO_TEMPLATE_CMD = "--template={branches}:{rev}:{node|short}\\n"; // NOI18N
0123: private static final String HG_GET_PREVIOUS_TEMPLATE_CMD = "--template={files}\\n"; // NOI18N
0124:
0125: private static final String HG_CREATE_CMD = "init"; // NOI18N
0126: private static final String HG_CLONE_CMD = "clone"; // NOI18N
0127:
0128: private static final String HG_UPDATE_ALL_CMD = "update"; // NOI18N
0129: private static final String HG_UPDATE_FORCE_ALL_CMD = "-C"; // NOI18N
0130:
0131: private static final String HG_REMOVE_CMD = "remove"; // NOI18N
0132: private static final String HG_REMOVE_FLAG_FORCE_CMD = "--force"; // NOI18N
0133:
0134: private static final String HG_LOG_CMD = "log"; // NOI18N
0135: private static final String HG_OUT_CMD = "out"; // NOI18N
0136: private static final String HG_LOG_LIMIT_ONE_CMD = "-l 1"; // NOI18N
0137: private static final String HG_LOG_LIMIT_CMD = "-l"; // NOI18N
0138: private static final String HG_LOG_TEMPLATE_SHORT_CMD = "--template={rev}\\n{desc|firstline}\\n{date|hgdate}\\n{node|short}\\n"; // NOI18N
0139: private static final String HG_LOG_TEMPLATE_LONG_CMD = "--template={rev}\\n{desc}\\n{date|hgdate}\\n{node|short}\\n"; // NOI18N
0140:
0141: private static final String HG_LOG_NO_MERGES_CMD = "-M";
0142: private static final String HG_LOG_DEBUG_CMD = "--debug";
0143: private static final String HG_LOG_TEMPLATE_HISTORY_CMD = "--template=rev:{rev}\\nauth:{author}\\ndesc:{desc}\\ndate:{date|hgdate}\\nid:{node|short}\\n"
0144: + // NOI18N
0145: "file_mods:{files}\\nfile_adds:{file_adds}\\nfile_dels:{file_dels}\\nfile_copies:\\nendCS:\\n"; // NOI18N
0146: private static final String HG_LOG_REVISION_OUT = "rev:"; // NOI18N
0147: private static final String HG_LOG_AUTHOR_OUT = "auth:"; // NOI18N
0148: private static final String HG_LOG_DESCRIPTION_OUT = "desc:"; // NOI18N
0149: private static final String HG_LOG_DATE_OUT = "date:"; // NOI18N
0150: private static final String HG_LOG_ID_OUT = "id:"; // NOI18N
0151: private static final String HG_LOG_FILEMODS_OUT = "file_mods:"; // NOI18N
0152: private static final String HG_LOG_FILEADDS_OUT = "file_adds:"; // NOI18N
0153: private static final String HG_LOG_FILEDELS_OUT = "file_dels:"; // NOI18N
0154: private static final String HG_LOG_FILECOPIESS_OUT = "file_copies:"; // NOI18N
0155: private static final String HG_LOG_ENDCS_OUT = "endCS:"; // NOI18N
0156:
0157: private static final String HG_LOG_PATCH_CMD = "-p";
0158: private static final String HG_LOG_TEMPLATE_EXPORT_FILE_CMD = "--template=# Mercurial Export File Diff\\n# changeset: \\t{rev}:{node|short}\\n# user:\\t\\t{author}\\n# date:\\t\\t{date|isodate}\\n# summary:\\t{desc}\\n\\n";
0159:
0160: private static final String HG_CSET_TEMPLATE_CMD = "--template={rev}:{node|short}\\n"; // NOI18N
0161: private static final String HG_REV_TEMPLATE_CMD = "--template={rev}\\n"; // NOI18N
0162: private static final String HG_CSET_TARGET_TEMPLATE_CMD = "--template={rev} ({node|short})\\n"; // NOI18N
0163:
0164: private static final String HG_CAT_CMD = "cat"; // NOI18N
0165: private static final String HG_FLAG_OUTPUT_CMD = "--output"; // NOI18N
0166:
0167: private static final String HG_ANNOTATE_CMD = "annotate"; // NOI18N
0168: private static final String HG_ANNOTATE_FLAGN_CMD = "--number"; // NOI18N
0169: private static final String HG_ANNOTATE_FLAGU_CMD = "--user"; // NOI18N
0170:
0171: private static final String HG_EXPORT_CMD = "export"; // NOI18N
0172: private static final String HG_IMPORT_CMD = "import"; // NOI18N
0173:
0174: private static final String HG_RENAME_CMD = "rename"; // NOI18N
0175: private static final String HG_RENAME_AFTER_CMD = "-A"; // NOI18N
0176: private static final String HG_PATH_DEFAULT_CMD = "paths"; // NOI18N
0177: private static final String HG_PATH_DEFAULT_OPT = "default"; // NOI18N
0178: private static final String HG_PATH_DEFAULT_PUSH_OPT = "default-push"; // NOI18N
0179:
0180: // TODO: replace this hack
0181: // Causes /usr/bin/hgmerge script to return when a merge
0182: // has conflicts with exit 0, instead of throwing up EDITOR.
0183: // Problem is after this Hg thinks the merge succeded and no longer
0184: // marks repository with a merge needed flag. So Plugin needs to
0185: // track this merge required status by changing merge conflict file
0186: // status. If the cache is removed this information would be lost.
0187: //
0188: // Really need Hg to give us back merge status information,
0189: // which it currently does not
0190: private static final String HG_MERGE_CMD = "merge"; // NOI18N
0191: private static final String HG_MERGE_FORCE_CMD = "-f"; // NOI18N
0192: private static final String HG_MERGE_ENV = "EDITOR=success || $TEST -s"; // NOI18N
0193:
0194: public static final String HG_HGK_PATH_SOLARIS10 = "/usr/demo/mercurial"; // NOI18N
0195: private static final String HG_HGK_PATH_SOLARIS10_ENV = "PATH=/usr/bin/:/usr/sbin:/bin:"
0196: + HG_HGK_PATH_SOLARIS10; // NOI18N
0197:
0198: private static final String HG_PULL_CMD = "pull"; // NOI18N
0199: private static final String HG_UPDATE_CMD = "-u"; // NOI18N
0200: private static final String HG_PUSH_CMD = "push"; // NOI18N
0201: private static final String HG_UNBUNDLE_CMD = "unbundle"; // NOI18N
0202: private static final String HG_ROLLBACK_CMD = "rollback"; // NOI18N
0203: private static final String HG_BACKOUT_CMD = "backout"; // NOI18N
0204: private static final String HG_BACKOUT_MERGE_CMD = "--merge"; // NOI18N
0205: private static final String HG_BACKOUT_COMMIT_MSG_CMD = "-m"; // NOI18N
0206: private static final String HG_REV_CMD = "-r"; // NOI18N
0207:
0208: private static final String HG_STRIP_CMD = "strip"; // NOI18N
0209: private static final String HG_STRIP_EXT_CMD = "extensions.mq="; // NOI18N
0210: private static final String HG_STRIP_NOBACKUP_CMD = "-n"; // NOI18N
0211: private static final String HG_STRIP_FORCE_MULTIHEAD_CMD = "-f"; // NOI18N
0212:
0213: private static final String HG_VERSION_CMD = "version"; // NOI18N
0214: private static final String HG_INCOMING_CMD = "incoming"; // NOI18N
0215: private static final String HG_OUTGOING_CMD = "outgoing"; // NOI18N
0216: private static final String HG_VIEW_CMD = "view"; // NOI18N
0217: private static final String HG_VERBOSE_CMD = "-v"; // NOI18N
0218: private static final String HG_CONFIG_OPTION_CMD = "--config"; // NOI18N
0219: private static final String HG_FETCH_EXT_CMD = "extensions.fetch="; // NOI18N
0220: private static final String HG_FETCH_CMD = "fetch"; // NOI18N
0221: public static final String HG_PROXY_ENV = "http_proxy="; // NOI18N
0222:
0223: private static final String HG_MERGE_NEEDED_ERR = "(run 'hg heads' to see heads, 'hg merge' to merge)"; // NOI18N
0224: public static final String HG_MERGE_CONFLICT_ERR = "conflicts detected in "; // NOI18N
0225: public static final String HG_MERGE_CONFLICT_WIN1_ERR = "merging"; // NOI18N
0226: public static final String HG_MERGE_CONFLICT_WIN2_ERR = "failed!"; // NOI18N
0227: private static final String HG_MERGE_MULTIPLE_HEADS_ERR = "abort: repo has "; // NOI18N
0228: private static final String HG_MERGE_UNCOMMITTED_ERR = "abort: outstanding uncommitted merges"; // NOI18N
0229:
0230: private static final String HG_MERGE_UNAVAILABLE_ERR = "is not recognized as an internal or external command";
0231:
0232: private static final String HG_NO_CHANGES_ERR = "no changes found"; // NOI18N
0233: private final static String HG_CREATE_NEW_BRANCH_ERR = "abort: push creates new remote branches!"; // NOI18N
0234: private final static String HG_HEADS_CREATED_ERR = "(+1 heads)"; // NOI18N
0235: private final static String HG_NO_HG_CMD_FOUND_ERR = "hg: not found";
0236: private final static String HG_ARG_LIST_TOO_LONG_ERR = "Arg list too long";
0237:
0238: private final static String HG_HEADS_CMD = "heads"; // NOI18N
0239:
0240: private static final String HG_NO_REPOSITORY_ERR = "There is no Mercurial repository here"; // NOI18N
0241: private static final String HG_NO_RESPONSE_ERR = "no suitable response from remote hg!"; // NOI18N
0242: private static final String HG_NOT_REPOSITORY_ERR = "does not appear to be an hg repository"; // NOI18N
0243: private static final String HG_REPOSITORY = "repository"; // NOI18N
0244: private static final String HG_NOT_FOUND_ERR = "not found!"; // NOI18N
0245: private static final String HG_UPDATE_SPAN_BRANCHES_ERR = "abort: update spans branches"; // NOI18N
0246: private static final String HG_ALREADY_TRACKED_ERR = " already tracked!"; // NOI18N
0247: private static final String HG_NOT_TRACKED_ERR = " no tracked!"; // NOI18N
0248: private static final String HG_CANNOT_READ_COMMIT_MESSAGE_ERR = "abort: can't read commit message"; // NOI18N
0249: private static final String HG_CANNOT_RUN_ERR = "Cannot run program"; // NOI18N
0250: private static final String HG_ABORT_ERR = "abort: "; // NOI18N
0251: private static final String HG_ABORT_PUSH_ERR = "abort: push creates new remote branches!"; // NOI18N
0252: private static final String HG_ABORT_NO_FILES_TO_COPY_ERR = "abort: no files to copy"; // NOI18N
0253: private static final String HG_ABORT_NO_DEFAULT_PUSH_ERR = "abort: repository default-push not found!"; // NOI18N
0254: private static final String HG_ABORT_NO_DEFAULT_ERR = "abort: repository default not found!"; // NOI18N
0255: private static final String HG_ABORT_POSSIBLE_PROXY_ERR = "abort: error: node name or service name not known"; // NOI18N
0256: private static final String HG_ABORT_UNCOMMITTED_CHANGES_ERR = "abort: outstanding uncommitted changes"; // NOI18N
0257: private static final String HG_BACKOUT_MERGE_NEEDED_ERR = "(use \"backout --merge\" if you want to auto-merge)";
0258: private static final String HG_ABORT_BACKOUT_MERGE_CSET_ERR = "abort: cannot back out a merge changeset without --parent"; // NOI18N"
0259:
0260: private static final String HG_NO_CHANGE_NEEDED_ERR = "no change needed"; // NOI18N
0261: private static final String HG_NO_ROLLBACK_ERR = "no rollback information available"; // NOI18N
0262: private static final String HG_NO_UPDATES_ERR = "0 files updated, 0 files merged, 0 files removed, 0 files unresolved"; // NOI18N
0263: private static final String HG_NO_VIEW_ERR = "hg: unknown command 'view'"; // NOI18N
0264: private static final String HG_HGK_NOT_FOUND_ERR = "sh: hgk: not found"; // NOI18N
0265: private static final String HG_NO_SUCH_FILE_ERR = "No such file"; // NOI18N
0266:
0267: private static final String HG_NO_REV_STRIP_ERR = "abort: unknown revision"; // NOI18N
0268: private static final String HG_LOCAL_CHANGES_STRIP_ERR = "abort: local changes found"; // NOI18N
0269: private static final String HG_MULTIPLE_HEADS_STRIP_ERR = "no rollback information available"; // NOI18N
0270:
0271: private static final char HG_STATUS_CODE_MODIFIED = 'M' + ' '; // NOI18N // STATUS_VERSIONED_MODIFIEDLOCALLY
0272: private static final char HG_STATUS_CODE_ADDED = 'A' + ' '; // NOI18N // STATUS_VERSIONED_ADDEDLOCALLY
0273: private static final char HG_STATUS_CODE_REMOVED = 'R' + ' '; // NOI18N // STATUS_VERSIONED_REMOVEDLOCALLY - still tracked, hg update will recover, hg commit
0274: private static final char HG_STATUS_CODE_CLEAN = 'C' + ' '; // NOI18N // STATUS_VERSIONED_UPTODATE
0275: private static final char HG_STATUS_CODE_DELETED = '!' + ' '; // NOI18N // STATUS_VERSIONED_DELETEDLOCALLY - still tracked, hg update will recover, hg commit no effect
0276: private static final char HG_STATUS_CODE_NOTTRACKED = '?' + ' '; // NOI18N // STATUS_NOTVERSIONED_NEWLOCALLY - not tracked
0277: private static final char HG_STATUS_CODE_IGNORED = 'I' + ' '; // NOI18N // STATUS_NOTVERSIONED_EXCLUDE - not shown by default
0278: private static final char HG_STATUS_CODE_CONFLICT = 'X' + ' '; // NOI18N // STATUS_VERSIONED_CONFLICT - TODO when Hg status supports conflict markers
0279:
0280: private static final char HG_STATUS_CODE_ABORT = 'a' + 'b'; // NOI18N
0281: public static final String HG_STR_CONFLICT_EXT = ".conflict~"; // NOI18N
0282:
0283: private static final String HG_EPOCH_PLUS_ONE_YEAR = "1971-01-01"; // NOI18N
0284:
0285: /**
0286: * Merge working directory with the head revision
0287: * Merge the contents of the current working directory and the
0288: * requested revision. Files that changed between either parent are
0289: * marked as changed for the next commit and a commit must be
0290: * performed before any further updates are allowed.
0291: *
0292: * @param File repository of the mercurial repository's root directory
0293: * @param Revision to merge with, if null will merge with default tip rev
0294: * @return hg merge output
0295: * @throws HgException
0296: */
0297: public static List<String> doMerge(File repository, String revStr)
0298: throws HgException {
0299: if (repository == null)
0300: return null;
0301: List<String> command = new ArrayList<String>();
0302: List<String> env = new ArrayList<String>();
0303:
0304: command.add(getHgCommand());
0305: command.add(HG_MERGE_CMD);
0306: command.add(HG_MERGE_FORCE_CMD);
0307: command.add(HG_OPT_REPOSITORY);
0308: command.add(repository.getAbsolutePath());
0309: if (revStr != null)
0310: command.add(revStr);
0311: env.add(HG_MERGE_ENV);
0312:
0313: List<String> list = execEnv(command, env);
0314: return list;
0315: }
0316:
0317: /**
0318: * Update the working directory to the tip revision.
0319: * By default, update will refuse to run if doing so would require
0320: * merging or discarding local changes.
0321: *
0322: * @param File repository of the mercurial repository's root directory
0323: * @param Boolean force an Update and overwrite any modified files in the working directory
0324: * @param String revision to be updated to
0325: * @param Boolean throw exception on error
0326: * @return hg update output
0327: * @throws org.netbeans.modules.mercurial.HgException
0328: */
0329: public static List<String> doUpdateAll(File repository,
0330: boolean bForce, String revision, boolean bThrowException)
0331: throws HgException {
0332: if (repository == null)
0333: return null;
0334: List<String> command = new ArrayList<String>();
0335:
0336: command.add(getHgCommand());
0337: command.add(HG_UPDATE_ALL_CMD);
0338: if (bForce)
0339: command.add(HG_UPDATE_FORCE_ALL_CMD);
0340: command.add(HG_OPT_REPOSITORY);
0341: command.add(repository.getAbsolutePath());
0342: if (revision != null) {
0343: command.add(revision);
0344: }
0345:
0346: List<String> list = exec(command);
0347: if (bThrowException) {
0348: if (!list.isEmpty()) {
0349: if (isErrorUpdateSpansBranches(list.get(0))) {
0350: throw new HgException(NbBundle.getMessage(
0351: HgCommand.class,
0352: "MSG_WARN_UPDATE_MERGE_TEXT"));
0353: } else if (isMergeAbortUncommittedMsg(list.get(0))) {
0354: throw new HgException(NbBundle.getMessage(
0355: HgCommand.class,
0356: "MSG_WARN_UPDATE_COMMIT_TEXT"));
0357: }
0358: }
0359: }
0360: return list;
0361: }
0362:
0363: public static List<String> doUpdateAll(File repository,
0364: boolean bForce, String revision) throws HgException {
0365: return doUpdateAll(repository, bForce, revision, true);
0366: }
0367:
0368: /**
0369: * Roll back the last transaction in this repository
0370: * Transactions are used to encapsulate the effects of all commands
0371: * that create new changesets or propagate existing changesets into a
0372: * repository. For example, the following commands are transactional,
0373: * and their effects can be rolled back:
0374: * commit, import, pull, push (with this repository as destination)
0375: * unbundle
0376: * There is only one level of rollback, and there is no way to undo a rollback.
0377: *
0378: * @param File repository of the mercurial repository's root directory
0379: * @return hg update output
0380: * @throws org.netbeans.modules.mercurial.HgException
0381: */
0382: public static List<String> doRollback(File repository,
0383: OutputLogger logger) throws HgException {
0384: if (repository == null)
0385: return null;
0386: List<String> command = new ArrayList<String>();
0387:
0388: command.add(getHgCommand());
0389: command.add(HG_ROLLBACK_CMD);
0390: command.add(HG_OPT_REPOSITORY);
0391: command.add(repository.getAbsolutePath());
0392:
0393: List<String> list = exec(command);
0394: if (list.isEmpty())
0395: handleError(command, list, NbBundle.getMessage(
0396: HgCommand.class, "MSG_ROLLBACK_FAILED"), logger);
0397:
0398: return list;
0399: }
0400:
0401: public static List<String> doBackout(File repository,
0402: String revision, boolean doMerge, String commitMsg,
0403: OutputLogger logger) throws HgException {
0404: if (repository == null)
0405: return null;
0406: List<String> env = new ArrayList<String>();
0407: List<String> command = new ArrayList<String>();
0408:
0409: command.add(getHgCommand());
0410: command.add(HG_BACKOUT_CMD);
0411: if (doMerge) {
0412: command.add(HG_BACKOUT_MERGE_CMD);
0413: env.add(HG_MERGE_ENV);
0414: }
0415:
0416: if (commitMsg != null && !commitMsg.equals("")) { // NOI18N
0417: command.add(HG_BACKOUT_COMMIT_MSG_CMD);
0418: command.add(commitMsg);
0419: } else {
0420: command.add(HG_BACKOUT_COMMIT_MSG_CMD);
0421: command.add(NbBundle.getMessage(HgCommand.class,
0422: "MSG_BACKOUT_MERGE_COMMIT_MSG", revision)); // NOI18N
0423: }
0424:
0425: command.add(HG_OPT_REPOSITORY);
0426: command.add(repository.getAbsolutePath());
0427: if (revision != null) {
0428: command.add(HG_REV_CMD);
0429: command.add(revision);
0430: }
0431:
0432: List<String> list;
0433: if (doMerge) {
0434: list = execEnv(command, env);
0435: } else {
0436: list = exec(command);
0437: }
0438: if (list.isEmpty())
0439: handleError(command, list, NbBundle.getMessage(
0440: HgCommand.class, "MSG_BACKOUT_FAILED"), logger);
0441:
0442: return list;
0443: }
0444:
0445: public static List<String> doStrip(File repository,
0446: String revision, boolean doForceMultiHead,
0447: boolean doBackup, OutputLogger logger) throws HgException {
0448: if (repository == null)
0449: return null;
0450: List<String> command = new ArrayList<String>();
0451:
0452: command.add(getHgCommand());
0453: command.add(HG_CONFIG_OPTION_CMD);
0454: command.add(HG_STRIP_EXT_CMD);
0455: command.add(HG_STRIP_CMD);
0456: if (doForceMultiHead) {
0457: command.add(HG_STRIP_FORCE_MULTIHEAD_CMD);
0458: }
0459: if (!doBackup) {
0460: command.add(HG_STRIP_NOBACKUP_CMD);
0461: }
0462: command.add(HG_OPT_REPOSITORY);
0463: command.add(repository.getAbsolutePath());
0464: if (revision != null) {
0465: command.add(revision);
0466: }
0467:
0468: List<String> list = exec(command);
0469: if (list.isEmpty())
0470: handleError(command, list, NbBundle.getMessage(
0471: HgCommand.class, "MSG_STRIP_FAILED"), logger);
0472:
0473: return list;
0474: }
0475:
0476: /**
0477: * Return the version of hg, e.g. "0.9.3". // NOI18N
0478: *
0479: * @return String
0480: */
0481: public static String getHgVersion() {
0482: List<String> list = new LinkedList<String>();
0483: try {
0484: list = execForVersionCheck();
0485: } catch (HgException ex) {
0486: // Ignore Exception
0487: return null;
0488: }
0489: if (!list.isEmpty()) {
0490: int start = list.get(0).indexOf('(');
0491: int end = list.get(0).indexOf(')');
0492: if (start != -1 && end != -1) {
0493: return list.get(0).substring(start + 9, end);
0494: }
0495: }
0496: return null;
0497: }
0498:
0499: /**
0500: * Pull changes from the default pull locarion and update working directory.
0501: * By default, update will refuse to run if doing so would require
0502: * merging or discarding local changes.
0503: *
0504: * @param File repository of the mercurial repository's root directory
0505: * @return hg pull output
0506: * @throws org.netbeans.modules.mercurial.HgException
0507: */
0508: public static List<String> doPull(File repository,
0509: OutputLogger logger) throws HgException {
0510: return doPull(repository, null, logger);
0511: }
0512:
0513: /**
0514: * Pull changes from the specified repository and
0515: * update working directory.
0516: * By default, update will refuse to run if doing so would require
0517: * merging or discarding local changes.
0518: *
0519: * @param File repository of the mercurial repository's root directory
0520: * @param String source repository to pull from
0521: * @return hg pull output
0522: * @throws org.netbeans.modules.mercurial.HgException
0523: */
0524: public static List<String> doPull(File repository, String from,
0525: OutputLogger logger) throws HgException {
0526: if (repository == null)
0527: return null;
0528: List<String> command = new ArrayList<String>();
0529:
0530: command.add(getHgCommand());
0531: command.add(HG_PULL_CMD);
0532: command.add(HG_UPDATE_CMD);
0533: command.add(HG_OPT_REPOSITORY);
0534: command.add(repository.getAbsolutePath());
0535: if (from != null) {
0536: command.add(from);
0537: }
0538:
0539: List<String> list;
0540: String defaultPull = new HgConfigFiles(repository)
0541: .getDefaultPull(false);
0542: String proxy = getGlobalProxyIfNeeded(defaultPull, true, logger);
0543: if (proxy != null) {
0544: List<String> env = new ArrayList<String>();
0545: env.add(HG_PROXY_ENV + proxy);
0546: list = execEnv(command, env);
0547: } else {
0548: list = exec(command);
0549: }
0550:
0551: if (!list.isEmpty() && isErrorAbort(list.get(list.size() - 1))) {
0552: handleError(command, list, NbBundle.getMessage(
0553: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
0554: }
0555: return list;
0556: }
0557:
0558: /**
0559: * Unbundle changes from the specified local source repository and
0560: * update working directory.
0561: * By default, update will refuse to run if doing so would require
0562: * merging or discarding local changes.
0563: *
0564: * @param File repository of the mercurial repository's root directory
0565: * @param File bundle identfies the compressed changegroup file to be applied
0566: * @return hg unbundle output
0567: * @throws org.netbeans.modules.mercurial.HgException
0568: */
0569: public static List<String> doUnbundle(File repository, File bundle,
0570: OutputLogger logger) throws HgException {
0571: if (repository == null)
0572: return null;
0573: List<String> command = new ArrayList<String>();
0574:
0575: command.add(getHgCommand());
0576: command.add(HG_UNBUNDLE_CMD);
0577: command.add(HG_UPDATE_CMD);
0578: command.add(HG_OPT_REPOSITORY);
0579: command.add(repository.getAbsolutePath());
0580: if (bundle != null) {
0581: command.add(bundle.getAbsolutePath());
0582: }
0583:
0584: List<String> list = exec(command);
0585: if (!list.isEmpty() && isErrorAbort(list.get(list.size() - 1))) {
0586: handleError(command, list, NbBundle.getMessage(
0587: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
0588: }
0589: return list;
0590: }
0591:
0592: /**
0593: * Show the changesets that would be pulled if a pull
0594: * was requested from the default pull location
0595: *
0596: * @param File repository of the mercurial repository's root directory
0597: * @return hg incoming output
0598: * @throws org.netbeans.modules.mercurial.HgException
0599: */
0600: public static List<String> doIncoming(File repository,
0601: OutputLogger logger) throws HgException {
0602: return doIncoming(repository, null, null, logger);
0603: }
0604:
0605: /**
0606: * Show the changesets that would be pulled if a pull
0607: * was requested from the specified repository
0608: *
0609: * @param File repository of the mercurial repository's root directory
0610: * @param String source repository to query
0611: * @param File bundle to store downloaded changesets.
0612: * @return hg incoming output
0613: * @throws org.netbeans.modules.mercurial.HgException
0614: */
0615: public static List<String> doIncoming(File repository, String from,
0616: File bundle, OutputLogger logger) throws HgException {
0617: if (repository == null)
0618: return null;
0619: List<String> command = new ArrayList<String>();
0620:
0621: command.add(getHgCommand());
0622: command.add(HG_INCOMING_CMD);
0623: command.add(HG_VERBOSE_CMD);
0624: command.add(HG_OPT_REPOSITORY);
0625: command.add(repository.getAbsolutePath());
0626: if (bundle != null) {
0627: command.add(HG_OPT_BUNDLE);
0628: command.add(bundle.getAbsolutePath());
0629: }
0630: if (from != null) {
0631: command.add(from);
0632: }
0633:
0634: List<String> list;
0635: String defaultPull = new HgConfigFiles(repository)
0636: .getDefaultPull(false);
0637: String proxy = getGlobalProxyIfNeeded(defaultPull, false, null);
0638: if (proxy != null) {
0639: List<String> env = new ArrayList<String>();
0640: env.add(HG_PROXY_ENV + proxy);
0641: list = execEnv(command, env);
0642: } else {
0643: list = exec(command);
0644: }
0645:
0646: if (!list.isEmpty() && isErrorAbort(list.get(list.size() - 1))) {
0647: handleError(command, list, NbBundle.getMessage(
0648: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
0649: }
0650: return list;
0651: }
0652:
0653: /**
0654: * Show the changesets that would be pushed if a push
0655: * was requested to the specified local source repository
0656: *
0657: * @param File repository of the mercurial repository's root directory
0658: * @param String source repository to query
0659: * @return hg outgoing output
0660: * @throws org.netbeans.modules.mercurial.HgException
0661: */
0662: public static List<String> doOutgoing(File repository, String to,
0663: OutputLogger logger) throws HgException {
0664: if (repository == null)
0665: return null;
0666: List<String> command = new ArrayList<String>();
0667:
0668: command.add(getHgCommand());
0669: command.add(HG_OUTGOING_CMD);
0670: command.add(HG_VERBOSE_CMD);
0671: command.add(HG_OPT_REPOSITORY);
0672: command.add(repository.getAbsolutePath());
0673: command.add(to);
0674:
0675: List<String> list;
0676: String defaultPush = new HgConfigFiles(repository)
0677: .getDefaultPush(false);
0678: String proxy = getGlobalProxyIfNeeded(defaultPush, false, null);
0679: if (proxy != null) {
0680: List<String> env = new ArrayList<String>();
0681: env.add(HG_PROXY_ENV + proxy);
0682: list = execEnv(command, env);
0683: } else {
0684: list = exec(command);
0685: }
0686: if (!list.isEmpty() && isErrorAbort(list.get(list.size() - 1))) {
0687: handleError(command, list, NbBundle.getMessage(
0688: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
0689: }
0690: return list;
0691: }
0692:
0693: /**
0694: * Push changes to the specified repository
0695: * By default, push will refuse to run if doing so would create multiple heads
0696: *
0697: * @param File repository of the mercurial repository's root directory
0698: * @param String source repository to push to
0699: * @return hg push output
0700: * @throws org.netbeans.modules.mercurial.HgException
0701: */
0702: public static List<String> doPush(File repository, String to,
0703: OutputLogger logger) throws HgException {
0704: if (repository == null || to == null)
0705: return null;
0706: List<String> command = new ArrayList<String>();
0707:
0708: command.add(getHgCommand());
0709: command.add(HG_PUSH_CMD);
0710: command.add(HG_OPT_REPOSITORY);
0711: command.add(repository.getAbsolutePath());
0712: command.add(to);
0713:
0714: List<String> list;
0715: String defaultPush = new HgConfigFiles(repository)
0716: .getDefaultPush(false);
0717: String proxy = getGlobalProxyIfNeeded(defaultPush, true, logger);
0718: if (proxy != null) {
0719: List<String> env = new ArrayList<String>();
0720: env.add(HG_PROXY_ENV + proxy);
0721: list = execEnv(command, env);
0722: } else {
0723: list = exec(command);
0724: }
0725:
0726: if (!list.isEmpty()
0727: && !isErrorAbortPush(list.get(list.size() - 1))
0728: && isErrorAbort(list.get(list.size() - 1))) {
0729: handleError(command, list, NbBundle.getMessage(
0730: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
0731: }
0732: return list;
0733: }
0734:
0735: /**
0736: * Run the command hg view for the specified repository
0737: *
0738: * @param File repository of the mercurial repository's root directory
0739: * @throws org.netbeans.modules.mercurial.HgException
0740: */
0741: public static List<String> doView(File repository,
0742: OutputLogger logger) throws HgException {
0743: if (repository == null)
0744: return null;
0745: List<String> command = new ArrayList<String>();
0746: List<String> env = new ArrayList<String>();
0747:
0748: command.add(getHgCommand());
0749: command.add(HG_VIEW_CMD);
0750: command.add(HG_OPT_REPOSITORY);
0751: command.add(repository.getAbsolutePath());
0752:
0753: List<String> list;
0754:
0755: if (HgUtils.isSolaris()) {
0756: env.add(HG_HGK_PATH_SOLARIS10_ENV);
0757: list = execEnv(command, env);
0758: } else {
0759: list = exec(command);
0760: }
0761:
0762: if (!list.isEmpty()) {
0763: if (isErrorNoView(list.get(list.size() - 1))) {
0764: throw new HgException(NbBundle.getMessage(
0765: HgCommand.class, "MSG_WARN_NO_VIEW_TEXT"));
0766: } else if (isErrorHgkNotFound(list.get(0))) {
0767: throw new HgException(NbBundle.getMessage(
0768: HgCommand.class, "MSG_WARN_HGK_NOT_FOUND_TEXT"));
0769: } else if (isErrorAbort(list.get(list.size() - 1))) {
0770: handleError(command, list, NbBundle.getMessage(
0771: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
0772: }
0773: }
0774: return list;
0775: }
0776:
0777: private static String getGlobalProxyIfNeeded(String defaultPath,
0778: boolean bOutputDetails, OutputLogger logger) {
0779: String proxy = null;
0780: if (defaultPath != null
0781: && (defaultPath.startsWith("http:") || defaultPath
0782: .startsWith("https:"))) { // NOI18N
0783: HgProxySettings ps = new HgProxySettings();
0784: if (ps.isManualSetProxy()) {
0785: if ((defaultPath.startsWith("http:") && !ps
0786: .getHttpHost().equals(""))
0787: || (defaultPath.startsWith("https:")
0788: && !ps.getHttpHost().equals("") && ps
0789: .getHttpsHost().equals(""))) { // NOI18N
0790: proxy = ps.getHttpHost();
0791: if (proxy != null && !proxy.equals("")) {
0792: proxy += ps.getHttpPort() > -1 ? ":"
0793: + Integer.toString(ps.getHttpPort())
0794: : ""; // NOI18N
0795: } else {
0796: proxy = null;
0797: }
0798: } else if (defaultPath.startsWith("https:")
0799: && !ps.getHttpsHost().equals("")) { // NOI18N
0800: proxy = ps.getHttpsHost();
0801: if (proxy != null && !proxy.equals("")) {
0802: proxy += ps.getHttpsPort() > -1 ? ":"
0803: + Integer.toString(ps.getHttpsPort())
0804: : ""; // NOI18N
0805: } else {
0806: proxy = null;
0807: }
0808: }
0809: }
0810: }
0811: if (proxy != null && bOutputDetails) {
0812: logger.output(NbBundle.getMessage(HgCommand.class,
0813: "MSG_USING_PROXY_INFO", proxy)); // NOI18N
0814: }
0815: return proxy;
0816: }
0817:
0818: /**
0819: * Run the fetch extension for the specified repository
0820: *
0821: * @param File repository of the mercurial repository's root directory
0822: * @throws org.netbeans.modules.mercurial.HgException
0823: */
0824: public static List<String> doFetch(File repository,
0825: OutputLogger logger) throws HgException {
0826: if (repository == null)
0827: return null;
0828: List<String> command = new ArrayList<String>();
0829:
0830: command.add(getHgCommand());
0831: command.add(HG_CONFIG_OPTION_CMD);
0832: command.add(HG_FETCH_EXT_CMD);
0833: command.add(HG_FETCH_CMD);
0834: command.add(HG_OPT_REPOSITORY);
0835: command.add(repository.getAbsolutePath());
0836:
0837: List<String> list;
0838: String defaultPull = new HgConfigFiles(repository)
0839: .getDefaultPull(false);
0840: String proxy = getGlobalProxyIfNeeded(defaultPull, true, logger);
0841: if (proxy != null) {
0842: List<String> env = new ArrayList<String>();
0843: env.add(HG_PROXY_ENV + proxy);
0844: list = execEnv(command, env);
0845: } else {
0846: list = exec(command);
0847: }
0848:
0849: if (!list.isEmpty()) {
0850: if (isErrorAbort(list.get(list.size() - 1))) {
0851: handleError(command, list, NbBundle.getMessage(
0852: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
0853: }
0854: }
0855: return list;
0856: }
0857:
0858: private static List<HgLogMessage> processLogMessages(
0859: List<String> list, final List<HgLogMessage> messages) {
0860: String rev, author, desc, date, id, fm, fa, fd, fc;
0861: if (list != null && !list.isEmpty()) {
0862: rev = author = desc = date = id = fm = fa = fd = fc = null;
0863: boolean bEnd = false;
0864: for (String s : list) {
0865: if (s.indexOf(HG_LOG_REVISION_OUT) == 0) {
0866: rev = s.substring(HG_LOG_REVISION_OUT.length())
0867: .trim();
0868: } else if (s.indexOf(HG_LOG_AUTHOR_OUT) == 0) {
0869: author = s.substring(HG_LOG_AUTHOR_OUT.length())
0870: .trim();
0871: } else if (s.indexOf(HG_LOG_DESCRIPTION_OUT) == 0) {
0872: desc = s.substring(HG_LOG_DESCRIPTION_OUT.length())
0873: .trim();
0874: } else if (s.indexOf(HG_LOG_DATE_OUT) == 0) {
0875: date = s.substring(HG_LOG_DATE_OUT.length()).trim();
0876: } else if (s.indexOf(HG_LOG_ID_OUT) == 0) {
0877: id = s.substring(HG_LOG_ID_OUT.length()).trim();
0878: } else if (s.indexOf(HG_LOG_FILEMODS_OUT) == 0) {
0879: fm = s.substring(HG_LOG_FILEMODS_OUT.length())
0880: .trim();
0881: } else if (s.indexOf(HG_LOG_FILEADDS_OUT) == 0) {
0882: fa = s.substring(HG_LOG_FILEADDS_OUT.length())
0883: .trim();
0884: } else if (s.indexOf(HG_LOG_FILEDELS_OUT) == 0) {
0885: fd = s.substring(HG_LOG_FILEDELS_OUT.length())
0886: .trim();
0887: } else if (s.indexOf(HG_LOG_FILECOPIESS_OUT) == 0) {
0888: fc = s.substring(HG_LOG_FILECOPIESS_OUT.length())
0889: .trim();
0890: } else if (s.indexOf(HG_LOG_ENDCS_OUT) == 0) {
0891: bEnd = true;
0892: } else {
0893: // Ignore all other lines
0894: }
0895:
0896: if (rev != null & bEnd) {
0897: messages.add(new HgLogMessage(rev, author, desc,
0898: date, id, fm, fa, fd, fc));
0899: rev = author = desc = date = id = fm = fa = fd = fc = null;
0900: bEnd = false;
0901: }
0902: }
0903: }
0904: return messages;
0905: }
0906:
0907: public static HgLogMessage[] getIncomingMessages(
0908: final String rootUrl, String toRevision,
0909: boolean bShowMerges, OutputLogger logger) {
0910: final List<HgLogMessage> messages = new ArrayList<HgLogMessage>(
0911: 0);
0912: final File root = new File(rootUrl);
0913:
0914: try {
0915:
0916: List<String> list = new LinkedList<String>();
0917: list = HgCommand.doIncomingForSearch(root, toRevision,
0918: bShowMerges, logger);
0919: processLogMessages(list, messages);
0920:
0921: } catch (HgException ex) {
0922: NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(
0923: ex);
0924: DialogDisplayer.getDefault().notifyLater(e);
0925: } finally {
0926: logger.closeLog();
0927: }
0928:
0929: return messages.toArray(new HgLogMessage[0]);
0930: }
0931:
0932: public static HgLogMessage[] getOutMessages(final String rootUrl,
0933: String toRevision, boolean bShowMerges, OutputLogger logger) {
0934: final List<HgLogMessage> messages = new ArrayList<HgLogMessage>(
0935: 0);
0936: final File root = new File(rootUrl);
0937:
0938: try {
0939:
0940: List<String> list = new LinkedList<String>();
0941: list = HgCommand.doOutForSearch(root, toRevision,
0942: bShowMerges, logger);
0943: processLogMessages(list, messages);
0944:
0945: } catch (HgException ex) {
0946: NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(
0947: ex);
0948: DialogDisplayer.getDefault().notifyLater(e);
0949: } finally {
0950: logger.closeLog();
0951: }
0952:
0953: return messages.toArray(new HgLogMessage[0]);
0954: }
0955:
0956: public static HgLogMessage[] getLogMessages(final String rootUrl,
0957: final Set<File> files, String fromRevision,
0958: String toRevision, boolean bShowMerges, OutputLogger logger) {
0959: final List<HgLogMessage> messages = new ArrayList<HgLogMessage>(
0960: 0);
0961: final File root = new File(rootUrl);
0962:
0963: try {
0964: String headRev = HgCommand.getLastRevision(root, null);
0965: if (headRev == null) {
0966: return messages.toArray(new HgLogMessage[0]);
0967: }
0968:
0969: List<String> list = new LinkedList<String>();
0970: list = HgCommand.doLogForHistory(root,
0971: files != null ? new ArrayList<File>(files) : null,
0972: fromRevision, toRevision, headRev, bShowMerges,
0973: logger);
0974: processLogMessages(list, messages);
0975:
0976: } catch (HgException ex) {
0977: NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(
0978: ex);
0979: DialogDisplayer.getDefault().notifyLater(e);
0980: } finally {
0981: logger.closeLog();
0982: }
0983:
0984: return messages.toArray(new HgLogMessage[0]);
0985: }
0986:
0987: /**
0988: * Determines whether repository requires a merge - has more than 1 heads
0989: *
0990: * @param File repository of the mercurial repository's root directory
0991: * @return Boolean which is true if the repository needs a merge
0992: */
0993: public static Boolean isMergeRequired(File repository) {
0994: if (repository == null)
0995: return false;
0996:
0997: try {
0998: List<String> list = getHeadRevisions(repository);
0999:
1000: if (!list.isEmpty() && list.size() > 1) {
1001: Mercurial.LOG.log(Level.FINE,
1002: "isMergeRequired(): TRUE " + list); // NOI18N
1003: return true;
1004: } else {
1005: Mercurial.LOG.log(Level.FINE,
1006: "isMergeRequired(): FALSE " + list); // NOI18N
1007: return false;
1008: }
1009: } catch (HgException e) {
1010: return false;
1011: }
1012: }
1013:
1014: /**
1015: * Determines whether anything has been committed to the repository
1016: *
1017: * @param File repository of the mercurial repository's root directory
1018: * @return Boolean which is true if the repository has revision history.
1019: */
1020: public static Boolean hasHistory(File repository) {
1021: if (repository == null)
1022: return false;
1023:
1024: List<String> command = new ArrayList<String>();
1025:
1026: command.add(getHgCommand());
1027: command.add(HG_LOG_CMD);
1028: command.add(HG_LOG_LIMIT_ONE_CMD);
1029: command.add(HG_OPT_REPOSITORY);
1030: command.add(repository.getAbsolutePath());
1031:
1032: try {
1033: List<String> list = exec(command);
1034: if (!list.isEmpty() && isErrorAbort(list.get(0)))
1035: return false;
1036: else
1037: return !list.isEmpty();
1038: } catch (HgException e) {
1039: return false;
1040: }
1041: }
1042:
1043: /**
1044: * Determines the previous name of the specified file
1045: * We make the assumption that the previous file name is in the
1046: * list of files returned by hg log command immediately befor
1047: * the file we started with.
1048: *
1049: * @param File repository of the mercurial repository's root directory
1050: * @param File file of the file whose previous name is required
1051: * @param String revision which the revision to start from.
1052: * @return File for the previous name of the file
1053: */
1054: private static File getPreviousName(File repository, File file,
1055: String revision) throws HgException {
1056: if (repository == null)
1057: return null;
1058: if (revision == null)
1059: return null;
1060:
1061: List<String> command = new ArrayList<String>();
1062:
1063: command.add(getHgCommand());
1064: command.add(HG_LOG_CMD);
1065: command.add(HG_OPT_FOLLOW);
1066: command.add(HG_OPT_REPOSITORY);
1067: command.add(repository.getAbsolutePath());
1068: command.add(HG_FLAG_REV_CMD);
1069: command.add(revision);
1070: command.add(HG_GET_PREVIOUS_TEMPLATE_CMD);
1071:
1072: command.add(file.getAbsolutePath());
1073:
1074: List<String> list = exec(command);
1075: try {
1076: list = exec(command);
1077: if (!list.isEmpty() && isErrorAbort(list.get(0)))
1078: return null;
1079: } catch (HgException e) {
1080: return null;
1081: }
1082: String[] fileNames = list.get(0).split(" ");
1083: for (int j = fileNames.length - 1; j > 0; j--) {
1084: File name = new File(repository, fileNames[j]);
1085: if (name.equals(file)) {
1086: return new File(repository, fileNames[j - 1]);
1087: }
1088: }
1089: return null;
1090: }
1091:
1092: /**
1093: * Retrives the log information with just first line of commit message for the specified file.
1094: *
1095: * @param File repository of the mercurial repository's root directory
1096: * @param File of file which revision history is to be retrieved.
1097: * @return List<String> list of the log entries for the specified file.
1098: * @throws org.netbeans.modules.mercurial.HgException
1099: */
1100: public static List<String> doLogShort(File repository, File file,
1101: OutputLogger logger) throws HgException {
1102: return doLog(repository, file, HG_LOG_TEMPLATE_SHORT_CMD,
1103: false, logger);
1104: }
1105:
1106: /**
1107: * Retrives the log information with the full commit message for the specified file.
1108: *
1109: * @param File repository of the mercurial repository's root directory
1110: * @param File of file which revision history is to be retrieved.
1111: * @return List<String> list of the log entries for the specified file.
1112: * @throws org.netbeans.modules.mercurial.HgException
1113: */
1114: public static List<String> doLogLong(File repository, File file,
1115: OutputLogger logger) throws HgException {
1116: return doLog(repository, file, HG_LOG_TEMPLATE_LONG_CMD, false,
1117: logger);
1118: }
1119:
1120: /**
1121: * Retrives the log information for the specified file, as defined by the LOG_TEMPLATE.
1122: *
1123: * @param File repository of the mercurial repository's root directory
1124: * @param File of file which revision history is to be retrieved.
1125: * @param String Template specifying how output should be returned
1126: * @param boolean flag indicating if debug param should be used - required to get all file mod, add, del info
1127: * @return List<String> list of the log entries for the specified file.
1128: * @throws org.netbeans.modules.mercurial.HgException
1129: */
1130: public static List<String> doLog(File repository, File file,
1131: String LOG_TEMPLATE, boolean bDebug, OutputLogger logger)
1132: throws HgException {
1133: if (repository == null)
1134: return null;
1135:
1136: List<String> command = new ArrayList<String>();
1137:
1138: command.add(getHgCommand());
1139: command.add(HG_LOG_CMD);
1140: if (!file.isDirectory()) {
1141: command.add(HG_OPT_FOLLOW);
1142: }
1143: command.add(HG_OPT_REPOSITORY);
1144: command.add(repository.getAbsolutePath());
1145: if (bDebug) {
1146: command.add(HG_LOG_DEBUG_CMD);
1147: }
1148: command.add(LOG_TEMPLATE);
1149: command.add(file.getAbsolutePath());
1150:
1151: List<String> list = exec(command);
1152: if (!list.isEmpty()) {
1153: if (isErrorNoRepository(list.get(0))) {
1154: handleError(command, list, NbBundle.getMessage(
1155: HgCommand.class, "MSG_NO_REPOSITORY_ERR"),
1156: logger);
1157: } else if (isErrorAbort(list.get(0))) {
1158: handleError(command, list, NbBundle.getMessage(
1159: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
1160: }
1161: }
1162: return list;
1163: }
1164:
1165: /**
1166: * Retrives the log information for the specified files.
1167: *
1168: * @param File repository of the mercurial repository's root directory
1169: * @param List<File> of files which revision history is to be retrieved.
1170: * @param String Template specifying how output should be returned
1171: * @param boolean flag indicating if debug param should be used - required to get all file mod, add, del info
1172: * @return List<String> list of the log entries for the specified file.
1173: * @throws org.netbeans.modules.mercurial.HgException
1174: */
1175: public static List<String> doLog(File repository, List<File> files,
1176: String LOG_TEMPLATE, boolean bDebug, OutputLogger logger)
1177: throws HgException {
1178: if (repository == null)
1179: return null;
1180: if (files != null && files.isEmpty())
1181: return null;
1182:
1183: List<String> command = new ArrayList<String>();
1184:
1185: command.add(getHgCommand());
1186: command.add(HG_VERBOSE_CMD);
1187: command.add(HG_LOG_CMD);
1188: boolean doFollow = true;
1189: if (files != null) {
1190: for (File f : files) {
1191: if (f.isDirectory()) {
1192: doFollow = false;
1193: break;
1194: }
1195: }
1196: }
1197: if (doFollow) {
1198: command.add(HG_OPT_FOLLOW);
1199: }
1200: command.add(HG_OPT_REPOSITORY);
1201: command.add(repository.getAbsolutePath());
1202: if (bDebug) {
1203: command.add(HG_LOG_DEBUG_CMD);
1204: }
1205: if (LOG_TEMPLATE != null) {
1206: command.add(LOG_TEMPLATE);
1207: }
1208: if (files != null) {
1209: for (File f : files) {
1210: command.add(f.getAbsolutePath());
1211: }
1212: }
1213:
1214: List<String> list = exec(command);
1215: if (!list.isEmpty()) {
1216: if (isErrorNoRepository(list.get(0))) {
1217: handleError(command, list, NbBundle.getMessage(
1218: HgCommand.class, "MSG_NO_REPOSITORY_ERR"),
1219: logger);
1220: } else if (isErrorAbort(list.get(0))) {
1221: handleError(command, list, NbBundle.getMessage(
1222: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
1223: }
1224: }
1225: return list;
1226: }
1227:
1228: /**
1229: * Retrives the log information for the specified files.
1230: *
1231: * @param File repository of the mercurial repository's root directory
1232: * @param List<File> of files which revision history is to be retrieved.
1233: * @param String Template specifying how output should be returned
1234: * @param boolean flag indicating if debug param should be used - required to get all file mod, add, del info
1235: * @return List<String> list of the log entries for the specified file.
1236: * @throws org.netbeans.modules.mercurial.HgException
1237: */
1238: public static List<String> doLogForHistory(File repository,
1239: List<File> files, String from, String to, String headRev,
1240: boolean bShowMerges, OutputLogger logger)
1241: throws HgException {
1242: if (repository == null)
1243: return null;
1244: if (files != null && files.isEmpty())
1245: return null;
1246:
1247: List<String> command = new ArrayList<String>();
1248:
1249: command.add(getHgCommand());
1250: command.add(HG_VERBOSE_CMD);
1251: command.add(HG_LOG_CMD);
1252: boolean doFollow = true;
1253: if (files != null) {
1254: for (File f : files) {
1255: if (f.isDirectory()) {
1256: doFollow = false;
1257: break;
1258: }
1259: }
1260: }
1261: if (doFollow) {
1262: command.add(HG_OPT_FOLLOW);
1263: }
1264: if (!bShowMerges) {
1265: command.add(HG_LOG_NO_MERGES_CMD);
1266: }
1267: command.add(HG_OPT_REPOSITORY);
1268: command.add(repository.getAbsolutePath());
1269: command.add(HG_LOG_DEBUG_CMD);
1270:
1271: String dateStr = handleRevDates(from, to);
1272: if (dateStr != null) {
1273: command.add(HG_FLAG_DATE_CMD);
1274: command.add(dateStr);
1275: }
1276: String revStr = handleRevNumbers(from, to, headRev);
1277: if (dateStr == null && revStr != null) {
1278: command.add(HG_FLAG_REV_CMD);
1279: command.add(revStr);
1280: }
1281: command.add(HG_LOG_TEMPLATE_HISTORY_CMD);
1282:
1283: if (files != null) {
1284: for (File f : files) {
1285: command.add(f.getAbsolutePath());
1286: }
1287: }
1288: List<String> list = exec(command);
1289: if (!list.isEmpty()) {
1290: if (isErrorNoRepository(list.get(0))) {
1291: handleError(command, list, NbBundle.getMessage(
1292: HgCommand.class, "MSG_NO_REPOSITORY_ERR"),
1293: logger);
1294: } else if (isErrorAbort(list.get(0))) {
1295: handleError(command, list, NbBundle.getMessage(
1296: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
1297: }
1298: }
1299: return list;
1300: }
1301:
1302: /**
1303: * Retrives the Out information for the specified repository
1304: *
1305: * @param File repository of the mercurial repository's root directory
1306: * @return List<String> list of the out entries for the specified repo.
1307: * @throws org.netbeans.modules.mercurial.HgException
1308: */
1309: public static List<String> doOutForSearch(File repository,
1310: String to, boolean bShowMerges, OutputLogger logger)
1311: throws HgException {
1312: if (repository == null)
1313: return null;
1314:
1315: List<String> command = new ArrayList<String>();
1316:
1317: command.add(getHgCommand());
1318: command.add(HG_OUT_CMD);
1319: command.add(HG_OPT_REPOSITORY);
1320: command.add(repository.getAbsolutePath());
1321: if (!bShowMerges) {
1322: command.add(HG_LOG_NO_MERGES_CMD);
1323: }
1324: command.add(HG_LOG_DEBUG_CMD);
1325: String revStr = handleIncomingRevNumber(to);
1326: if (revStr != null) {
1327: command.add(HG_FLAG_REV_CMD);
1328: command.add(revStr);
1329: }
1330: command.add(HG_LOG_TEMPLATE_HISTORY_CMD);
1331:
1332: List<String> list;
1333: String defaultPush = new HgConfigFiles(repository)
1334: .getDefaultPush(false);
1335: String proxy = getGlobalProxyIfNeeded(defaultPush, false, null);
1336: if (proxy != null) {
1337: List<String> env = new ArrayList<String>();
1338: env.add(HG_PROXY_ENV + proxy);
1339: list = execEnv(command, env);
1340: } else {
1341: list = exec(command);
1342: }
1343: if (!list.isEmpty()) {
1344: if (isErrorNoDefaultPush(list.get(0))) {
1345: // Ignore
1346: } else if (isErrorNoRepository(list.get(0))) {
1347: handleError(command, list, NbBundle.getMessage(
1348: HgCommand.class, "MSG_NO_REPOSITORY_ERR"),
1349: logger);
1350: } else if (isErrorAbort(list.get(0))) {
1351: handleError(command, list, NbBundle.getMessage(
1352: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
1353: }
1354: }
1355: return list;
1356: }
1357:
1358: /**
1359: * Retrives the Incoming changeset information for the specified repository
1360: *
1361: * @param File repository of the mercurial repository's root directory
1362: * @return List<String> list of the out entries for the specified repo.
1363: * @throws org.netbeans.modules.mercurial.HgException
1364: */
1365: public static List<String> doIncomingForSearch(File repository,
1366: String to, boolean bShowMerges, OutputLogger logger)
1367: throws HgException {
1368: if (repository == null)
1369: return null;
1370:
1371: List<String> command = new ArrayList<String>();
1372:
1373: command.add(getHgCommand());
1374: command.add(HG_INCOMING_CMD);
1375: command.add(HG_OPT_REPOSITORY);
1376: command.add(repository.getAbsolutePath());
1377: if (!bShowMerges) {
1378: command.add(HG_LOG_NO_MERGES_CMD);
1379: }
1380: command.add(HG_LOG_DEBUG_CMD);
1381: String revStr = handleIncomingRevNumber(to);
1382: if (revStr != null) {
1383: command.add(HG_FLAG_REV_CMD);
1384: command.add(revStr);
1385: }
1386: command.add(HG_LOG_TEMPLATE_HISTORY_CMD);
1387:
1388: List<String> list = exec(command);
1389: String defaultPull = new HgConfigFiles(repository)
1390: .getDefaultPull(false);
1391: String proxy = getGlobalProxyIfNeeded(defaultPull, false, null);
1392: if (proxy != null) {
1393: List<String> env = new ArrayList<String>();
1394: env.add(HG_PROXY_ENV + proxy);
1395: list = execEnv(command, env);
1396: } else {
1397: list = exec(command);
1398: }
1399:
1400: if (!list.isEmpty()) {
1401: if (isErrorNoDefaultPath(list.get(0))) {
1402: // Ignore
1403: } else if (isErrorNoRepository(list.get(0))) {
1404: handleError(command, list, NbBundle.getMessage(
1405: HgCommand.class, "MSG_NO_REPOSITORY_ERR"),
1406: logger);
1407: } else if (isErrorAbort(list.get(0))
1408: || isErrorAbort(list.get(list.size() - 1))) {
1409: handleError(command, list, NbBundle.getMessage(
1410: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
1411: }
1412: }
1413: return list;
1414: }
1415:
1416: private static String handleRevDates(String from, String to) {
1417: // Check for Date range:
1418: Date fromDate = null;
1419: Date toDate = null;
1420: Date currentDate = new Date(); // Current Date
1421: Date epochPlusOneDate = null;
1422:
1423: try {
1424: epochPlusOneDate = new SimpleDateFormat("yyyy-MM-dd")
1425: .parse(HG_EPOCH_PLUS_ONE_YEAR); // NOI18N
1426: } catch (ParseException ex) {
1427: // Ignore invalid dates
1428: }
1429:
1430: // Set From date
1431: try {
1432: if (from != null)
1433: fromDate = new SimpleDateFormat("yyyy-MM-dd")
1434: .parse(from); // NOI18N
1435: } catch (ParseException ex) {
1436: // Ignore invalid dates
1437: }
1438:
1439: // Set To date
1440: try {
1441: if (to != null)
1442: toDate = new SimpleDateFormat("yyyy-MM-dd").parse(to); // NOI18N
1443: } catch (ParseException ex) {
1444: // Ignore invalid dates
1445: }
1446:
1447: // If From date is set, but To date is not - default To date to current date
1448: if (fromDate != null && toDate == null && to == null) {
1449: toDate = currentDate;
1450: to = new SimpleDateFormat("yyyy-MM-dd").format(toDate);
1451: }
1452: // If To date is set, but From date is not - default From date to 1971-01-01
1453: if (fromDate == null && from == null && toDate != null) {
1454: fromDate = epochPlusOneDate;
1455: from = HG_EPOCH_PLUS_ONE_YEAR; // NOI18N
1456: }
1457:
1458: // If using dates make sure both From and To are set to dates
1459: if ((fromDate != null && toDate == null && to != null)
1460: || (fromDate == null && from != null && toDate != null)) {
1461: HgUtils.warningDialog(HgCommand.class,
1462: "MSG_SEARCH_HISTORY_TITLE",// NOI18N
1463: "MSG_SEARCH_HISTORY_WARN_BOTHDATES_NEEDED_TEXT"); // NOI18N
1464: return null;
1465: }
1466:
1467: if (fromDate != null && toDate != null) {
1468: // Check From date - default to 1971-01-01 if From date is earlier than this
1469: if (epochPlusOneDate != null
1470: && fromDate.before(epochPlusOneDate)) {
1471: fromDate = epochPlusOneDate;
1472: from = HG_EPOCH_PLUS_ONE_YEAR; // NOI18N
1473: }
1474: // Set To date - default to current date if To date is later than this
1475: if (currentDate != null && toDate.after(currentDate)) {
1476: toDate = currentDate;
1477: to = new SimpleDateFormat("yyyy-MM-dd").format(toDate);
1478: }
1479:
1480: // Make sure the From date is before the To date
1481: if (fromDate.after(toDate)) {
1482: HgUtils
1483: .warningDialog(HgCommand.class,
1484: "MSG_SEARCH_HISTORY_TITLE",// NOI18N
1485: "MSG_SEARCH_HISTORY_WARN_FROM_BEFORE_TODATE_NEEDED_TEXT"); // NOI18N
1486: return null;
1487: }
1488: return from + " to " + to; // NOI18N
1489: }
1490: return null;
1491: }
1492:
1493: private static String handleIncomingRevNumber(String to) {
1494: int toInt = -1;
1495:
1496: // Handle users entering head or tip for revision, instead of a number
1497: if (to != null
1498: && (to.equalsIgnoreCase(HG_STATUS_FLAG_TIP_CMD) || to
1499: .equalsIgnoreCase(HG_HEAD_STR))) {
1500: to = HG_STATUS_FLAG_TIP_CMD;
1501: }
1502: try {
1503: toInt = Integer.parseInt(to);
1504: } catch (NumberFormatException e) {
1505: // ignore invalid numbers
1506: }
1507:
1508: return (toInt > -1) ? to : HG_STATUS_FLAG_TIP_CMD;
1509: }
1510:
1511: private static String handleRevNumbers(String from, String to,
1512: String headRev) {
1513: int fromInt = -1;
1514: int toInt = -1;
1515: int headRevInt = -1;
1516:
1517: // Handle users entering head or tip for revision, instead of a number
1518: if (from != null
1519: && (from.equalsIgnoreCase(HG_STATUS_FLAG_TIP_CMD) || from
1520: .equalsIgnoreCase(HG_HEAD_STR)))
1521: from = headRev;
1522: if (to != null
1523: && (to.equalsIgnoreCase(HG_STATUS_FLAG_TIP_CMD) || to
1524: .equalsIgnoreCase(HG_HEAD_STR)))
1525: to = headRev;
1526:
1527: try {
1528: fromInt = Integer.parseInt(from);
1529: } catch (NumberFormatException e) {
1530: // ignore invalid numbers
1531: }
1532: try {
1533: toInt = Integer.parseInt(to);
1534: } catch (NumberFormatException e) {
1535: // ignore invalid numbers
1536: }
1537: try {
1538: headRevInt = Integer.parseInt(headRev);
1539: } catch (NumberFormatException e) {
1540: // ignore invalid numbers
1541: }
1542:
1543: // Handle out of range revisions
1544: if (headRevInt > -1 && toInt > headRevInt) {
1545: to = headRev;
1546: toInt = headRevInt;
1547: }
1548: if (headRevInt > -1 && fromInt > headRevInt) {
1549: from = headRev;
1550: fromInt = headRevInt;
1551: }
1552:
1553: // Handle revision ranges
1554: String revStr = null;
1555: if (fromInt > -1 && toInt > -1) {
1556: revStr = from + ":" + to;
1557: } else if (fromInt > -1) {
1558: revStr = from + (headRevInt != -1 ? ":" + headRevInt : "");
1559: } else if (toInt > -1) {
1560: revStr = "0:" + to;
1561: }
1562:
1563: return revStr;
1564: }
1565:
1566: /**
1567: * Retrieves the base revision of the specified file to the
1568: * specified output file.
1569: *
1570: * @param File repository of the mercurial repository's root directory
1571: * @param File file in the mercurial repository
1572: * @param File outFile to contain the contents of the file
1573: * @throws org.netbeans.modules.mercurial.HgException
1574: */
1575: public static void doCat(File repository, File file, File outFile,
1576: OutputLogger logger) throws HgException {
1577: doCat(repository, file, outFile, "tip", false, logger); //NOI18N
1578: }
1579:
1580: /**
1581: * Retrieves the specified revision of the specified file to the
1582: * specified output file.
1583: *
1584: * @param File repository of the mercurial repository's root directory
1585: * @param File file in the mercurial repository
1586: * @param File outFile to contain the contents of the file
1587: * @param String of revision for the revision of the file to be
1588: * printed to the output file.
1589: * @return List<String> list of all the log entries
1590: * @throws org.netbeans.modules.mercurial.HgException
1591: */
1592: public static void doCat(File repository, File file, File outFile,
1593: String revision, OutputLogger logger) throws HgException {
1594: doCat(repository, file, outFile, revision, true, logger); //NOI18N
1595: }
1596:
1597: public static void doCat(File repository, File file, File outFile,
1598: String revision, boolean retry, OutputLogger logger)
1599: throws HgException {
1600: if (repository == null)
1601: return;
1602: if (file == null)
1603: return;
1604:
1605: List<String> command = new ArrayList<String>();
1606:
1607: command.add(getHgCommand());
1608: command.add(HG_CAT_CMD);
1609: command.add(HG_OPT_REPOSITORY);
1610: command.add(repository.getAbsolutePath());
1611: command.add(HG_FLAG_OUTPUT_CMD);
1612: command.add(outFile.getAbsolutePath());
1613:
1614: if (revision != null) {
1615: command.add(HG_FLAG_REV_CMD);
1616: command.add(revision);
1617: }
1618: command.add(file.getAbsolutePath());
1619: List<String> list = exec(command);
1620:
1621: if (!list.isEmpty()) {
1622: if (isErrorNoRepository(list.get(0))) {
1623: handleError(command, list, NbBundle.getMessage(
1624: HgCommand.class, "MSG_NO_REPOSITORY_ERR"),
1625: logger);
1626: } else if (isErrorAbort(list.get(0))) {
1627: handleError(command, list, NbBundle.getMessage(
1628: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
1629: }
1630: }
1631: if (outFile.length() == 0 && retry) {
1632: // Perhaps the file has changed its name
1633: String newRevision = Integer.toString(Integer
1634: .parseInt(revision) + 1);
1635: File prevFile = getPreviousName(repository, file,
1636: newRevision);
1637: if (prevFile != null) {
1638: doCat(repository, prevFile, outFile, revision, false,
1639: logger); //NOI18N
1640: }
1641: }
1642: }
1643:
1644: /**
1645: * Initialize a new repository in the given directory. If the given
1646: * directory does not exist, it is created. Will throw a HgException
1647: * if the repository already exists.
1648: *
1649: * @param root for the mercurial repository
1650: * @return void
1651: * @throws org.netbeans.modules.mercurial.HgException
1652: */
1653: public static void doCreate(File root, OutputLogger logger)
1654: throws HgException {
1655: if (root == null)
1656: return;
1657: List<String> command = new ArrayList<String>();
1658:
1659: command.add(getHgCommand());
1660: command.add(HG_CREATE_CMD);
1661: command.add(root.getAbsolutePath());
1662:
1663: List<String> list = exec(command);
1664: if (!list.isEmpty())
1665: handleError(command, list, NbBundle.getMessage(
1666: HgCommand.class, "MSG_CREATE_FAILED"), logger);
1667: }
1668:
1669: /**
1670: * Clone an exisiting repository to the specified target directory
1671: *
1672: * @param File repository of the mercurial repository's root directory
1673: * @param target directory to clone to
1674: * @return clone output
1675: * @throws org.netbeans.modules.mercurial.HgException
1676: */
1677: public static List<String> doClone(File repository, String target,
1678: OutputLogger logger) throws HgException {
1679: if (repository == null)
1680: return null;
1681: return doClone(repository.getAbsolutePath(), target, logger);
1682: }
1683:
1684: /**
1685: * Clone a repository to the specified target directory
1686: *
1687: * @param String repository of the mercurial repository
1688: * @param target directory to clone to
1689: * @return clone output
1690: * @throws org.netbeans.modules.mercurial.HgException
1691: */
1692: public static List<String> doClone(String repository,
1693: String target, OutputLogger logger) throws HgException {
1694: if (repository == null || target == null)
1695: return null;
1696:
1697: // Ensure that parent directory of target exists, creating if necessary
1698: File fileTarget = new File(target);
1699: File parentTarget = fileTarget.getParentFile();
1700: try {
1701: if (!parentTarget.mkdir()) {
1702: if (!parentTarget.isDirectory()) {
1703: Mercurial.LOG.log(Level.WARNING,
1704: "File.mkdir() failed for : "
1705: + parentTarget.getAbsolutePath()); // NOI18N
1706: throw (new HgException(NbBundle.getMessage(
1707: HgCommand.class,
1708: "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
1709: }
1710: }
1711: } catch (SecurityException e) {
1712: Mercurial.LOG.log(Level.WARNING, "File.mkdir() for : "
1713: + parentTarget.getAbsolutePath()
1714: + " threw SecurityException " + e.getMessage()); // NOI18N
1715: throw (new HgException(NbBundle.getMessage(HgCommand.class,
1716: "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
1717: }
1718: List<String> command = new ArrayList<String>();
1719:
1720: command.add(getHgCommand());
1721: command.add(HG_CLONE_CMD);
1722: command.add(HG_VERBOSE_CMD);
1723: // Workaround for http://www.selenic.com/mercurial/bts/issue776
1724: // Strip off file:// from start of repository
1725: if (repository.startsWith("file://")) {
1726: command.add(repository.substring(7));
1727: } else {
1728: command.add(repository);
1729: }
1730: command.add(target);
1731:
1732: List<String> list = exec(command);
1733: if (!list.isEmpty()) {
1734: if (isErrorNoRepository(list.get(0))) {
1735: handleError(command, list, NbBundle.getMessage(
1736: HgCommand.class, "MSG_NO_REPOSITORY_ERR"),
1737: logger);
1738: } else if (isErrorNoResponse(list.get(list.size() - 1))) {
1739: handleError(command, list, NbBundle.getMessage(
1740: HgCommand.class, "MSG_NO_RESPONSE_ERR"), logger);
1741: } else if (isErrorAbort(list.get(list.size() - 1))) {
1742: handleError(command, list, NbBundle.getMessage(
1743: HgCommand.class, "MSG_COMMAND_ABORTED"), logger);
1744: }
1745: }
1746: return list;
1747: }
1748:
1749: /**
1750: * Commits the list of Locally Changed files to the mercurial Repository
1751: *
1752: * @param File repository of the mercurial repository's root directory
1753: * @param List<files> of files to be committed to hg
1754: * @param String for commitMessage
1755: * @return void
1756: * @throws org.netbeans.modules.mercurial.HgException
1757: */
1758: public static void doCommit(File repository,
1759: List<File> commitFiles, String commitMessage,
1760: OutputLogger logger) throws HgException {
1761: List<String> command = new ArrayList<String>();
1762:
1763: command.add(getHgCommand());
1764: command.add(HG_COMMIT_CMD);
1765: command.add(HG_OPT_REPOSITORY);
1766: command.add(repository.getAbsolutePath());
1767:
1768: String projectUserName = new HgConfigFiles(repository)
1769: .getUserName(false);
1770: String globalUsername = HgConfigFiles.getInstance()
1771: .getUserName();
1772: String username = null;
1773: if (projectUserName != null && projectUserName.length() > 0)
1774: username = projectUserName;
1775: else if (globalUsername != null && globalUsername.length() > 0)
1776: username = globalUsername;
1777:
1778: if (username != null) {
1779: command.add(HG_OPT_USERNAME);
1780: command.add(username);
1781: }
1782:
1783: File tempfile = null;
1784:
1785: try {
1786: if (commitMessage == null || commitMessage.length() == 0) {
1787: commitMessage = HG_COMMIT_DEFAULT_MESSAGE;
1788: }
1789: // Create temporary file.
1790: tempfile = File.createTempFile(HG_COMMIT_TEMPNAME,
1791: HG_COMMIT_TEMPNAME_SUFFIX);
1792:
1793: // Write to temp file
1794: BufferedWriter out = new BufferedWriter(new FileWriter(
1795: tempfile));
1796: out.write(commitMessage);
1797: out.close();
1798:
1799: command.add(HG_COMMIT_OPT_LOGFILE_CMD);
1800: command.add(tempfile.getAbsolutePath());
1801:
1802: for (File f : commitFiles) {
1803: command.add(f.getAbsolutePath());
1804: }
1805: List<String> list = exec(command);
1806:
1807: if (!list.isEmpty()
1808: && (isErrorNotTracked(list.get(0)) || isErrorCannotReadCommitMsg(list
1809: .get(0))))
1810: handleError(command, list, NbBundle.getMessage(
1811: HgCommand.class, "MSG_COMMIT_FAILED"), logger);
1812:
1813: } catch (IOException ex) {
1814: throw new HgException(NbBundle.getMessage(HgCommand.class,
1815: "MSG_FAILED_TO_READ_COMMIT_MESSAGE"));
1816: } finally {
1817: if (commitMessage != null && tempfile != null) {
1818: tempfile.delete();
1819: }
1820: }
1821: }
1822:
1823: /**
1824: * Rename a source file to a destination file.
1825: * mercurial hg rename
1826: *
1827: * @param File repository of the mercurial repository's root directory
1828: * @param File of sourceFile which was renamed
1829: * @param File of destFile to which sourceFile has been renaned
1830: * @param boolean whether to do a rename --after
1831: * @return void
1832: * @throws org.netbeans.modules.mercurial.HgException
1833: */
1834: public static void doRename(File repository, File sourceFile,
1835: File destFile, OutputLogger logger) throws HgException {
1836: doRename(repository, sourceFile, destFile, false, logger);
1837: }
1838:
1839: private static void doRename(File repository, File sourceFile,
1840: File destFile, boolean bAfter, OutputLogger logger)
1841: throws HgException {
1842: if (repository == null)
1843: return;
1844:
1845: List<String> command = new ArrayList<String>();
1846:
1847: command.add(getHgCommand());
1848: command.add(HG_RENAME_CMD);
1849: if (bAfter)
1850: command.add(HG_RENAME_AFTER_CMD);
1851: command.add(HG_OPT_REPOSITORY);
1852: command.add(repository.getAbsolutePath());
1853: command.add(HG_OPT_CWD_CMD);
1854: command.add(repository.getAbsolutePath());
1855:
1856: command.add(sourceFile.getAbsolutePath().substring(
1857: repository.getAbsolutePath().length() + 1));
1858: command.add(destFile.getAbsolutePath().substring(
1859: repository.getAbsolutePath().length() + 1));
1860:
1861: List<String> list = exec(command);
1862: if (!list.isEmpty() && isErrorAbort(list.get(list.size() - 1))) {
1863: if (!bAfter
1864: || !isErrorAbortNoFilesToCopy(list
1865: .get(list.size() - 1))) {
1866: handleError(command, list, NbBundle.getMessage(
1867: HgCommand.class, "MSG_RENAME_FAILED"), logger);
1868: }
1869: }
1870: }
1871:
1872: /**
1873: * Mark a source file as having been renamed to a destination file.
1874: * mercurial hg rename -A.
1875: *
1876: * @param File repository of the mercurial repository's root directory
1877: * @param File of sourceFile which was renamed
1878: * @param File of destFile to which sourceFile has been renaned
1879: * @return void
1880: * @throws org.netbeans.modules.mercurial.HgException
1881: */
1882: public static void doRenameAfter(File repository, File sourceFile,
1883: File destFile, OutputLogger logger) throws HgException {
1884: doRename(repository, sourceFile, destFile, true, logger);
1885: }
1886:
1887: /**
1888: * Adds the list of Locally New files to the mercurial Repository
1889: * Their status will change to added and they will be added on the next
1890: * mercurial hg add.
1891: *
1892: * @param File repository of the mercurial repository's root directory
1893: * @param List<Files> of files to be added to hg
1894: * @return void
1895: * @throws org.netbeans.modules.mercurial.HgException
1896: */
1897: public static void doAdd(File repository, List<File> addFiles,
1898: OutputLogger logger) throws HgException {
1899: if (repository == null)
1900: return;
1901: if (addFiles.size() == 0)
1902: return;
1903: List<String> command = new ArrayList<String>();
1904:
1905: command.add(getHgCommand());
1906: command.add(HG_ADD_CMD);
1907: command.add(HG_OPT_REPOSITORY);
1908: command.add(repository.getAbsolutePath());
1909:
1910: for (File f : addFiles) {
1911: if (f.isDirectory())
1912: continue;
1913: // We do not look for files to ignore as we should not here
1914: // with a file to be ignored.
1915: command.add(f.getAbsolutePath());
1916: }
1917: List<String> list = exec(command);
1918: if (!list.isEmpty() && isErrorAlreadyTracked(list.get(0)))
1919: handleError(command, list, NbBundle.getMessage(
1920: HgCommand.class, "MSG_ALREADY_TRACKED"), logger);
1921: }
1922:
1923: /**
1924: * Reverts the list of files in the mercurial Repository to the specified revision
1925: *
1926: * @param File repository of the mercurial repository's root directory
1927: * @param List<Files> of files to be reverted
1928: * @param String revision to be reverted to
1929: * @return void
1930: * @throws org.netbeans.modules.mercurial.HgException
1931: */
1932: public static void doRevert(File repository,
1933: List<File> revertFiles, String revision, boolean doBackup,
1934: OutputLogger logger) throws HgException {
1935: if (repository == null)
1936: return;
1937: if (revertFiles.size() == 0)
1938: return;
1939:
1940: List<String> command = new ArrayList<String>();
1941:
1942: command.add(getHgCommand());
1943: command.add(HG_REVERT_CMD);
1944: if (!doBackup) {
1945: command.add(HG_REVERT_NOBACKUP_CMD);
1946: }
1947: command.add(HG_OPT_REPOSITORY);
1948: command.add(repository.getAbsolutePath());
1949: if (revision != null) {
1950: command.add(HG_FLAG_REV_CMD);
1951: command.add(revision);
1952: }
1953:
1954: for (File f : revertFiles) {
1955: command.add(f.getAbsolutePath());
1956: }
1957: List<String> list = exec(command);
1958: if (!list.isEmpty() && isErrorNoChangeNeeded(list.get(0)))
1959: handleError(command, list, NbBundle.getMessage(
1960: HgCommand.class, "MSG_REVERT_FAILED"), logger);
1961: }
1962:
1963: /**
1964: * Adds a Locally New file to the mercurial Repository
1965: * The status will change to added and they will be added on the next
1966: * mercurial hg commit.
1967: *
1968: * @param File repository of the mercurial repository's root directory
1969: * @param File of file to be added to hg
1970: * @return void
1971: * @throws org.netbeans.modules.mercurial.HgException
1972: */
1973: public static void doAdd(File repository, File file,
1974: OutputLogger logger) throws HgException {
1975: if (repository == null)
1976: return;
1977: if (file == null)
1978: return;
1979: if (file.isDirectory())
1980: return;
1981: // We do not look for file to ignore as we should not here
1982: // with a file to be ignored.
1983:
1984: List<String> command = new ArrayList<String>();
1985:
1986: command.add(getHgCommand());
1987: command.add(HG_ADD_CMD);
1988: command.add(HG_OPT_REPOSITORY);
1989: command.add(repository.getAbsolutePath());
1990:
1991: command.add(file.getAbsolutePath());
1992: List<String> list = exec(command);
1993: if (!list.isEmpty() && isErrorAlreadyTracked(list.get(0)))
1994: handleError(command, list, NbBundle.getMessage(
1995: HgCommand.class, "MSG_ALREADY_TRACKED"), logger);
1996: }
1997:
1998: /**
1999: * Get the annotations for the specified file
2000: *
2001: * @param File repository of the mercurial repository
2002: * @param File file to be annotated
2003: * @param String revision of the file to be annotated
2004: * @return List<String> list of the annotated lines of the file
2005: * @throws org.netbeans.modules.mercurial.HgException
2006: */
2007: public static List<String> doAnnotate(File repository, File file,
2008: String revision, OutputLogger logger) throws HgException {
2009: if (repository == null)
2010: return null;
2011: List<String> command = new ArrayList<String>();
2012:
2013: command.add(getHgCommand());
2014: command.add(HG_ANNOTATE_CMD);
2015: command.add(HG_OPT_REPOSITORY);
2016: command.add(repository.getAbsolutePath());
2017:
2018: if (revision != null) {
2019: command.add(HG_FLAG_REV_CMD);
2020: command.add(revision);
2021: }
2022: command.add(HG_ANNOTATE_FLAGN_CMD);
2023: command.add(HG_ANNOTATE_FLAGU_CMD);
2024: command.add(HG_OPT_FOLLOW);
2025: command.add(file.getAbsolutePath());
2026: List<String> list = exec(command);
2027: if (!list.isEmpty()) {
2028: if (isErrorNoRepository(list.get(0))) {
2029: handleError(command, list, NbBundle.getMessage(
2030: HgCommand.class, "MSG_NO_REPOSITORY_ERR"),
2031: logger);
2032: } else if (isErrorNoSuchFile(list.get(0))) {
2033: // This can happen if we have multiple heads and the wrong
2034: // one was picked by default hg annotation
2035: if (revision == null) {
2036: String rev = getLastRevision(repository, file);
2037: if (rev != null) {
2038: list = doAnnotate(repository, file, rev, logger);
2039: } else {
2040: list = null;
2041: }
2042: } else {
2043: list = null;
2044: }
2045: }
2046: }
2047: return list;
2048: }
2049:
2050: public static List<String> doAnnotate(File repository, File file,
2051: OutputLogger logger) throws HgException {
2052: return doAnnotate(repository, file, null, logger);
2053: }
2054:
2055: /**
2056: * Get the revisions this file has been modified in.
2057: *
2058: * @param File repository of the mercurial repository's root directory
2059: * @param files to query revisions for
2060: * @param Int limit on nunmber of revisions (-1 for no limit)
2061: * @return List<String> list of the revisions of the file - {<rev>:<short cset hash>}
2062: * or null if no commits made yet.
2063: */
2064: public static List<String> getRevisionsForFile(File repository,
2065: File[] files, int limit) {
2066: if (repository == null)
2067: return null;
2068: List<String> command = new ArrayList<String>();
2069:
2070: command.add(getHgCommand());
2071: command.add(HG_LOG_CMD);
2072: if (limit >= 0) {
2073: command.add(HG_LOG_LIMIT_CMD);
2074: command.add(Integer.toString(limit));
2075: }
2076: command.add(HG_OPT_REPOSITORY);
2077: command.add(repository.getAbsolutePath());
2078: command.add(HG_CSET_TARGET_TEMPLATE_CMD);
2079: if (files != null) {
2080: for (File file : files) {
2081: command.add(file.getAbsolutePath());
2082: }
2083: }
2084:
2085: List<String> list = new ArrayList<String>();
2086: try {
2087: list = exec(command);
2088: } catch (HgException ex) {
2089: // Ignore Exception
2090: }
2091: return list == null || list.isEmpty() ? null : list;
2092: }
2093:
2094: /**
2095: * Get the revisions for a repository
2096: *
2097: * @param File repository of the mercurial repository's root directory
2098: * @return List<String> list of the revisions of the repository - {<rev>:<short cset hash>}
2099: * or null if no commits made yet.
2100: */
2101: public static List<String> getRevisions(File repository, int limit) {
2102: if (repository == null)
2103: return null;
2104: return getRevisionsForFile(repository, null, limit);
2105: }
2106:
2107: /**
2108: * Get the pull default for the specified repository, i.e. the default
2109: * destination for hg pull commmands.
2110: *
2111: * @param File repository of the mercurial repository's root directory
2112: * @return String for pull default
2113: */
2114: public static String getPullDefault(File repository) {
2115: return getPathDefault(repository, HG_PATH_DEFAULT_OPT);
2116: }
2117:
2118: /**
2119: * Get the push default for the specified repository, i.e. the default
2120: * destination for hg push commmands.
2121: *
2122: * @param File repository of the mercurial repository's root directory
2123: * @return String for push default
2124: */
2125: public static String getPushDefault(File repository) {
2126: return getPathDefault(repository, HG_PATH_DEFAULT_PUSH_OPT);
2127: }
2128:
2129: private static String getPathDefault(File repository, String type) {
2130: if (repository == null)
2131: return null;
2132: List<String> command = new ArrayList<String>();
2133:
2134: command.add(getHgCommand());
2135: command.add(HG_PATH_DEFAULT_CMD);
2136: command.add(HG_OPT_REPOSITORY);
2137: command.add(repository.getAbsolutePath());
2138: command.add(type);
2139:
2140: String res = null;
2141:
2142: List<String> list = new LinkedList<String>();
2143: try {
2144: list = exec(command);
2145: } catch (HgException ex) {
2146: // Ignore Exception
2147: }
2148: if (!list.isEmpty() && (!isErrorNotFound(list.get(0)))) {
2149: res = list.get(0);
2150: }
2151: return res;
2152: }
2153:
2154: /**
2155: * Returns the mercurial branch name if any for a repository
2156: *
2157: * @param File repository of the mercurial repository's root directory
2158: * @return String branch name or null if not named
2159: * @throws org.netbeans.modules.mercurial.HgException
2160: */
2161: public static String getBranchName(File repository)
2162: throws HgException {
2163: if (repository == null)
2164: return null;
2165:
2166: List<String> command = new ArrayList<String>();
2167:
2168: command.add(getHgCommand());
2169: command.add(HG_BRANCH_CMD);
2170: command.add(HG_OPT_REPOSITORY);
2171: command.add(repository.getAbsolutePath());
2172:
2173: List<String> list = exec(command);
2174: if (!list.isEmpty()) {
2175: return list.get(0);
2176: } else {
2177: return null;
2178: }
2179: }
2180:
2181: /**
2182: * Returns the mercurial branch revision for a repository
2183: *
2184: * @param File repository of the mercurial repository's root directory
2185: * @return int value of revision for repository tip
2186: * @throws org.netbeans.modules.mercurial.HgException
2187: */
2188: public static int getBranchRev(File repository) throws HgException {
2189: if (repository == null)
2190: return -1;
2191:
2192: List<String> command = new ArrayList<String>();
2193:
2194: command.add(getHgCommand());
2195: command.add(HG_BRANCH_REV_CMD);
2196: command.add(HG_BRANCH_REV_TEMPLATE_CMD);
2197: command.add(HG_OPT_REPOSITORY);
2198: command.add(repository.getAbsolutePath());
2199:
2200: List<String> list = exec(command);
2201: if (!list.isEmpty()) {
2202: return Integer.parseInt(list.get(0));
2203: } else {
2204: return -1;
2205: }
2206: }
2207:
2208: /**
2209: * Returns the mercurial branch name if any for a repository
2210: *
2211: * @param File repository of the mercurial repository's root directory
2212: * @return String branch short change set hash
2213: * @throws org.netbeans.modules.mercurial.HgException
2214: */
2215: public static String getBranchShortChangesetHash(File repository)
2216: throws HgException {
2217: if (repository == null)
2218: return null;
2219:
2220: List<String> command = new ArrayList<String>();
2221:
2222: command.add(getHgCommand());
2223: command.add(HG_BRANCH_REV_CMD);
2224: command.add(HG_BRANCH_SHORT_CS_TEMPLATE_CMD);
2225: command.add(HG_OPT_REPOSITORY);
2226: command.add(repository.getAbsolutePath());
2227:
2228: List<String> list = exec(command);
2229: if (!list.isEmpty()) {
2230: return list.get(0);
2231: } else {
2232: return null;
2233: }
2234: }
2235:
2236: /**
2237: * Returns the mercurial branch info for a repository
2238: *
2239: * @param File repository of the mercurial repository's root directory
2240: * @return String of form :<branch>:<rev>:<shortchangeset>:
2241: * @throws org.netbeans.modules.mercurial.HgException
2242: */
2243: public static String getBranchInfo(File repository)
2244: throws HgException {
2245: if (repository == null)
2246: return null;
2247:
2248: List<String> command = new ArrayList<String>();
2249:
2250: command.add(getHgCommand());
2251: command.add(HG_BRANCH_REV_CMD);
2252: command.add(HG_BRANCH_INFO_TEMPLATE_CMD);
2253: command.add(HG_OPT_REPOSITORY);
2254: command.add(repository.getAbsolutePath());
2255:
2256: List<String> list = exec(command);
2257: if (!list.isEmpty()) {
2258: return list.get(0);
2259: } else {
2260: return null;
2261: }
2262: }
2263:
2264: /**
2265: * Returns the revision number for the heads in a repository
2266: *
2267: * @param File repository of the mercurial repository's root directory
2268: * @return List<String> of revision numbers.
2269: * @throws org.netbeans.modules.mercurial.HgException
2270: */
2271: public static List<String> getHeadRevisions(File repository)
2272: throws HgException {
2273: return getHeadInfo(repository, HG_REV_TEMPLATE_CMD);
2274: }
2275:
2276: /**
2277: * Returns the revision number for the heads in a repository
2278: *
2279: * @param String repository of the mercurial repository
2280: * @return List<String> of revision numbers.
2281: * @throws org.netbeans.modules.mercurial.HgException
2282: */
2283: public static List<String> getHeadRevisions(String repository)
2284: throws HgException {
2285: return getHeadInfo(repository, HG_REV_TEMPLATE_CMD);
2286: }
2287:
2288: /**
2289: * Returns the changeset for the the heads in a repository
2290: *
2291: * @param File repository of the mercurial repository's root directory
2292: * @param File file of the file whose last changeset is to be returned.
2293: * @return List<String> of changeset ids.
2294: * @throws org.netbeans.modules.mercurial.HgException
2295: */
2296: public static List<String> getHeadChangeSetIds(File repository)
2297: throws HgException {
2298: return getHeadInfo(repository, HG_CSET_TARGET_TEMPLATE_CMD);
2299: }
2300:
2301: private static List<String> getHeadInfo(String repository,
2302: String template) throws HgException {
2303: if (repository == null)
2304: return null;
2305:
2306: List<String> command = new ArrayList<String>();
2307:
2308: command.add(getHgCommand());
2309: command.add(HG_HEADS_CMD);
2310: command.add(HG_OPT_REPOSITORY);
2311: command.add(repository);
2312: command.add(template);
2313:
2314: return exec(command);
2315: }
2316:
2317: private static List<String> getHeadInfo(File repository,
2318: String template) throws HgException {
2319: if (repository == null)
2320: return null;
2321: return getHeadInfo(repository.getAbsolutePath(), template);
2322: }
2323:
2324: /**
2325: * Returns the revision number for the last change to a file
2326: *
2327: * @param File repository of the mercurial repository's root directory
2328: * @param File file of the file whose last revision number is to be returned, if null test for repo
2329: * @return String in the form of a revision number.
2330: * @throws org.netbeans.modules.mercurial.HgException
2331: */
2332: public static String getLastRevision(File repository, File file)
2333: throws HgException {
2334: return getLastChange(repository, file, HG_REV_TEMPLATE_CMD);
2335: }
2336:
2337: /**
2338: * Returns the changeset for the last change to a file
2339: *
2340: * @param File repository of the mercurial repository's root directory
2341: * @param File file of the file whose last changeset is to be returned.
2342: * @return String in the form of a changeset id.
2343: * @throws org.netbeans.modules.mercurial.HgException
2344: */
2345: public static String getLastChangeSetId(File repository, File file)
2346: throws HgException {
2347: return getLastChange(repository, file, HG_CSET_TEMPLATE_CMD);
2348: }
2349:
2350: private static String getLastChange(File repository, File file,
2351: String template) throws HgException {
2352:
2353: if (repository == null)
2354: return null;
2355:
2356: List<String> command = new ArrayList<String>();
2357:
2358: command.add(getHgCommand());
2359: command.add(HG_LOG_CMD);
2360: command.add(HG_LOG_LIMIT_ONE_CMD);
2361: command.add(HG_OPT_REPOSITORY);
2362: command.add(repository.getAbsolutePath());
2363: command.add(template);
2364: if (file != null)
2365: command.add(file.getAbsolutePath());
2366:
2367: List<String> list = exec(command);
2368: if (!list.isEmpty()) {
2369: return new StringBuffer(list.get(0)).toString();
2370: } else {
2371: return null;
2372: }
2373: }
2374:
2375: /**
2376: * Returns the mercurial status for a given file
2377: *
2378: * @param File repository of the mercurial repository's root directory
2379: * @param cwd current working directory containing file to be checked
2380: * @param filename name of file whose status is to be checked
2381: * @return FileInformation for the given filename
2382: * @throws org.netbeans.modules.mercurial.HgException
2383: */
2384: public static FileInformation getSingleStatus(File repository,
2385: String cwd, String filename) throws HgException {
2386: FileInformation info = null;
2387: List<String> list = doSingleStatusCmd(repository, cwd, filename);
2388: if (list == null || list.isEmpty())
2389: return new FileInformation(FileInformation.STATUS_UNKNOWN,
2390: null, false);
2391:
2392: info = getFileInformationFromStatusLine(list.get(0));
2393: // Handles Copy status
2394: // Could save copy source in FileStatus but for now we don't need it.
2395: // FileStatus used in Fileinformation.java:getStatusText() and getShortStatusText() to check if
2396: // file is Locally Copied when it's status is Locally Added
2397: if (list.size() == 2) {
2398: if (list.get(1).length() > 0) {
2399: if (list.get(1).charAt(0) == ' ') {
2400:
2401: info = new FileInformation(
2402: FileInformation.STATUS_VERSIONED_ADDEDLOCALLY,
2403: new FileStatus(new File(new File(cwd),
2404: filename), true), false);
2405: Mercurial.LOG
2406: .log(
2407: Level.FINE,
2408: "getSingleStatus() - Copied: Locally Added {0}, Copy Source {1}", // NOI18N
2409: new Object[] { list.get(0),
2410: list.get(1) });
2411: }
2412: } else {
2413: Mercurial.LOG
2414: .log(
2415: Level.FINE,
2416: "getSingleStatus() - Second line empty: first line: {0}",
2417: list.get(0)); // NOI18N
2418: }
2419: }
2420:
2421: // Handle Conflict Status
2422: // TODO: remove this if Hg status supports Conflict marker
2423: if (existsConflictFile(cwd + File.separator + filename)) {
2424: info = new FileInformation(
2425: FileInformation.STATUS_VERSIONED_CONFLICT, null,
2426: false);
2427: Mercurial.LOG
2428: .log(
2429: Level.FINE,
2430: "getSingleStatus(): CONFLICT StatusLine: {0} Status: {1} {2} RepoPath:{3} cwd:{4} CONFLICT {5}", // NOI18N
2431: new Object[] {
2432: list.get(0),
2433: info.getStatus(),
2434: filename,
2435: repository.getAbsolutePath(),
2436: cwd,
2437: cwd
2438: + File.separator
2439: + filename
2440: + HgCommand.HG_STR_CONFLICT_EXT });
2441: }
2442:
2443: Mercurial.LOG
2444: .log(
2445: Level.FINE,
2446: "getSingleStatus(): StatusLine: {0} Status: {1} {2} RepoPath:{3} cwd:{4}", // NOI18N
2447: new Object[] { list.get(0), info.getStatus(),
2448: filename, repository.getAbsolutePath(),
2449: cwd });
2450: return info;
2451: }
2452:
2453: /**
2454: * Returns the mercurial status for all files in a given subdirectory of
2455: * a repository
2456: *
2457: * @param File repository of the mercurial repository's root directory
2458: * @param File dir of the subdirectoy of interest.
2459: * @return Map of files and status for all files in the specified subdirectory
2460: * @throws org.netbeans.modules.mercurial.HgException
2461: */
2462: public static Map<File, FileInformation> getAllStatus(
2463: File repository, File dir) throws HgException {
2464: return getDirStatusWithFlags(repository, dir,
2465: HG_STATUS_FLAG_ALL_CMD, true);
2466: }
2467:
2468: /**
2469: * Returns the mercurial status for all files in a given repository
2470: *
2471: * @param File repository of the mercurial repository's root directory
2472: * @return Map of files and status for all files under the repository root
2473: * @throws org.netbeans.modules.mercurial.HgException
2474: */
2475: public static Map<File, FileInformation> getAllStatus(
2476: File repository) throws HgException {
2477: return getAllStatusWithFlags(repository,
2478: HG_STATUS_FLAG_ALL_CMD, true);
2479: }
2480:
2481: /**
2482: * Returns the mercurial status for only files of interest to us in a given repository
2483: * that is modified, locally added, locally removed, locally deleted, locally new and ignored.
2484: *
2485: * @param File repository of the mercurial repository's root directory
2486: * @return Map of files and status for all files of interest under the repository root
2487: * @throws org.netbeans.modules.mercurial.HgException
2488: */
2489: public static Map<File, FileInformation> getAllInterestingStatus(
2490: File repository) throws HgException {
2491: return getAllStatusWithFlags(repository,
2492: HG_STATUS_FLAG_INTERESTING_CMD, true);
2493: }
2494:
2495: /**
2496: * Returns the mercurial status for only files of interest to us in a given directory in a repository
2497: * that is modified, locally added, locally removed, locally deleted, locally new and ignored.
2498: *
2499: * @param File repository of the mercurial repository's root directory
2500: * @param File dir of the directory of interest
2501: * @return Map of files and status for all files of interest in the directory of interest
2502: * @throws org.netbeans.modules.mercurial.HgException
2503: */
2504: public static Map<File, FileInformation> getInterestingStatus(
2505: File repository, File dir) throws HgException {
2506: return getDirStatusWithFlags(repository, dir,
2507: HG_STATUS_FLAG_INTERESTING_CMD, true);
2508: }
2509:
2510: /**
2511: * Returns the mercurial status for only files of interest to us in a given repository
2512: * that is modified, locally added, locally removed, locally deleted, locally new and ignored.
2513: *
2514: * @param File repository of the mercurial repository's root directory
2515: * @return Map of files and status for all files of interest under the repository root
2516: * @throws org.netbeans.modules.mercurial.HgException
2517: */
2518: public static Map<File, FileInformation> getAllRemovedDeletedStatus(
2519: File repository) throws HgException {
2520: return getAllStatusWithFlags(repository,
2521: HG_STATUS_FLAG_REM_DEL_CMD, true);
2522: }
2523:
2524: /**
2525: * Returns the mercurial status for only files of interest to us in a given directory in a repository
2526: * that is modified, locally added, locally removed, locally deleted, locally new and ignored.
2527: *
2528: * @param File repository of the mercurial repository's root directory
2529: * @param File dir of the directory of interest
2530: * @return Map of files and status for all files of interest in the specified directory
2531: * @throws org.netbeans.modules.mercurial.HgException
2532: */
2533: public static Map<File, FileInformation> getRemovedDeletedStatus(
2534: File repository, File dir) throws HgException {
2535: return getDirStatusWithFlags(repository, dir,
2536: HG_STATUS_FLAG_REM_DEL_CMD, true);
2537: }
2538:
2539: /**
2540: * Returns the unknown files in a specified directory under a mercurial repository root
2541: *
2542: * @param File of the mercurial repository's root directory
2543: * @param File of the directory whose files are required
2544: * @return Map of files and status for all files under the repository root
2545: * @throws org.netbeans.modules.mercurial.HgException
2546: */
2547: public static Map<File, FileInformation> getUnknownStatus(
2548: File repository, File dir) throws HgException {
2549: Map<File, FileInformation> files = getDirStatusWithFlags(
2550: repository, dir, HG_STATUS_FLAG_UNKNOWN_CMD, false);
2551: int share = SharabilityQuery
2552: .getSharability(dir == null ? repository : dir);
2553: for (Iterator i = files.keySet().iterator(); i.hasNext();) {
2554: File file = (File) i.next();
2555: if ((share == SharabilityQuery.MIXED && SharabilityQuery
2556: .getSharability(file) == SharabilityQuery.NOT_SHARABLE)
2557: || (share == SharabilityQuery.NOT_SHARABLE)) {
2558: i.remove();
2559: }
2560: }
2561: return files;
2562: }
2563:
2564: /**
2565: * Returns the unknown files under a mercurial repository root
2566: *
2567: * @param File repository of the mercurial repository's root directory
2568: * @return Map of files and status for all files under the repository root
2569: * @throws org.netbeans.modules.mercurial.HgException
2570: */
2571: public static Map<File, FileInformation> getAllUnknownStatus(
2572: File repository) throws HgException {
2573: return getUnknownStatus(repository, null);
2574: }
2575:
2576: /**
2577: * Remove the specified file from the mercurial Repository
2578: *
2579: * @param File repository of the mercurial repository's root directory
2580: * @param List<Files> of files to be added to hg
2581: * @param f path to be removed from the repository
2582: * @throws org.netbeans.modules.mercurial.HgException
2583: */
2584: public static void doRemove(File repository,
2585: List<File> removeFiles, OutputLogger logger)
2586: throws HgException {
2587: List<String> command = new ArrayList<String>();
2588:
2589: command.add(getHgCommand());
2590: command.add(HG_REMOVE_CMD);
2591: command.add(HG_OPT_REPOSITORY);
2592: command.add(repository.getAbsolutePath());
2593: command.add(HG_REMOVE_FLAG_FORCE_CMD);
2594: for (File f : removeFiles) {
2595: command.add(f.getAbsolutePath());
2596: }
2597:
2598: List<String> list = exec(command);
2599: if (!list.isEmpty() && isErrorAlreadyTracked(list.get(0)))
2600: handleError(command, list, NbBundle.getMessage(
2601: HgCommand.class, "MSG_ALREADY_TRACKED"), logger);
2602: }
2603:
2604: /**
2605: * Remove the specified files from the mercurial Repository
2606: *
2607: * @param File repository of the mercurial repository's root directory
2608: * @param f path to be removed from the repository
2609: * @throws org.netbeans.modules.mercurial.HgException
2610: */
2611: public static void doRemove(File repository, File f,
2612: OutputLogger logger) throws HgException {
2613: List<String> command = new ArrayList<String>();
2614:
2615: command.add(getHgCommand());
2616: command.add(HG_REMOVE_CMD);
2617: command.add(HG_OPT_REPOSITORY);
2618: command.add(repository.getAbsolutePath());
2619: command.add(HG_REMOVE_FLAG_FORCE_CMD);
2620: command.add(f.getAbsolutePath());
2621:
2622: List<String> list = exec(command);
2623: if (!list.isEmpty() && isErrorAlreadyTracked(list.get(0)))
2624: handleError(command, list, NbBundle.getMessage(
2625: HgCommand.class, "MSG_ALREADY_TRACKED"), logger);
2626: }
2627:
2628: /**
2629: * Export the diffs for the specified revision to the specified output file
2630: /**
2631: * Export the diffs for the specified revision to the specified output file
2632: *
2633: * @param File repository of the mercurial repository's root directory
2634: * @param revStr the revision whose diffs are to be exported
2635: * @param outputFileName path of the output file
2636: * @throws org.netbeans.modules.mercurial.HgException
2637: */
2638: public static List<String> doExport(File repository, String revStr,
2639: String outputFileName, OutputLogger logger)
2640: throws HgException {
2641: // Ensure that parent directory of target exists, creating if necessary
2642: File fileTarget = new File(outputFileName);
2643: File parentTarget = fileTarget.getParentFile();
2644: try {
2645: if (!parentTarget.mkdir()) {
2646: if (!parentTarget.isDirectory()) {
2647: Mercurial.LOG.log(Level.WARNING,
2648: "File.mkdir() failed for : "
2649: + parentTarget.getAbsolutePath()); // NOI18N
2650: throw (new HgException(NbBundle.getMessage(
2651: HgCommand.class,
2652: "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
2653: }
2654: }
2655: } catch (SecurityException e) {
2656: Mercurial.LOG.log(Level.WARNING, "File.mkdir() for : "
2657: + parentTarget.getAbsolutePath()
2658: + " threw SecurityException " + e.getMessage()); // NOI18N
2659: throw (new HgException(NbBundle.getMessage(HgCommand.class,
2660: "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
2661: }
2662: List<String> command = new ArrayList<String>();
2663:
2664: command.add(getHgCommand());
2665: command.add(HG_EXPORT_CMD);
2666: command.add(HG_VERBOSE_CMD);
2667: command.add(HG_OPT_REPOSITORY);
2668: command.add(repository.getAbsolutePath());
2669: command.add(HG_FLAG_OUTPUT_CMD);
2670: command.add(outputFileName);
2671: command.add(revStr);
2672:
2673: List<String> list = exec(command);
2674: if (!list.isEmpty() && isErrorAbort(list.get(list.size() - 1))) {
2675: handleError(command, list, NbBundle.getMessage(
2676: HgCommand.class, "MSG_EXPORT_FAILED"), logger);
2677: }
2678: return list;
2679: }
2680:
2681: /**
2682: * Export the diffs for the specified revision to the specified output file
2683: /**
2684: * Export the diffs for the specified revision to the specified output file
2685: *
2686: * @param File repository of the mercurial repository's root directory
2687: * @param revStr the revision whose diffs are to be exported
2688: * @param outputFileName path of the output file
2689: * @throws org.netbeans.modules.mercurial.HgException
2690: */
2691: public static List<String> doExportFileDiff(File repository,
2692: File file, String revStr, String outputFileName,
2693: OutputLogger logger) throws HgException {
2694: // Ensure that parent directory of target exists, creating if necessary
2695: File fileTarget = new File(outputFileName);
2696: File parentTarget = fileTarget.getParentFile();
2697: try {
2698: if (!parentTarget.mkdir()) {
2699: if (!parentTarget.isDirectory()) {
2700: Mercurial.LOG.log(Level.WARNING,
2701: "File.mkdir() failed for : "
2702: + parentTarget.getAbsolutePath()); // NOI18N
2703: throw (new HgException(NbBundle.getMessage(
2704: HgCommand.class,
2705: "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
2706: }
2707: }
2708: } catch (SecurityException e) {
2709: Mercurial.LOG.log(Level.WARNING, "File.mkdir() for : "
2710: + parentTarget.getAbsolutePath()
2711: + " threw SecurityException " + e.getMessage()); // NOI18N
2712: throw (new HgException(NbBundle.getMessage(HgCommand.class,
2713: "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
2714: }
2715: List<String> command = new ArrayList<String>();
2716:
2717: command.add(getHgCommand());
2718: command.add(HG_LOG_CMD);
2719: command.add(HG_OPT_REPOSITORY);
2720: command.add(repository.getAbsolutePath());
2721: command.add(HG_REV_CMD);
2722: command.add(revStr);
2723: command.add(HG_LOG_TEMPLATE_EXPORT_FILE_CMD);
2724: command.add(HG_LOG_PATCH_CMD);
2725: command.add(file.getAbsolutePath());
2726:
2727: List<String> list = exec(command);
2728: if (!list.isEmpty() && isErrorAbort(list.get(list.size() - 1))) {
2729: handleError(command, list, NbBundle.getMessage(
2730: HgCommand.class, "MSG_EXPORT_FAILED"), logger);
2731: } else {
2732: writeOutputFileDiff(list, outputFileName);
2733: }
2734: return list;
2735: }
2736:
2737: private static void writeOutputFileDiff(List<String> list,
2738: String outputFileName) {
2739: PrintWriter pw = null;
2740: try {
2741: pw = new PrintWriter(new FileWriter(outputFileName));
2742: for (String s : list) {
2743: pw.println(s);
2744: pw.flush();
2745: }
2746: } catch (IOException ex) {
2747: // Ignore
2748: } finally {
2749: if (pw != null)
2750: pw.close();
2751: }
2752: }
2753:
2754: /**
2755: * Imports the diffs from the specified file
2756: *
2757: * @param File repository of the mercurial repository's root directory
2758: * @param File patchFile of the patch file
2759: * @throws org.netbeans.modules.mercurial.HgException
2760: */
2761: public static List<String> doImport(File repository,
2762: File patchFile, OutputLogger logger) throws HgException {
2763: List<String> command = new ArrayList<String>();
2764:
2765: command.add(getHgCommand());
2766: command.add(HG_IMPORT_CMD);
2767: command.add(HG_VERBOSE_CMD);
2768: command.add(HG_OPT_REPOSITORY);
2769: command.add(repository.getAbsolutePath());
2770: command.add(HG_OPT_CWD_CMD);
2771: command.add(repository.getAbsolutePath());
2772: command.add(patchFile.getAbsolutePath());
2773:
2774: List<String> list = exec(command);
2775: if (!list.isEmpty() && isErrorAbort(list.get(list.size() - 1))) {
2776: logger.output(list); // need the failure info from import
2777: handleError(command, list, NbBundle.getMessage(
2778: HgCommand.class, "MSG_IMPORT_FAILED"), logger);
2779: }
2780: return list;
2781: }
2782:
2783: /**
2784: * Returns Map of mercurial file and status for files in a given repository as specified by the status flags
2785: */
2786: private static Map<File, FileInformation> getAllStatusWithFlags(
2787: File repository, String statusFlags,
2788: boolean bIgnoreUnversioned) throws HgException {
2789: return getDirStatusWithFlags(repository, null, statusFlags,
2790: bIgnoreUnversioned);
2791: }
2792:
2793: private static Map<File, FileInformation> getDirStatusWithFlags(
2794: File repository, File dir, String statusFlags,
2795: boolean bIgnoreUnversioned) throws HgException {
2796: if (repository == null)
2797: return null;
2798:
2799: List<FileStatus> statusList = new ArrayList<FileStatus>();
2800: FileInformation prev_info = null;
2801: List<String> list = doRepositoryDirStatusCmd(repository, dir,
2802: statusFlags);
2803:
2804: Map<File, FileInformation> repositoryFiles = new HashMap<File, FileInformation>(
2805: list.size());
2806:
2807: StringBuffer filePath = null;
2808: for (String statusLine : list) {
2809: FileInformation info = getFileInformationFromStatusLine(statusLine);
2810: Mercurial.LOG
2811: .log(
2812: Level.FINE,
2813: "getDirStatusWithFlags(): status line {0} info {1}",
2814: new Object[] { statusLine, info }); // NOI18N
2815: if (statusLine.length() > 0) {
2816: if (statusLine.charAt(0) == ' ') {
2817: // Locally Added but Copied
2818: if (filePath != null) {
2819: prev_info = new FileInformation(
2820: FileInformation.STATUS_VERSIONED_ADDEDLOCALLY,
2821: new FileStatus(new File(filePath
2822: .toString()), true), false);
2823: Mercurial.LOG
2824: .log(
2825: Level.FINE,
2826: "getDirStatusWithFlags(): prev_info {0} filePath {1}",
2827: new Object[] { prev_info,
2828: filePath.toString() }); // NOI18N
2829: } else {
2830: Mercurial.LOG
2831: .log(
2832: Level.FINE,
2833: "getDirStatusWithFlags(): repository path: {0} status flags: {1} status line {2} filepath == nullfor prev_info ",
2834: new Object[] {
2835: repository
2836: .getAbsolutePath(),
2837: statusFlags, statusLine }); // NOI18N
2838: }
2839: break;
2840: } else {
2841: if (filePath != null) {
2842: repositoryFiles.put(new File(filePath
2843: .toString()), prev_info);
2844: }
2845: }
2846: }
2847: if (bIgnoreUnversioned) {
2848: if (info.getStatus() == FileInformation.STATUS_NOTVERSIONED_NOTMANAGED
2849: || info.getStatus() == FileInformation.STATUS_UNKNOWN)
2850: continue;
2851: } else {
2852: if (info.getStatus() == FileInformation.STATUS_UNKNOWN)
2853: continue;
2854: }
2855: filePath = new StringBuffer(repository.getAbsolutePath())
2856: .append(File.separatorChar);
2857: StringBuffer sb = new StringBuffer(statusLine);
2858: sb.delete(0, 2); // Strip status char and following 2 spaces: [MARC\?\!I][ ][ ]
2859: filePath.append(sb.toString());
2860:
2861: // Handle Conflict Status
2862: // TODO: remove this if Hg status supports Conflict marker
2863: if (existsConflictFile(filePath.toString())) {
2864: info = new FileInformation(
2865: FileInformation.STATUS_VERSIONED_CONFLICT,
2866: null, false);
2867: Mercurial.LOG
2868: .log(
2869: Level.FINE,
2870: "getDirStatusWithFlags(): CONFLICT repository path: {0} status flags: {1} status line {2} CONFLICT {3}",
2871: new Object[] {
2872: repository.getAbsolutePath(),
2873: statusFlags,
2874: statusLine,
2875: filePath.toString()
2876: + HgCommand.HG_STR_CONFLICT_EXT }); // NOI18N
2877: }
2878: prev_info = info;
2879: }
2880: if (prev_info != null) {
2881: repositoryFiles.put(new File(filePath.toString()),
2882: prev_info);
2883: }
2884:
2885: if (list.size() < 10) {
2886: Mercurial.LOG
2887: .log(
2888: Level.FINE,
2889: "getDirStatusWithFlags(): repository path: {0} status flags: {1} status list {2}", // NOI18N
2890: new Object[] {
2891: repository.getAbsolutePath(),
2892: statusFlags, list });
2893: } else {
2894: Mercurial.LOG
2895: .log(
2896: Level.FINE,
2897: "getDirStatusWithFlags(): repository path: {0} status flags: {1} status list has {2} elements", // NOI18N
2898: new Object[] {
2899: repository.getAbsolutePath(),
2900: statusFlags, list.size() });
2901: }
2902: return repositoryFiles;
2903: }
2904:
2905: /**
2906: * Gets file information for a given hg status output status line
2907: */
2908: private static FileInformation getFileInformationFromStatusLine(
2909: String status) {
2910: FileInformation info = null;
2911: if (status == null || (status.length() == 0))
2912: return new FileInformation(
2913: FileInformation.STATUS_VERSIONED_UPTODATE, null,
2914: false);
2915:
2916: char c0 = status.charAt(0);
2917: char c1 = status.charAt(1);
2918: switch (c0 + c1) {
2919: case HG_STATUS_CODE_MODIFIED:
2920: info = new FileInformation(
2921: FileInformation.STATUS_VERSIONED_MODIFIEDLOCALLY,
2922: null, false);
2923: break;
2924: case HG_STATUS_CODE_ADDED:
2925: info = new FileInformation(
2926: FileInformation.STATUS_VERSIONED_ADDEDLOCALLY,
2927: null, false);
2928: break;
2929: case HG_STATUS_CODE_REMOVED:
2930: info = new FileInformation(
2931: FileInformation.STATUS_VERSIONED_REMOVEDLOCALLY,
2932: null, false);
2933: break;
2934: case HG_STATUS_CODE_CLEAN:
2935: info = new FileInformation(
2936: FileInformation.STATUS_VERSIONED_UPTODATE, null,
2937: false);
2938: break;
2939: case HG_STATUS_CODE_DELETED:
2940: info = new FileInformation(
2941: FileInformation.STATUS_VERSIONED_DELETEDLOCALLY,
2942: null, false);
2943: break;
2944: case HG_STATUS_CODE_IGNORED:
2945: info = new FileInformation(
2946: FileInformation.STATUS_NOTVERSIONED_EXCLUDED, null,
2947: false);
2948: break;
2949: case HG_STATUS_CODE_NOTTRACKED:
2950: info = new FileInformation(
2951: FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY,
2952: null, false);
2953: break;
2954: // Leave this here for whenever Hg status suports conflict markers
2955: case HG_STATUS_CODE_CONFLICT:
2956: info = new FileInformation(
2957: FileInformation.STATUS_VERSIONED_CONFLICT, null,
2958: false);
2959: break;
2960: case HG_STATUS_CODE_ABORT:
2961: info = new FileInformation(
2962: FileInformation.STATUS_NOTVERSIONED_NOTMANAGED,
2963: null, false);
2964: break;
2965: default:
2966: info = new FileInformation(FileInformation.STATUS_UNKNOWN,
2967: null, false);
2968: break;
2969: }
2970:
2971: return info;
2972: }
2973:
2974: /**
2975: * Gets hg status command output line for a given file
2976: */
2977: private static List<String> doSingleStatusCmd(File repository,
2978: String cwd, String filename) throws HgException {
2979: String statusLine = null;
2980:
2981: List<String> command = new ArrayList<String>();
2982:
2983: command.add(getHgCommand());
2984: command.add(HG_STATUS_CMD);
2985: command.add(HG_STATUS_FLAG_ALL_CMD);
2986: command.add(HG_OPT_REPOSITORY);
2987: command.add(repository.getAbsolutePath());
2988: command.add(HG_OPT_CWD_CMD);
2989: command.add(repository.getAbsolutePath());
2990:
2991: // In 0.9.3 hg status does not give back copy information unless we
2992: // use relative paths from repository. This is fixed in 0.9.4.
2993: // See http://www.selenic.com/mercurial/bts/issue545.
2994: command.add(new File(cwd, filename).getAbsolutePath()
2995: .substring(repository.getAbsolutePath().length() + 1));
2996:
2997: return exec(command);
2998: }
2999:
3000: /**
3001: * Gets hg status command output list for the specified status flags for a given repository and directory
3002: */
3003: private static List<String> doRepositoryDirStatusCmd(
3004: File repository, File dir, String statusFlags)
3005: throws HgException {
3006: List<String> command = new ArrayList<String>();
3007:
3008: command.add(getHgCommand());
3009: command.add(HG_STATUS_CMD);
3010:
3011: command.add(statusFlags);
3012: command.add(HG_OPT_REPOSITORY);
3013: command.add(repository.getAbsolutePath());
3014: command.add(HG_OPT_CWD_CMD);
3015: command.add(repository.getAbsolutePath());
3016: if (dir != null) {
3017: command.add(dir.getAbsolutePath());
3018: } else {
3019: command.add(repository.getAbsolutePath());
3020: }
3021:
3022: List<String> list = exec(command);
3023: if (!list.isEmpty() && isErrorNoRepository(list.get(0))) {
3024: OutputLogger logger = OutputLogger.getLogger(repository
3025: .getAbsolutePath());
3026: try {
3027: handleError(command, list, NbBundle.getMessage(
3028: HgCommand.class, "MSG_NO_REPOSITORY_ERR"),
3029: logger);
3030: } finally {
3031: logger.closeLog();
3032: }
3033: }
3034: return list;
3035: }
3036:
3037: /**
3038: * Returns the ouput from the given command
3039: *
3040: * @param command to execute
3041: * @return List of the command's output or an exception if one occured
3042: */
3043:
3044: private static List<String> execEnv(List<String> command,
3045: List<String> env) throws HgException {
3046: if (EventQueue.isDispatchThread()) {
3047: Mercurial.LOG
3048: .log(Level.FINE,
3049: "WARNING execEnv(): calling Hg command in AWT Thread - could stall UI"); // NOI18N
3050: }
3051: assert (command != null && command.size() > 0);
3052: List<String> list = new ArrayList<String>();
3053: BufferedReader input = null;
3054: Process proc = null;
3055: try {
3056: if (command.size() > 10) {
3057: List<String> smallCommand = new ArrayList<String>();
3058: int count = 0;
3059: for (Iterator i = command.iterator(); i.hasNext();) {
3060: smallCommand.add((String) i.next());
3061: if (count++ > 10)
3062: break;
3063: }
3064: Mercurial.LOG.log(Level.FINE, "execEnv(): "
3065: + smallCommand); // NOI18N
3066: } else {
3067: Mercurial.LOG.log(Level.FINE, "execEnv(): " + command); // NOI18N
3068: }
3069: if (env != null && env.size() > 0) {
3070: ProcessBuilder pb = new ProcessBuilder(command);
3071: Map<String, String> envOrig = pb.environment();
3072: for (String s : env) {
3073: envOrig.put(s.substring(0, s.indexOf('=')), s
3074: .substring(s.indexOf('=') + 1));
3075: }
3076: proc = pb.start();
3077: } else {
3078: proc = new ProcessBuilder(command).start();
3079: }
3080:
3081: input = new BufferedReader(new InputStreamReader(proc
3082: .getInputStream()));
3083:
3084: String line;
3085: while ((line = input.readLine()) != null) {
3086: list.add(line);
3087: }
3088: input.close();
3089: input = new BufferedReader(new InputStreamReader(proc
3090: .getErrorStream()));
3091: while ((line = input.readLine()) != null) {
3092: list.add(line);
3093: }
3094: input.close();
3095: input = null;
3096: try {
3097: proc.waitFor();
3098: // By convention we assume that 255 (or -1) is a serious error.
3099: // For instance, the command line could be too long.
3100: if (proc.exitValue() == 255) {
3101: Mercurial.LOG.log(Level.FINE,
3102: "execEnv(): process returned 255"); // NOI18N
3103: if (list.isEmpty()) {
3104: Mercurial.LOG.log(Level.SEVERE, "command: "
3105: + command); // NOI18N
3106: throw new HgException(NbBundle.getMessage(
3107: HgCommand.class,
3108: "MSG_UNABLE_EXECUTE_COMMAND"));
3109: }
3110: }
3111: } catch (InterruptedException e) {
3112: Mercurial.LOG.log(Level.FINE,
3113: "execEnv(): process interrupted " + e); // NOI18N
3114: }
3115: } catch (InterruptedIOException e) {
3116: // We get here is we try to cancel so kill the process
3117: Mercurial.LOG.log(Level.FINE,
3118: "execEnv(): execEnv(): InterruptedIOException "
3119: + e); // NOI18N
3120: if (proc != null) {
3121: try {
3122: proc.getInputStream().close();
3123: proc.getOutputStream().close();
3124: proc.getErrorStream().close();
3125: } catch (IOException ioex) {
3126: //Just ignore. Closing streams.
3127: }
3128: proc.destroy();
3129: }
3130: throw new HgException(NbBundle.getMessage(HgCommand.class,
3131: "MSG_COMMAND_CANCELLED"));
3132: } catch (IOException e) {
3133: // Hg does not seem to be returning error status != 0
3134: // even when it fails when for instance adding an already tracked file to
3135: // the repository - we will have to examine the output in the context of the
3136: // calling func and raise exceptions there if needed
3137: Mercurial.LOG.log(Level.SEVERE,
3138: "execEnv(): execEnv(): IOException " + e); // NOI18N
3139:
3140: // Handle low level Mercurial failures
3141: if (isErrorArgsTooLong(e.getMessage())) {
3142: assert (command.size() > 2);
3143: throw new HgException(NbBundle.getMessage(
3144: HgCommand.class, "MSG_ARG_LIST_TOO_LONG_ERR",
3145: command.get(1), command.size() - 2));
3146: } else if (isErrorNoHg(e.getMessage())
3147: || isErrorCannotRun(e.getMessage())) {
3148: throw new HgException(NbBundle.getMessage(
3149: Mercurial.class, "MSG_VERSION_NONE_MSG"));
3150: } else {
3151: throw new HgException(NbBundle.getMessage(
3152: HgCommand.class, "MSG_UNABLE_EXECUTE_COMMAND"));
3153: }
3154: } finally {
3155: if (input != null) {
3156: try {
3157: input.close();
3158: } catch (IOException ioex) {
3159: //Just ignore. Closing streams.
3160: }
3161: input = null;
3162: }
3163: }
3164: return list;
3165: }
3166:
3167: /**
3168: * Returns the ouput from the given command
3169: *
3170: * @param command to execute
3171: * @return List of the command's output or an exception if one occured
3172: */
3173: private static List<String> exec(List<String> command)
3174: throws HgException {
3175: if (!Mercurial.getInstance().isGoodVersion()) {
3176: return new ArrayList<String>();
3177: }
3178: return execEnv(command, null);
3179: }
3180:
3181: private static List<String> execForVersionCheck()
3182: throws HgException {
3183: List<String> command = new ArrayList<String>();
3184: command.add(getHgCommand());
3185: command.add(HG_VERSION_CMD);
3186:
3187: return execEnv(command, null);
3188: }
3189:
3190: private static String getHgCommand() {
3191: String defaultPath = HgModuleConfig.getDefault()
3192: .getExecutableBinaryPath();
3193: if (defaultPath == null || defaultPath.length() == 0)
3194: return HG_COMMAND;
3195: else
3196: return defaultPath + File.separatorChar + HG_COMMAND;
3197: }
3198:
3199: private static void handleError(List<String> command,
3200: List<String> list, String message, OutputLogger logger)
3201: throws HgException {
3202: if (command != null && list != null && logger != null) {
3203: Mercurial.LOG.log(Level.WARNING, "command: "
3204: + HgUtils.replaceHttpPassword(command)); // NOI18N
3205: Mercurial.LOG.log(Level.WARNING, "output: "
3206: + HgUtils.replaceHttpPassword(list)); // NOI18N
3207: logger.outputInRed(NbBundle.getMessage(HgCommand.class,
3208: "MSG_COMMAND_ERR")); // NOI18N
3209: logger.output(NbBundle.getMessage(HgCommand.class,
3210: "MSG_COMMAND_INFO_ERR", HgUtils
3211: .replaceHttpPassword(command), HgUtils
3212: .replaceHttpPassword(list))); // NOI18N
3213: }
3214:
3215: if (list != null
3216: && (isErrorPossibleProxyIssue(list.get(0)) || isErrorPossibleProxyIssue(list
3217: .get(list.size() - 1)))) {
3218: boolean bConfirmSetProxy;
3219: bConfirmSetProxy = HgUtils.confirmDialog(HgCommand.class,
3220: "MSG_POSSIBLE_PROXY_ISSUE_TITLE",
3221: "MSG_POSSIBLE_PROXY_ISSUE_QUERY"); // NOI18N
3222: if (bConfirmSetProxy) {
3223: OptionsDisplayer.getDefault().open("General"); // NOI18N
3224: }
3225: } else {
3226: throw new HgException(message);
3227: }
3228: }
3229:
3230: public static boolean isMergeNeededMsg(String msg) {
3231: return msg.indexOf(HG_MERGE_NEEDED_ERR) > -1; // NOI18N
3232: }
3233:
3234: public static boolean isBackoutMergeNeededMsg(String msg) {
3235: return msg.indexOf(HG_BACKOUT_MERGE_NEEDED_ERR) > -1; // NOI18N
3236: }
3237:
3238: public static boolean isMergeConflictMsg(String msg) {
3239: if (Utilities.isWindows()) {
3240: return (msg.indexOf(HG_MERGE_CONFLICT_WIN1_ERR) > -1) && // NOI18N
3241: (msg.indexOf(HG_MERGE_CONFLICT_WIN2_ERR) > -1); // NOI18N
3242: } else {
3243: return msg.indexOf(HG_MERGE_CONFLICT_ERR) > -1; // NOI18N
3244: }
3245: }
3246:
3247: public static boolean isMergeUnavailableMsg(String msg) {
3248: return msg.indexOf(HG_MERGE_UNAVAILABLE_ERR) > -1; // NOI18N
3249: }
3250:
3251: public static boolean isMergeAbortMultipleHeadsMsg(String msg) {
3252: return msg.indexOf(HG_MERGE_MULTIPLE_HEADS_ERR) > -1; // NOI18N
3253: }
3254:
3255: public static boolean isMergeAbortUncommittedMsg(String msg) {
3256: return msg.indexOf(HG_MERGE_UNCOMMITTED_ERR) > -1; // NOI18N
3257: }
3258:
3259: public static boolean isNoChanges(String msg) {
3260: return msg.indexOf(HG_NO_CHANGES_ERR) > -1; // NOI18N
3261: }
3262:
3263: private static boolean isErrorNoDefaultPush(String msg) {
3264: return msg.indexOf(HG_ABORT_NO_DEFAULT_PUSH_ERR) > -1; // NOI18N
3265: }
3266:
3267: private static boolean isErrorNoDefaultPath(String msg) {
3268: return msg.indexOf(HG_ABORT_NO_DEFAULT_ERR) > -1; // NOI18N
3269: }
3270:
3271: private static boolean isErrorPossibleProxyIssue(String msg) {
3272: return msg.indexOf(HG_ABORT_POSSIBLE_PROXY_ERR) > -1; // NOI18N
3273: }
3274:
3275: private static boolean isErrorNoRepository(String msg) {
3276: return msg.indexOf(HG_NO_REPOSITORY_ERR) > -1
3277: || msg.indexOf(HG_NOT_REPOSITORY_ERR) > -1
3278: || (msg.indexOf(HG_REPOSITORY) > -1 && msg
3279: .indexOf(HG_NOT_FOUND_ERR) > -1); // NOI18N
3280: }
3281:
3282: private static boolean isErrorNoHg(String msg) {
3283: return msg.indexOf(HG_NO_HG_CMD_FOUND_ERR) > -1; // NOI18N
3284: }
3285:
3286: private static boolean isErrorArgsTooLong(String msg) {
3287: return msg.indexOf(HG_ARG_LIST_TOO_LONG_ERR) > -1; // NOI18N
3288: }
3289:
3290: private static boolean isErrorCannotRun(String msg) {
3291: return msg.indexOf(HG_CANNOT_RUN_ERR) > -1; // NOI18N
3292: }
3293:
3294: private static boolean isErrorUpdateSpansBranches(String msg) {
3295: return msg.indexOf(HG_UPDATE_SPAN_BRANCHES_ERR) > -1; // NOI18N
3296: }
3297:
3298: private static boolean isErrorAlreadyTracked(String msg) {
3299: return msg.indexOf(HG_ALREADY_TRACKED_ERR) > -1; // NOI18N
3300: }
3301:
3302: private static boolean isErrorNotTracked(String msg) {
3303: return msg.indexOf(HG_NOT_TRACKED_ERR) > -1; // NOI18N
3304: }
3305:
3306: private static boolean isErrorNotFound(String msg) {
3307: return msg.indexOf(HG_NOT_FOUND_ERR) > -1; // NOI18N
3308: }
3309:
3310: private static boolean isErrorCannotReadCommitMsg(String msg) {
3311: return msg.indexOf(HG_CANNOT_READ_COMMIT_MESSAGE_ERR) > -1; // NOI18N
3312: }
3313:
3314: private static boolean isErrorAbort(String msg) {
3315: return msg.indexOf(HG_ABORT_ERR) > -1; // NOI18N
3316: }
3317:
3318: public static boolean isErrorAbortPush(String msg) {
3319: return msg.indexOf(HG_ABORT_PUSH_ERR) > -1; // NOI18N
3320: }
3321:
3322: public static boolean isErrorAbortNoFilesToCopy(String msg) {
3323: return msg.indexOf(HG_ABORT_NO_FILES_TO_COPY_ERR) > -1; // NOI18N
3324: }
3325:
3326: private static boolean isErrorNoChangeNeeded(String msg) {
3327: return msg.indexOf(HG_NO_CHANGE_NEEDED_ERR) > -1; // NOI18N
3328: }
3329:
3330: public static boolean isCreateNewBranch(String msg) {
3331: return msg.indexOf(HG_CREATE_NEW_BRANCH_ERR) > -1; // NOI18N
3332: }
3333:
3334: public static boolean isHeadsCreated(String msg) {
3335: return msg.indexOf(HG_HEADS_CREATED_ERR) > -1; // NOI18N
3336: }
3337:
3338: public static boolean isNoRollbackPossible(String msg) {
3339: return msg.indexOf(HG_NO_ROLLBACK_ERR) > -1; // NOI18N
3340: }
3341:
3342: public static boolean isNoRevStrip(String msg) {
3343: return msg.indexOf(HG_NO_REV_STRIP_ERR) > -1; // NOI18N
3344: }
3345:
3346: public static boolean isLocalChangesStrip(String msg) {
3347: return msg.indexOf(HG_LOCAL_CHANGES_STRIP_ERR) > -1; // NOI18N
3348: }
3349:
3350: public static boolean isMultipleHeadsStrip(String msg) {
3351: return msg.indexOf(HG_MULTIPLE_HEADS_STRIP_ERR) > -1; // NOI18N
3352: }
3353:
3354: public static boolean isUncommittedChangesBackout(String msg) {
3355: return msg.indexOf(HG_ABORT_UNCOMMITTED_CHANGES_ERR) > -1; // NOI18N
3356: }
3357:
3358: public static boolean isMergeChangesetBackout(String msg) {
3359: return msg.indexOf(HG_ABORT_BACKOUT_MERGE_CSET_ERR) > -1; // NOI18N
3360: }
3361:
3362: public static boolean isNoUpdates(String msg) {
3363: return msg.indexOf(HG_NO_UPDATES_ERR) > -1; // NOI18N
3364: }
3365:
3366: private static boolean isErrorNoView(String msg) {
3367: return msg.indexOf(HG_NO_VIEW_ERR) > -1; // NOI18N
3368: }
3369:
3370: private static boolean isErrorHgkNotFound(String msg) {
3371: return msg.indexOf(HG_HGK_NOT_FOUND_ERR) > -1; // NOI18N
3372: }
3373:
3374: private static boolean isErrorNoSuchFile(String msg) {
3375: return msg.indexOf(HG_NO_SUCH_FILE_ERR) > -1; // NOI18N
3376: }
3377:
3378: private static boolean isErrorNoResponse(String msg) {
3379: return msg.indexOf(HG_NO_RESPONSE_ERR) > -1; // NOI18N
3380: }
3381:
3382: public static void createConflictFile(String path) {
3383: try {
3384: File file = new File(path + HG_STR_CONFLICT_EXT);
3385:
3386: boolean success = file.createNewFile();
3387: Mercurial.LOG.log(Level.FINE,
3388: "createConflictFile(): File: {0} {1}", // NOI18N
3389: new Object[] { path + HG_STR_CONFLICT_EXT,
3390: success ? "Created" : "Not Created" }); // NOI18N
3391: } catch (IOException e) {
3392: }
3393: }
3394:
3395: public static void deleteConflictFile(String path) {
3396: boolean success = (new File(path + HG_STR_CONFLICT_EXT))
3397: .delete();
3398:
3399: Mercurial.LOG.log(Level.FINE,
3400: "deleteConflictFile(): File: {0} {1}", // NOI18N
3401: new Object[] { path + HG_STR_CONFLICT_EXT,
3402: success ? "Deleted" : "Not Deleted" }); // NOI18N
3403: }
3404:
3405: public static boolean existsConflictFile(String path) {
3406: File file = new File(path + HG_STR_CONFLICT_EXT);
3407: boolean bExists = file.canWrite();
3408:
3409: if (bExists) {
3410: Mercurial.LOG
3411: .log(Level.FINE,
3412: "existsConflictFile(): File: {0} {1}", // NOI18N
3413: new Object[] { path + HG_STR_CONFLICT_EXT,
3414: "Exists" }); // NOI18N
3415: }
3416: return bExists;
3417: }
3418:
3419: /**
3420: * This utility class should not be instantiated anywhere.
3421: */
3422: private HgCommand() {
3423: }
3424: }
|