Renamed constants, local variables and member variables using "hg" to "git".
[nbgit.git] / src / org / netbeans / modules / git / util / GitCommand.java
blobc529f7c6fa736f5476404e5cbeacba582825f02e
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common
8 * Development and Distribution License("CDDL") (collectively, the
9 * "License"). You may not use this file except in compliance with the
10 * License. You can obtain a copy of the License at
11 * http://www.netbeans.org/cddl-gplv2.html
12 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13 * specific language governing permissions and limitations under the
14 * License. When distributing the software, include this License Header
15 * Notice in each file and include the License file at
16 * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17 * particular file as subject to the "Classpath" exception as provided
18 * by Sun in the GPL Version 2 section of the License file that
19 * accompanied this code. If applicable, add the following below the
20 * License Header, with the fields enclosed by brackets [] replaced by
21 * your own identifying information:
22 * "Portions Copyrighted [year] [name of copyright owner]"
24 * Contributor(s):
26 * The Original Software is NetBeans. The Initial Developer of the Original
27 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28 * Microsystems, Inc. All Rights Reserved.
29 * Portions Copyright 2008 Alexander Coles (Ikonoklastik Productions).
31 * If you wish your version of this file to be governed by only the CDDL
32 * or only the GPL Version 2, indicate your decision by adding
33 * "[Contributor] elects to include this software in this distribution
34 * under the [CDDL or GPL Version 2] license." If you do not indicate a
35 * single choice of license, a recipient has the option to distribute
36 * your version of this file under either the CDDL, the GPL Version 2 or
37 * to extend the choice of license to its licensees as provided above.
38 * However, if you add GPL Version 2 code and therefore, elected the GPL
39 * Version 2 license, then the option applies only if the new code is
40 * made subject to such option by the copyright holder.
42 package org.netbeans.modules.git.util;
44 import java.awt.EventQueue;
45 import java.io.BufferedReader;
46 import java.io.BufferedWriter;
47 import java.io.File;
48 import java.io.FileWriter;
49 import java.io.IOException;
50 import java.io.InputStreamReader;
51 import java.io.InterruptedIOException;
52 import java.text.ParseException;
53 import java.text.SimpleDateFormat;
54 import java.util.ArrayList;
55 import java.util.Date;
56 import java.util.HashMap;
57 import java.util.Iterator;
58 import java.util.LinkedList;
59 import java.util.List;
60 import java.util.Map;
61 import java.util.Set;
62 import java.util.logging.Level;
63 import org.netbeans.api.options.OptionsDisplayer;
64 import org.netbeans.api.queries.SharabilityQuery;
65 import org.netbeans.modules.git.FileInformation;
66 import org.netbeans.modules.git.FileStatus;
67 import org.netbeans.modules.git.Git;
68 import org.netbeans.modules.git.GitException;
69 import org.netbeans.modules.git.GitModuleConfig;
70 import org.netbeans.modules.git.OutputLogger;
71 import org.netbeans.modules.git.config.GitConfigFiles;
72 import org.netbeans.modules.git.ui.log.GitLogMessage;
73 import org.netbeans.modules.versioning.spi.VCSContext;
74 import org.openide.DialogDisplayer;
75 import org.openide.NotifyDescriptor;
76 import org.openide.util.NbBundle;
77 import org.openide.util.Utilities;
79 /**
82 * @author alexbcoles
84 public class GitCommand {
86 public static final String GIT_COMMAND = "git"; // NOI18N
87 public static final String GITK_COMMAND = "gitk";
89 private static final String GIT_STATUS_CMD = "status"; // NOI18N // need -A to see ignored files, specified in .gitignore, see man gitignore for details
90 private static final String GIT_OPT_REPOSITORY = "--repository"; // NOI18N
91 private static final String GIT_OPT_BUNDLE = "--bundle"; // NOI18N
92 private static final String GIT_OPT_CWD_CMD = "--cwd"; // NOI18N
93 private static final String GIT_OPT_USERNAME = "--user"; // NOI18N
95 private static final String GIT_OPT_FOLLOW = "--follow"; // NOI18N
96 private static final String GIT_STATUS_FLAG_ALL_CMD = "-marduicC"; // NOI18N
97 private static final String GIT_FLAG_REV_CMD = "--rev"; // NOI18N
98 private static final String GIT_STATUS_FLAG_TIP_CMD = "tip"; // NOI18N
99 private static final String GIT_STATUS_FLAG_REM_DEL_CMD = "-rd"; // NOI18N
100 private static final String GIT_STATUS_FLAG_INCLUDE_CMD = "-I"; // NOI18N
101 private static final String GIT_STATUS_FLAG_INCLUDE_GLOB_CMD = "glob:"; // NOI18N
102 private static final String GIT_STATUS_FLAG_INCLUDE_END_CMD = "*"; // NOI18N
103 private static final String GIT_STATUS_FLAG_INTERESTING_CMD = "-marduC"; // NOI18N
104 private static final String GIT_STATUS_FLAG_UNKNOWN_CMD = "-u"; // NOI18N
105 private static final String GIT_HEAD_STR = "HEAD"; // NOI18N
106 private static final String GIT_FLAG_DATE_CMD = "--date"; // NOI18N
108 private static final String GIT_COMMIT_CMD = "commit"; // NOI18N
109 private static final String GIT_COMMIT_OPT_LOGFILE_CMD = "--logfile"; // NOI18N
110 private static final String GIT_COMMIT_TEMPNAME = "gitcommit"; // NOI18N
111 private static final String GIT_COMMIT_TEMPNAME_SUFFIX = ".gitm"; // NOI18N
112 private static final String GIT_COMMIT_DEFAULT_MESSAGE = "[no commit message]"; // NOI18N
114 private static final String GIT_REVERT_CMD = "revert"; // NOI18N
115 private static final String GIT_REVERT_NOBACKUP_CMD = "--no-backup"; // NOI18N
116 private static final String GIT_ADD_CMD = "add"; // NOI18N
118 private static final String GIT_BRANCH_CMD = "branch"; // NOI18N
119 private static final String GIT_BRANCH_REV_CMD = "tip"; // NOI18N
120 private static final String GIT_BRANCH_REV_TEMPLATE_CMD = "--template={rev}\\n"; // NOI18N
121 private static final String GIT_BRANCH_SHORT_CS_TEMPLATE_CMD = "--template={node|short}\\n"; // NOI18N
122 private static final String GIT_BRANCH_INFO_TEMPLATE_CMD = "--template={branches}:{rev}:{node|short}\\n"; // NOI18N
123 private static final String GIT_GET_PREVIOUS_TEMPLATE_CMD = "--template={files}\\n"; // NOI18N
125 private static final String GIT_CREATE_CMD = "init"; // NOI18N
126 private static final String GIT_CLONE_CMD = "clone"; // NOI18N
128 private static final String GIT_UPDATE_ALL_CMD = "update"; // NOI18N
129 private static final String GIT_UPDATE_FORCE_ALL_CMD = "-C"; // NOI18N
131 private static final String GIT_REMOVE_CMD = "remove"; // NOI18N
132 private static final String GIT_REMOVE_FLAG_FORCE_CMD = "--force"; // NOI18N
134 private static final String GIT_LOG_CMD = "log"; // NOI18N
135 private static final String GIT_OUT_CMD = "out"; // NOI18N
136 private static final String GIT_LOG_LIMIT_ONE_CMD = "-l 1"; // NOI18N
137 private static final String GIT_LOG_LIMIT_CMD = "-l"; // NOI18N
138 private static final String GIT_LOG_TEMPLATE_SHORT_CMD = "--template={rev}\\n{desc|firstline}\\n{date|gitdate}\\n{node|short}\\n"; // NOI18N
139 private static final String GIT_LOG_TEMPLATE_LONG_CMD = "--template={rev}\\n{desc}\\n{date|gitdate}\\n{node|short}\\n"; // NOI18N
141 private static final String GIT_LOG_NO_MERGES_CMD = "-M";
142 private static final String GIT_LOG_DEBUG_CMD = "--debug";
143 private static final String GIT_LOG_TEMPLATE_HISTORY_CMD =
144 "--template=rev:{rev}\\nauth:{author}\\ndesc:{desc}\\ndate:{date|gitdate}\\nid:{node|short}\\n" + // NOI18N
145 "file_mods:{files}\\nfile_adds:{file_adds}\\nfile_dels:{file_dels}\\nfile_copies:\\nendCS:\\n"; // NOI18N
146 private static final String GIT_LOG_REVISION_OUT = "rev:"; // NOI18N
147 private static final String GIT_LOG_AUTHOR_OUT = "auth:"; // NOI18N
148 private static final String GIT_LOG_DESCRIPTION_OUT = "desc:"; // NOI18N
149 private static final String GIT_LOG_DATE_OUT = "date:"; // NOI18N
150 private static final String GIT_LOG_ID_OUT = "id:"; // NOI18N
151 private static final String GIT_LOG_FILEMODS_OUT = "file_mods:"; // NOI18N
152 private static final String GIT_LOG_FILEADDS_OUT = "file_adds:"; // NOI18N
153 private static final String GIT_LOG_FILEDELS_OUT = "file_dels:"; // NOI18N
154 private static final String GIT_LOG_FILECOPIESS_OUT = "file_copies:"; // NOI18N
155 private static final String GIT_LOG_ENDCS_OUT = "endCS:"; // NOI18N
157 private static final String GIT_CSET_TEMPLATE_CMD = "--template={rev}:{node|short}\\n"; // NOI18N
158 private static final String GIT_REV_TEMPLATE_CMD = "--template={rev}\\n"; // NOI18N
159 private static final String GIT_CSET_TARGET_TEMPLATE_CMD = "--template={rev} ({node|short})\\n"; // NOI18N
161 private static final String GIT_CAT_CMD = "cat"; // NOI18N
162 private static final String GIT_FLAG_OUTPUT_CMD = "--output"; // NOI18N
164 private static final String GIT_ANNOTATE_CMD = "annotate"; // NOI18N
165 private static final String GIT_ANNOTATE_FLAGN_CMD = "--number"; // NOI18N
166 private static final String GIT_ANNOTATE_FLAGU_CMD = "--user"; // NOI18N
168 private static final String GIT_EXPORT_CMD = "export"; // NOI18N
169 private static final String GIT_IMPORT_CMD = "import"; // NOI18N
171 private static final String GIT_RENAME_CMD = "rename"; // NOI18N
172 private static final String GIT_RENAME_AFTER_CMD = "-A"; // NOI18N
173 private static final String GIT_PATH_DEFAULT_CMD = "paths"; // NOI18N
174 private static final String GIT_PATH_DEFAULT_OPT = "default"; // NOI18N
175 private static final String GIT_PATH_DEFAULT_PUSH_OPT = "default-push"; // NOI18N
177 private static final String GIT_MERGE_CMD = "merge"; // NOI18N
178 private static final String GIT_MERGE_FORCE_CMD = "-f"; // NOI18N
179 private static final String GIT_MERGE_ENV = "EDITOR=success || $TEST -s"; // NOI18N
181 public static final String GIT_GITK_PATH_SOLARIS10 = "/usr/demo/gitk"; // NOI18N
182 private static final String GIT_GITK_PATH_SOLARIS10_ENV = "PATH=/usr/bin/:/usr/sbin:/bin:"+ GIT_GITK_PATH_SOLARIS10; // NOI18N
184 private static final String GIT_PULL_CMD = "pull"; // NOI18N
185 private static final String GIT_UPDATE_CMD = "-u"; // NOI18N
186 private static final String GIT_PUSH_CMD = "push"; // NOI18N
187 private static final String GIT_UNBUNDLE_CMD = "unbundle"; // NOI18N
188 private static final String GIT_ROLLBACK_CMD = "rollback"; // NOI18N
190 private static final String GIT_BACKOUT_CMD = "backout"; // NOI18N
191 private static final String GIT_BACKOUT_MERGE_CMD = "--merge"; // NOI18N
192 private static final String GIT_BACKOUT_COMMIT_MSG_CMD = "-m"; // NOI18N
193 private static final String GIT_BACKOUT_REV_CMD = "-r"; // NOI18N
195 private static final String GIT_STRIP_CMD = "strip"; // NOI18N
196 private static final String GIT_STRIP_EXT_CMD = "extensions.mq="; // NOI18N
197 private static final String GIT_STRIP_NOBACKUP_CMD = "-n"; // NOI18N
198 private static final String GIT_STRIP_FORCE_MULTIHEAD_CMD = "-f"; // NOI18N
200 private static final String GIT_VERSION_CMD = "version"; // NOI18N
201 private static final String GIT_INCOMING_CMD = "incoming"; // NOI18N
202 private static final String GIT_OUTGOING_CMD = "outgoing"; // NOI18N
203 private static final String GIT_VIEW_CMD = "view"; // NOI18N
204 private static final String GIT_VERBOSE_CMD = "-v"; // NOI18N
205 private static final String GIT_CONFIG_OPTION_CMD = "--config"; // NOI18N
206 private static final String GIT_FETCH_EXT_CMD = "extensions.fetch="; // NOI18N
207 private static final String GIT_FETCH_CMD = "fetch"; // NOI18N
208 public static final String GIT_PROXY_ENV = "http_proxy="; // NOI18N
210 private static final String GIT_MERGE_NEEDED_ERR = "(run 'git heads' to see heads, 'git merge' to merge)"; // NOI18N
211 public static final String GIT_MERGE_CONFLICT_ERR = "conflicts detected in "; // NOI18N
212 public static final String GIT_MERGE_CONFLICT_WIN1_ERR = "merging"; // NOI18N
213 public static final String GIT_MERGE_CONFLICT_WIN2_ERR = "failed!"; // NOI18N
214 private static final String GIT_MERGE_MULTIPLE_HEADS_ERR = "abort: repo has "; // NOI18N
215 private static final String GIT_MERGE_UNCOMMITTED_ERR = "abort: outstanding uncommitted merges"; // NOI18N
217 private static final String GIT_MERGE_UNAVAILABLE_ERR = "is not recognized as an internal or external command";
219 private static final String GIT_NO_CHANGES_ERR = "no changes found"; // NOI18N
220 private final static String GIT_CREATE_NEW_BRANCH_ERR = "abort: push creates new remote branches!"; // NOI18N
221 private final static String GIT_HEADS_CREATED_ERR = "(+1 heads)"; // NOI18N
222 private final static String GIT_NO_GIT_CMD_FOUND_ERR = "git: not found";
223 private final static String GIT_ARG_LIST_TOO_LONG_ERR = "Arg list too long";
225 private final static String GIT_HEADS_CMD = "heads"; // NOI18N
227 private static final String GIT_NO_REPOSITORY_ERR = "There is no Git repository here"; // NOI18N
228 private static final String GIT_NO_RESPONSE_ERR = "no suitable response from remote git!"; // NOI18N
229 private static final String GIT_NOT_REPOSITORY_ERR = "does not appear to be an git repository"; // NOI18N
230 private static final String GIT_REPOSITORY = "repository"; // NOI18N
231 private static final String GIT_NOT_FOUND_ERR = "not found!"; // NOI18N
232 private static final String GIT_UPDATE_SPAN_BRANCHES_ERR = "abort: update spans branches"; // NOI18N
233 private static final String GIT_ALREADY_TRACKED_ERR = " already tracked!"; // NOI18N
234 private static final String GIT_NOT_TRACKED_ERR = " no tracked!"; // NOI18N
235 private static final String GIT_CANNOT_READ_COMMIT_MESSAGE_ERR = "abort: can't read commit message"; // NOI18N
236 private static final String GIT_CANNOT_RUN_ERR = "Cannot run program"; // NOI18N
237 private static final String GIT_ABORT_ERR = "abort: "; // NOI18N
238 private static final String GIT_ABORT_PUSH_ERR = "abort: push creates new remote branches!"; // NOI18N
239 private static final String GIT_ABORT_NO_FILES_TO_COPY_ERR = "abort: no files to copy"; // NOI18N
240 private static final String GIT_ABORT_NO_DEFAULT_PUSH_ERR = "abort: repository default-push not found!"; // NOI18N
241 private static final String GIT_ABORT_NO_DEFAULT_ERR = "abort: repository default not found!"; // NOI18N
242 private static final String GIT_ABORT_POSSIBLE_PROXY_ERR = "abort: error: node name or service name not known"; // NOI18N
243 private static final String GIT_ABORT_UNCOMMITTED_CHANGES_ERR = "abort: outstanding uncommitted changes"; // NOI18N
244 private static final String GIT_BACKOUT_MERGE_NEEDED_ERR = "(use \"backout --merge\" if you want to auto-merge)";
245 private static final String GIT_ABORT_BACKOUT_MERGE_CSET_ERR = "abort: cannot back out a merge changeset without --parent"; // NOI18N"
247 private static final String GIT_NO_CHANGE_NEEDED_ERR = "no change needed"; // NOI18N
248 private static final String GIT_NO_ROLLBACK_ERR = "no rollback information available"; // NOI18N
249 private static final String GIT_NO_UPDATES_ERR = "0 files updated, 0 files merged, 0 files removed, 0 files unresolved"; // NOI18N
250 private static final String GIT_NO_VIEW_ERR = "git: unknown command 'view'"; // NOI18N
251 private static final String GIT_GITK_NOT_FOUND_ERR = "sh: gitk: not found"; // NOI18N
252 private static final String GIT_NO_SUCH_FILE_ERR = "No such file"; // NOI18N
254 private static final String GIT_NO_REV_STRIP_ERR = "abort: unknown revision"; // NOI18N
255 private static final String GIT_LOCAL_CHANGES_STRIP_ERR = "abort: local changes found"; // NOI18N
256 private static final String GIT_MULTIPLE_HEADS_STRIP_ERR = "no rollback information available"; // NOI18N
258 private static final char GIT_STATUS_CODE_MODIFIED = 'M' + ' '; // NOI18N // STATUS_VERSIONED_MODIFIEDLOCALLY
259 private static final char GIT_STATUS_CODE_ADDED = 'A' + ' '; // NOI18N // STATUS_VERSIONED_ADDEDLOCALLY
260 private static final char GIT_STATUS_CODE_REMOVED = 'R' + ' '; // NOI18N // STATUS_VERSIONED_REMOVEDLOCALLY - still tracked, git update will recover, git commit
261 private static final char GIT_STATUS_CODE_CLEAN = 'C' + ' '; // NOI18N // STATUS_VERSIONED_UPTODATE
262 private static final char GIT_STATUS_CODE_DELETED = '!' + ' '; // NOI18N // STATUS_VERSIONED_DELETEDLOCALLY - still tracked, git update will recover, git commit no effect
263 private static final char GIT_STATUS_CODE_NOTTRACKED = '?' + ' '; // NOI18N // STATUS_NOTVERSIONED_NEWLOCALLY - not tracked
264 private static final char GIT_STATUS_CODE_IGNORED = 'I' + ' '; // NOI18N // STATUS_NOTVERSIONED_EXCLUDE - not shown by default
265 private static final char GIT_STATUS_CODE_CONFLICT = 'X' + ' '; // NOI18N // STATUS_VERSIONED_CONFLICT - TODO when Git status supports conflict markers
267 private static final char GIT_STATUS_CODE_ABORT = 'a' + 'b'; // NOI18N
268 public static final String GIT_STR_CONFLICT_EXT = ".conflict~"; // NOI18N
270 private static final String GIT_EPOCH_PLUS_ONE_YEAR = "1971-01-01"; // NOI18N
274 * Merge working directory with the head revision
275 * Merge the contents of the current working directory and the
276 * requested revision. Files that changed between either parent are
277 * marked as changed for the next commit and a commit must be
278 * performed before any further updates are allowed.
280 * @param File repository of the Git repository's root directory
281 * @param Revision to merge with, if null will merge with default tip rev
282 * @return git merge output
283 * @throws GitException
285 public static List<String> doMerge(File repository, String revStr) throws GitException {
286 if (repository == null ) return null;
287 List<String> command = new ArrayList<String>();
288 List<String> env = new ArrayList<String>();
290 command.add(getGitCommand());
291 command.add(GIT_MERGE_CMD);
292 command.add(GIT_MERGE_FORCE_CMD);
293 command.add(GIT_OPT_REPOSITORY);
294 command.add(repository.getAbsolutePath());
295 if(revStr != null)
296 command.add(revStr);
297 env.add(GIT_MERGE_ENV);
299 List<String> list = execEnv(command, env);
300 return list;
304 * Update the working directory to the tip revision.
305 * By default, update will refuse to run if doing so would require
306 * merging or discarding local changes.
308 * @param File repository of the Git repository's root directory
309 * @param Boolean force an Update and overwrite any modified files in the working directory
310 * @param String revision to be updated to
311 * @param Boolean throw exception on error
312 * @return git update output
313 * @throws org.netbeans.modules.git.GitException
315 public static List<String> doUpdateAll(File repository, boolean bForce, String revision, boolean bThrowException) throws GitException {
316 if (repository == null ) return null;
317 List<String> command = new ArrayList<String>();
319 command.add(getGitCommand());
320 command.add(GIT_UPDATE_ALL_CMD);
321 if (bForce) command.add(GIT_UPDATE_FORCE_ALL_CMD);
322 command.add(GIT_OPT_REPOSITORY);
323 command.add(repository.getAbsolutePath());
324 if (revision != null){
325 command.add(revision);
328 List<String> list = exec(command);
329 if (bThrowException) {
330 if (!list.isEmpty()) {
331 if (isErrorUpdateSpansBranches(list.get(0))) {
332 throw new GitException(NbBundle.getMessage(GitCommand.class, "MSG_WARN_UPDATE_MERGE_TEXT"));
333 } else if (isMergeAbortUncommittedMsg(list.get(0))) {
334 throw new GitException(NbBundle.getMessage(GitCommand.class, "MSG_WARN_UPDATE_COMMIT_TEXT"));
338 return list;
341 public static List<String> doUpdateAll(File repository, boolean bForce, String revision) throws GitException {
342 return doUpdateAll(repository, bForce, revision, true);
346 * Roll back the last transaction in this repository.
348 * Transactions are used to encapsulate the effects of all commands
349 * that create new changesets or propagate existing changesets into a
350 * repository. For example, the following commands are transactional,
351 * and their effects can be rolled back:
352 * commit, import, pull, push (with this repository as destination)
353 * unbundle
354 * There is only one level of rollback, and there is no way to undo a rollback.
356 * @param File repository of the Git repository's root directory
357 * @return git update output
358 * @throws org.netbeans.modules.git.GitException
360 public static List<String> doRollback(File repository, OutputLogger logger) throws GitException {
361 if (repository == null ) return null;
362 List<String> command = new ArrayList<String>();
364 command.add(getGitCommand());
365 command.add(GIT_ROLLBACK_CMD);
366 command.add(GIT_OPT_REPOSITORY);
367 command.add(repository.getAbsolutePath());
369 List<String> list = exec(command);
370 if (list.isEmpty())
371 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_ROLLBACK_FAILED"), logger);
373 return list;
375 public static List<String> doBackout(File repository, String revision,
376 boolean doMerge, String commitMsg, OutputLogger logger) throws GitException {
377 if (repository == null ) return null;
378 List<String> env = new ArrayList<String>();
379 List<String> command = new ArrayList<String>();
381 command.add(getGitCommand());
382 command.add(GIT_BACKOUT_CMD);
383 if(doMerge){
384 command.add(GIT_BACKOUT_MERGE_CMD);
385 env.add(GIT_MERGE_ENV);
388 if (commitMsg != null && !commitMsg.equals("")) { // NOI18N
389 command.add(GIT_BACKOUT_COMMIT_MSG_CMD);
390 command.add(commitMsg);
391 } else {
392 command.add(GIT_BACKOUT_COMMIT_MSG_CMD);
393 command.add(NbBundle.getMessage(GitCommand.class, "MSG_BACKOUT_MERGE_COMMIT_MSG", revision)); // NOI18N
396 command.add(GIT_OPT_REPOSITORY);
397 command.add(repository.getAbsolutePath());
398 if (revision != null){
399 command.add(GIT_BACKOUT_REV_CMD);
400 command.add(revision);
403 List<String> list;
404 if(doMerge){
405 list = execEnv(command, env);
406 }else{
407 list = exec(command);
409 if (list.isEmpty())
410 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_BACKOUT_FAILED"), logger);
412 return list;
415 public static List<String> doStrip(File repository, String revision,
416 boolean doForceMultiHead, boolean doBackup, OutputLogger logger) throws GitException {
417 if (repository == null ) return null;
418 List<String> command = new ArrayList<String>();
420 command.add(getGitCommand());
421 command.add(GIT_CONFIG_OPTION_CMD);
422 command.add(GIT_STRIP_EXT_CMD);
423 command.add(GIT_STRIP_CMD);
424 if(doForceMultiHead){
425 command.add(GIT_STRIP_FORCE_MULTIHEAD_CMD);
427 if(!doBackup){
428 command.add(GIT_STRIP_NOBACKUP_CMD);
430 command.add(GIT_OPT_REPOSITORY);
431 command.add(repository.getAbsolutePath());
432 if (revision != null){
433 command.add(revision);
436 List<String> list = exec(command);
437 if (list.isEmpty())
438 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_STRIP_FAILED"), logger);
440 return list;
444 * Returns the version of Git, e.g. 1.5.3.7.
446 * @return String
448 public static String getGitVersion() {
449 List<String> list = new LinkedList<String>();
450 try {
451 list = execForVersionCheck();
452 } catch (GitException ex) {
453 // Ignore Exception
454 return null;
456 if (!list.isEmpty()) {
457 int start = list.get(0).indexOf('(');
458 int end = list.get(0).indexOf(')');
459 if (start != -1 && end != -1) {
460 return list.get(0).substring(start + 9, end);
463 return null;
467 * Pull changes from the default pull locarion and update working directory.
468 * By default, update will refuse to run if doing so would require
469 * merging or discarding local changes.
471 * @param File repository of the Git repository's root directory
472 * @return git pull output
473 * @throws org.netbeans.modules.git.GitException
475 public static List<String> doPull(File repository, OutputLogger logger) throws GitException {
476 return doPull(repository, null, logger);
480 * Pull changes from the specified repository and update working directory.
481 * By default, update will refuse to run if doing so would require
482 * merging or discarding local changes.
484 * @param File repository of the Git repository's root directory
485 * @param String source repository to pull from
486 * @return git pull output
487 * @throws org.netbeans.modules.git.GitException
489 public static List<String> doPull(File repository, String from, OutputLogger logger) throws GitException {
490 if (repository == null ) return null;
491 List<String> command = new ArrayList<String>();
493 command.add(getGitCommand());
494 command.add(GIT_PULL_CMD);
495 command.add(GIT_UPDATE_CMD);
496 command.add(GIT_OPT_REPOSITORY);
497 command.add(repository.getAbsolutePath());
498 if (from != null) {
499 command.add(from);
502 List<String> list;
503 String defaultPull = new GitConfigFiles(repository).getDefaultPull(false);
504 String proxy = getGlobalProxyIfNeeded(defaultPull, true, logger);
505 if(proxy != null){
506 List<String> env = new ArrayList<String>();
507 env.add(GIT_PROXY_ENV + proxy);
508 list = execEnv(command, env);
509 }else{
510 list = exec(command);
513 if (!list.isEmpty() &&
514 isErrorAbort(list.get(list.size() -1))) {
515 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
517 return list;
521 * Unbundle changes from the specified local source repository and
522 * update working directory.
523 * By default, update will refuse to run if doing so would require
524 * merging or discarding local changes.
526 * @param File repository of the Git repository's root directory
527 * @param File bundle identfies the compressed changegroup file to be applied
528 * @return git unbundle output
529 * @throws org.netbeans.modules.git.GitException
531 public static List<String> doUnbundle(File repository, File bundle, OutputLogger logger) throws GitException {
532 if (repository == null ) return null;
533 List<String> command = new ArrayList<String>();
535 command.add(getGitCommand());
536 command.add(GIT_UNBUNDLE_CMD);
537 command.add(GIT_UPDATE_CMD);
538 command.add(GIT_OPT_REPOSITORY);
539 command.add(repository.getAbsolutePath());
540 if (bundle != null) {
541 command.add(bundle.getAbsolutePath());
544 List<String> list = exec(command);
545 if (!list.isEmpty() &&
546 isErrorAbort(list.get(list.size() -1))) {
547 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
549 return list;
553 * Show the changesets that would be pulled if a pull
554 * was requested from the default pull location
556 * @param File repository of the Git repository's root directory
557 * @return git incoming output
558 * @throws org.netbeans.modules.git.GitException
560 public static List<String> doIncoming(File repository, OutputLogger logger) throws GitException {
561 return doIncoming(repository, null, null, logger);
565 * Show the changesets that would be pulled if a pull
566 * was requested from the specified repository
568 * @param File repository of the Git repository's root directory
569 * @param String source repository to query
570 * @param File bundle to store downloaded changesets.
571 * @return git incoming output
572 * @throws org.netbeans.modules.git.GitException
574 public static List<String> doIncoming(File repository, String from, File bundle, OutputLogger logger) throws GitException {
575 if (repository == null ) return null;
576 List<String> command = new ArrayList<String>();
578 command.add(getGitCommand());
579 command.add(GIT_INCOMING_CMD);
580 command.add(GIT_VERBOSE_CMD);
581 command.add(GIT_OPT_REPOSITORY);
582 command.add(repository.getAbsolutePath());
583 if (bundle != null) {
584 command.add(GIT_OPT_BUNDLE);
585 command.add(bundle.getAbsolutePath());
587 if (from != null) {
588 command.add(from);
591 List<String> list;
592 String defaultPull = new GitConfigFiles(repository).getDefaultPull(false);
593 String proxy = getGlobalProxyIfNeeded(defaultPull, false, null);
594 if(proxy != null){
595 List<String> env = new ArrayList<String>();
596 env.add(GIT_PROXY_ENV + proxy);
597 list = execEnv(command, env);
598 }else{
599 list = exec(command);
602 if (!list.isEmpty() &&
603 isErrorAbort(list.get(list.size() -1))) {
604 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
606 return list;
610 * Show the changesets that would be pushed if a push
611 * was requested to the specified local source repository
613 * @param File repository of the Git repository's root directory
614 * @param String source repository to query
615 * @return git outgoing output
616 * @throws org.netbeans.modules.git.GitException
618 public static List<String> doOutgoing(File repository, String to, OutputLogger logger) throws GitException {
619 if (repository == null ) return null;
620 List<String> command = new ArrayList<String>();
622 command.add(getGitCommand());
623 command.add(GIT_OUTGOING_CMD);
624 command.add(GIT_VERBOSE_CMD);
625 command.add(GIT_OPT_REPOSITORY);
626 command.add(repository.getAbsolutePath());
627 command.add(to);
629 List<String> list;
630 String defaultPush = new GitConfigFiles(repository).getDefaultPush(false);
631 String proxy = getGlobalProxyIfNeeded(defaultPush, false, null);
632 if(proxy != null){
633 List<String> env = new ArrayList<String>();
634 env.add(GIT_PROXY_ENV + proxy);
635 list = execEnv(command, env);
636 }else{
637 list = exec(command);
639 if (!list.isEmpty() &&
640 isErrorAbort(list.get(list.size() -1))) {
641 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
643 return list;
647 * Push changes to the specified repository
648 * By default, push will refuse to run if doing so would create multiple heads
650 * @param File repository of the Git repository's root directory
651 * @param String source repository to push to
652 * @return git push output
653 * @throws org.netbeans.modules.git.GitException
655 public static List<String> doPush(File repository, String to, OutputLogger logger) throws GitException {
656 if (repository == null || to == null ) return null;
657 List<String> command = new ArrayList<String>();
659 command.add(getGitCommand());
660 command.add(GIT_PUSH_CMD);
661 command.add(GIT_OPT_REPOSITORY);
662 command.add(repository.getAbsolutePath());
663 command.add(to);
665 List<String> list;
666 String defaultPush = new GitConfigFiles(repository).getDefaultPush(false);
667 String proxy = getGlobalProxyIfNeeded(defaultPush, true, logger);
668 if(proxy != null){
669 List<String> env = new ArrayList<String>();
670 env.add(GIT_PROXY_ENV + proxy);
671 list = execEnv(command, env);
672 }else{
673 list = exec(command);
677 if (!list.isEmpty() &&
678 !isErrorAbortPush(list.get(list.size() -1)) &&
679 isErrorAbort(list.get(list.size() -1))) {
680 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
682 return list;
686 * Run the command git view for the specified repository
688 * @param File repository of the Git repository's root directory
689 * @throws org.netbeans.modules.git.GitException
691 public static List<String> doView(File repository, OutputLogger logger) throws GitException {
692 if (repository == null) return null;
693 List<String> command = new ArrayList<String>();
694 List<String> env = new ArrayList<String>();
696 command.add(getGitCommand());
697 command.add(GIT_VIEW_CMD);
698 command.add(GIT_OPT_REPOSITORY);
699 command.add(repository.getAbsolutePath());
701 List<String> list;
703 if(GitUtils.isSolaris()){
704 env.add(GIT_GITK_PATH_SOLARIS10_ENV);
705 list = execEnv(command, env);
706 }else{
707 list = exec(command);
710 if (!list.isEmpty()) {
711 if (isErrorNoView(list.get(list.size() -1))) {
712 throw new GitException(NbBundle.getMessage(GitCommand.class, "MSG_WARN_NO_VIEW_TEXT"));
714 else if (isErrorGitkNotFound(list.get(0))) {
715 throw new GitException(NbBundle.getMessage(GitCommand.class, "MSG_WARN_GITK_NOT_FOUND_TEXT"));
716 } else if (isErrorAbort(list.get(list.size() -1))) {
717 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
720 return list;
723 static File getRootFile(VCSContext ctx) {
724 throw new UnsupportedOperationException("Not yet implemented");
727 private static String getGlobalProxyIfNeeded(String defaultPath, boolean bOutputDetails, OutputLogger logger){
728 String proxy = null;
729 if(defaultPath != null &&
730 (defaultPath.startsWith("http:") || defaultPath.startsWith("https:"))){ // NOI18N
731 GitProxySettings ps = new GitProxySettings();
732 if (ps.isManualSetProxy()) {
733 if ((defaultPath.startsWith("http:") && !ps.getHttpHost().equals(""))||
734 (defaultPath.startsWith("https:") && !ps.getHttpHost().equals("") && ps.getHttpsHost().equals(""))) { // NOI18N
735 proxy = ps.getHttpHost();
736 if (proxy != null && !proxy.equals("")) {
737 proxy += ps.getHttpPort() > -1 ? ":" + Integer.toString(ps.getHttpPort()) : ""; // NOI18N
738 } else {
739 proxy = null;
741 } else if (defaultPath.startsWith("https:") && !ps.getHttpsHost().equals("")) { // NOI18N
742 proxy = ps.getHttpsHost();
743 if (proxy != null && !proxy.equals("")) {
744 proxy += ps.getHttpsPort() > -1 ? ":" + Integer.toString(ps.getHttpsPort()) : ""; // NOI18N
745 } else {
746 proxy = null;
751 if(proxy != null && bOutputDetails){
752 logger.output(NbBundle.getMessage(GitCommand.class, "MSG_USING_PROXY_INFO", proxy)); // NOI18N
754 return proxy;
757 * Run the fetch extension for the specified repository.
759 * @param File repository of the Git repository's root directory
760 * @throws org.netbeans.modules.git.GitException
762 public static List<String> doFetch(File repository, OutputLogger logger) throws GitException {
763 if (repository == null) return null;
764 List<String> command = new ArrayList<String>();
766 command.add(getGitCommand());
767 command.add(GIT_CONFIG_OPTION_CMD);
768 command.add(GIT_FETCH_EXT_CMD);
769 command.add(GIT_FETCH_CMD);
770 command.add(GIT_OPT_REPOSITORY);
771 command.add(repository.getAbsolutePath());
773 List<String> list;
774 String defaultPull = new GitConfigFiles(repository).getDefaultPull(false);
775 String proxy = getGlobalProxyIfNeeded(defaultPull, true, logger);
776 if(proxy != null){
777 List<String> env = new ArrayList<String>();
778 env.add(GIT_PROXY_ENV + proxy);
779 list = execEnv(command, env);
780 }else{
781 list = exec(command);
784 if (!list.isEmpty()) {
785 if (isErrorAbort(list.get(list.size() -1))) {
786 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
789 return list;
792 private static List<GitLogMessage> processLogMessages(List<String> list, final List<GitLogMessage> messages) {
793 String rev, author, desc, date, id, fm, fa, fd, fc;
794 if (list != null && !list.isEmpty()) {
795 rev = author = desc = date = id = fm = fa = fd = fc = null;
796 boolean bEnd = false;
797 for (String s : list) {
798 if (s.indexOf(GIT_LOG_REVISION_OUT) == 0) {
799 rev = s.substring(GIT_LOG_REVISION_OUT.length()).trim();
800 } else if (s.indexOf(GIT_LOG_AUTHOR_OUT) == 0) {
801 author = s.substring(GIT_LOG_AUTHOR_OUT.length()).trim();
802 } else if (s.indexOf(GIT_LOG_DESCRIPTION_OUT) == 0) {
803 desc = s.substring(GIT_LOG_DESCRIPTION_OUT.length()).trim();
804 } else if (s.indexOf(GIT_LOG_DATE_OUT) == 0) {
805 date = s.substring(GIT_LOG_DATE_OUT.length()).trim();
806 } else if (s.indexOf(GIT_LOG_ID_OUT) == 0) {
807 id = s.substring(GIT_LOG_ID_OUT.length()).trim();
808 } else if (s.indexOf(GIT_LOG_FILEMODS_OUT) == 0) {
809 fm = s.substring(GIT_LOG_FILEMODS_OUT.length()).trim();
810 } else if (s.indexOf(GIT_LOG_FILEADDS_OUT) == 0) {
811 fa = s.substring(GIT_LOG_FILEADDS_OUT.length()).trim();
812 } else if (s.indexOf(GIT_LOG_FILEDELS_OUT) == 0) {
813 fd = s.substring(GIT_LOG_FILEDELS_OUT.length()).trim();
814 } else if (s.indexOf(GIT_LOG_FILECOPIESS_OUT) == 0) {
815 fc = s.substring(GIT_LOG_FILECOPIESS_OUT.length()).trim();
816 } else if (s.indexOf(GIT_LOG_ENDCS_OUT) == 0) {
817 bEnd = true;
818 } else {
819 // Ignore all other lines
822 if (rev != null & bEnd) {
823 messages.add(new GitLogMessage(rev, author, desc, date, id, fm, fa, fd, fc));
824 rev = author = desc = date = id = fm = fa = fd = fc = null;
825 bEnd = false;
829 return messages;
832 public static GitLogMessage[] getIncomingMessages(final String rootUrl, String toRevision, boolean bShowMerges, OutputLogger logger) {
833 final List<GitLogMessage> messages = new ArrayList<GitLogMessage>(0);
834 final File root = new File(rootUrl);
836 try {
838 List<String> list = new LinkedList<String>();
839 list = GitCommand.doIncomingForSearch(root, toRevision, bShowMerges, logger);
840 processLogMessages(list, messages);
842 } catch (GitException ex) {
843 NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(ex);
844 DialogDisplayer.getDefault().notifyLater(e);
845 } finally {
846 logger.closeLog();
849 return messages.toArray(new GitLogMessage[0]);
852 public static GitLogMessage[] getOutMessages(final String rootUrl, boolean bShowMerges, OutputLogger logger) {
853 final List<GitLogMessage> messages = new ArrayList<GitLogMessage>(0);
854 final File root = new File(rootUrl);
856 try {
858 List<String> list = new LinkedList<String>();
859 list = GitCommand.doOut(root, bShowMerges, logger);
860 processLogMessages(list, messages);
862 } catch (GitException ex) {
863 NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(ex);
864 DialogDisplayer.getDefault().notifyLater(e);
865 } finally {
866 logger.closeLog();
869 return messages.toArray(new GitLogMessage[0]);
872 public static GitLogMessage[] getLogMessages(final String rootUrl,
873 final Set<File> files, String fromRevision, String toRevision, boolean bShowMerges, OutputLogger logger) {
874 final List<GitLogMessage> messages = new ArrayList<GitLogMessage>(0);
875 final File root = new File(rootUrl);
877 try {
878 String headRev = GitCommand.getLastRevision(root, null);
879 if (headRev == null) {
880 return messages.toArray(new GitLogMessage[0]);
883 List<String> list = new LinkedList<String>();
884 list = GitCommand.doLogForHistory(root,
885 files != null ? new ArrayList<File>(files) : null,
886 fromRevision, toRevision, headRev, bShowMerges, logger);
887 processLogMessages(list, messages);
889 } catch (GitException ex) {
890 NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(ex);
891 DialogDisplayer.getDefault().notifyLater(e);
892 } finally {
893 logger.closeLog();
896 return messages.toArray(new GitLogMessage[0]);
900 * Determines whether repository requires a merge - has more than 1 heads
902 * @param File repository of the Git repository's root directory
903 * @return Boolean which is true if the repository needs a merge
905 public static Boolean isMergeRequired(File repository) {
906 if (repository == null ) return false;
908 try {
909 List<String> list = getHeadRevisions(repository);
911 if (!list.isEmpty() && list.size() > 1){
912 Git.LOG.log(Level.FINE, "isMergeRequired(): TRUE " + list); // NOI18N
913 return true;
914 }else{
915 Git.LOG.log(Level.FINE, "isMergeRequired(): FALSE " + list); // NOI18N
916 return false;
918 } catch (GitException e) {
919 return false;
924 * Determines whether anything has been committed to the repository
926 * @param File repository of the Git repository's root directory
927 * @return Boolean which is true if the repository has revision history.
929 public static Boolean hasHistory(File repository) {
930 if (repository == null ) return false;
932 List<String> command = new ArrayList<String>();
934 command.add(getGitCommand());
935 command.add(GIT_LOG_CMD);
936 command.add(GIT_LOG_LIMIT_ONE_CMD);
937 command.add(GIT_OPT_REPOSITORY);
938 command.add(repository.getAbsolutePath());
940 try {
941 List<String> list = exec(command);
942 if (!list.isEmpty() && isErrorAbort(list.get(0)))
943 return false;
944 else
945 return !list.isEmpty();
946 } catch (GitException e) {
947 return false;
952 * Determines the previous name of the specified file.
954 * We make the assumption that the previous file name is in the
955 * list of files returned by git log command immediately befor
956 * the file we started with.
958 * @param File repository of the Git repository's root directory
959 * @param File file of the file whose previous name is required
960 * @param String revision which the revision to start from.
961 * @return File for the previous name of the file
963 private static File getPreviousName(File repository, File file, String revision) throws GitException {
964 if (repository == null ) return null;
965 if (revision == null ) return null;
967 List<String> command = new ArrayList<String>();
969 command.add(getGitCommand());
970 command.add(GIT_LOG_CMD);
971 command.add(GIT_OPT_FOLLOW);
972 command.add(GIT_OPT_REPOSITORY);
973 command.add(repository.getAbsolutePath());
974 command.add(GIT_FLAG_REV_CMD);
975 command.add(revision);
976 command.add(GIT_GET_PREVIOUS_TEMPLATE_CMD);
978 command.add(file.getAbsolutePath());
980 List<String> list = exec(command);
981 try {
982 list = exec(command);
983 if (!list.isEmpty() && isErrorAbort(list.get(0)))
984 return null;
985 } catch (GitException e) {
986 return null;
988 String[] fileNames = list.get(0).split(" ");
989 for (int j = fileNames.length -1 ; j > 0; j--) {
990 File name = new File(repository, fileNames[j]);
991 if (name.equals(file)) {
992 return new File(repository, fileNames[j-1]);
995 return null;
999 * Retrives the log information with just first line of commit message for the specified file.
1001 * @param File repository of the Git repository's root directory
1002 * @param File of file which revision history is to be retrieved.
1003 * @return List<String> list of the log entries for the specified file.
1004 * @throws org.netbeans.modules.git.GitException
1006 public static List<String> doLogShort(File repository, File file, OutputLogger logger) throws GitException {
1007 return doLog(repository, file, GIT_LOG_TEMPLATE_SHORT_CMD, false, logger);
1011 * Retrives the log information with the full commit message for the specified file.
1013 * @param File repository of the Git repository's root directory
1014 * @param File of file which revision history is to be retrieved.
1015 * @return List<String> list of the log entries for the specified file.
1016 * @throws org.netbeans.modules.git.GitException
1018 public static List<String> doLogLong(File repository, File file, OutputLogger logger) throws GitException {
1019 return doLog(repository, file, GIT_LOG_TEMPLATE_LONG_CMD, false, logger);
1023 * Retrives the log information for the specified file, as defined by the LOG_TEMPLATE.
1025 * @param File repository of the Git repository's root directory
1026 * @param File of file which revision history is to be retrieved.
1027 * @param String Template specifying how output should be returned
1028 * @param boolean flag indicating if debug param should be used - required to get all file mod, add, del info
1029 * @return List<String> list of the log entries for the specified file.
1030 * @throws org.netbeans.modules.git.GitException
1032 public static List<String> doLog(File repository, File file, String LOG_TEMPLATE, boolean bDebug, OutputLogger logger) throws GitException {
1033 if (repository == null ) return null;
1035 List<String> command = new ArrayList<String>();
1037 command.add(getGitCommand());
1038 command.add(GIT_LOG_CMD);
1039 if (!file.isDirectory()) {
1040 command.add(GIT_OPT_FOLLOW);
1042 command.add(GIT_OPT_REPOSITORY);
1043 command.add(repository.getAbsolutePath());
1044 if(bDebug){
1045 command.add(GIT_LOG_DEBUG_CMD);
1047 command.add(LOG_TEMPLATE);
1048 command.add(file.getAbsolutePath());
1050 List<String> list = exec(command);
1051 if (!list.isEmpty()) {
1052 if (isErrorNoRepository(list.get(0))) {
1053 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_REPOSITORY_ERR"), logger);
1054 } else if (isErrorAbort(list.get(0))) {
1055 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
1058 return list;
1062 * Retrives the log information for the specified files.
1064 * @param File repository of the Git repository's root directory
1065 * @param List<File> of files which revision history is to be retrieved.
1066 * @param String Template specifying how output should be returned
1067 * @param boolean flag indicating if debug param should be used - required to get all file mod, add, del info
1068 * @return List<String> list of the log entries for the specified file.
1069 * @throws org.netbeans.modules.git.GitException
1071 public static List<String> doLog(File repository, List<File> files, String LOG_TEMPLATE, boolean bDebug, OutputLogger logger) throws GitException {
1072 if (repository == null ) return null;
1073 if (files != null && files.isEmpty()) return null;
1075 List<String> command = new ArrayList<String>();
1077 command.add(getGitCommand());
1078 command.add(GIT_VERBOSE_CMD);
1079 command.add(GIT_LOG_CMD);
1080 boolean doFollow = true;
1081 if( files != null){
1082 for (File f : files) {
1083 if (f.isDirectory()) {
1084 doFollow = false;
1085 break;
1089 if (doFollow) {
1090 command.add(GIT_OPT_FOLLOW);
1092 command.add(GIT_OPT_REPOSITORY);
1093 command.add(repository.getAbsolutePath());
1094 if(bDebug){
1095 command.add(GIT_LOG_DEBUG_CMD);
1097 if(LOG_TEMPLATE != null){
1098 command.add(LOG_TEMPLATE);
1100 if( files != null){
1101 for (File f : files) {
1102 command.add(f.getAbsolutePath());
1106 List<String> list = exec(command);
1107 if (!list.isEmpty()) {
1108 if (isErrorNoRepository(list.get(0))) {
1109 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_REPOSITORY_ERR"), logger);
1110 } else if (isErrorAbort(list.get(0))) {
1111 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
1114 return list;
1118 * Retrives the log information for the specified files.
1120 * @param File repository of the Git repository's root directory
1121 * @param List<File> of files which revision history is to be retrieved.
1122 * @param String Template specifying how output should be returned
1123 * @param boolean flag indicating if debug param should be used - required to get all file mod, add, del info
1124 * @return List<String> list of the log entries for the specified file.
1125 * @throws org.netbeans.modules.git.GitException
1127 public static List<String> doLogForHistory(File repository, List<File> files,
1128 String from, String to, String headRev, boolean bShowMerges, OutputLogger logger) throws GitException {
1129 if (repository == null ) return null;
1130 if (files != null && files.isEmpty()) return null;
1132 List<String> command = new ArrayList<String>();
1134 command.add(getGitCommand());
1135 command.add(GIT_VERBOSE_CMD);
1136 command.add(GIT_LOG_CMD);
1137 boolean doFollow = true;
1138 if( files != null){
1139 for (File f : files) {
1140 if (f.isDirectory()) {
1141 doFollow = false;
1142 break;
1146 if (doFollow) {
1147 command.add(GIT_OPT_FOLLOW);
1149 if(!bShowMerges){
1150 command.add(GIT_LOG_NO_MERGES_CMD);
1152 command.add(GIT_OPT_REPOSITORY);
1153 command.add(repository.getAbsolutePath());
1154 command.add(GIT_LOG_DEBUG_CMD);
1156 String dateStr = handleRevDates(from, to);
1157 if(dateStr != null){
1158 command.add(GIT_FLAG_DATE_CMD);
1159 command.add(dateStr);
1161 String revStr = handleRevNumbers(from, to, headRev);
1162 if(dateStr == null && revStr != null){
1163 command.add(GIT_FLAG_REV_CMD);
1164 command.add(revStr);
1166 command.add(GIT_LOG_TEMPLATE_HISTORY_CMD);
1168 if( files != null){
1169 for (File f : files) {
1170 command.add(f.getAbsolutePath());
1173 List<String> list = exec(command);
1174 if (!list.isEmpty()) {
1175 if (isErrorNoRepository(list.get(0))) {
1176 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_REPOSITORY_ERR"), logger);
1177 } else if (isErrorAbort(list.get(0))) {
1178 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
1181 return list;
1185 * Retrives the Out information for the specified repository
1187 * @param File repository of the Git repository's root directory
1188 * @return List<String> list of the out entries for the specified repo.
1189 * @throws org.netbeans.modules.git.GitException
1191 public static List<String> doOut(File repository, boolean bShowMerges, OutputLogger logger) throws GitException {
1192 if (repository == null ) return null;
1194 List<String> command = new ArrayList<String>();
1196 command.add(getGitCommand());
1197 command.add(GIT_OUT_CMD);
1198 command.add(GIT_OPT_REPOSITORY);
1199 command.add(repository.getAbsolutePath());
1200 if(!bShowMerges){
1201 command.add(GIT_LOG_NO_MERGES_CMD);
1203 command.add(GIT_LOG_DEBUG_CMD);
1205 command.add(GIT_LOG_TEMPLATE_HISTORY_CMD);
1207 List<String> list;
1208 String defaultPush = new GitConfigFiles(repository).getDefaultPush(false);
1209 String proxy = getGlobalProxyIfNeeded(defaultPush, false, null);
1210 if(proxy != null){
1211 List<String> env = new ArrayList<String>();
1212 env.add(GIT_PROXY_ENV + proxy);
1213 list = execEnv(command, env);
1214 }else{
1215 list = exec(command);
1217 if (!list.isEmpty()) {
1218 if(isErrorNoDefaultPush(list.get(0))){
1219 // Ignore
1220 }else if (isErrorNoRepository(list.get(0))) {
1221 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_REPOSITORY_ERR"), logger);
1222 } else if (isErrorAbort(list.get(0))) {
1223 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
1226 return list;
1230 * Retrives the Incoming changeset information for the specified repository
1232 * @param File repository of the Git repository's root directory
1233 * @return List<String> list of the out entries for the specified repo.
1234 * @throws org.netbeans.modules.git.GitException
1236 public static List<String> doIncomingForSearch(File repository, String to, boolean bShowMerges, OutputLogger logger) throws GitException {
1237 if (repository == null ) return null;
1239 List<String> command = new ArrayList<String>();
1241 command.add(getGitCommand());
1242 command.add(GIT_INCOMING_CMD);
1243 command.add(GIT_OPT_REPOSITORY);
1244 command.add(repository.getAbsolutePath());
1245 if(!bShowMerges){
1246 command.add(GIT_LOG_NO_MERGES_CMD);
1248 command.add(GIT_LOG_DEBUG_CMD);
1249 String revStr = handleIncomingRevNumber(to);
1250 if(revStr != null){
1251 command.add(GIT_FLAG_REV_CMD);
1252 command.add(revStr);
1254 command.add(GIT_LOG_TEMPLATE_HISTORY_CMD);
1256 List<String> list = exec(command);
1257 String defaultPull = new GitConfigFiles(repository).getDefaultPull(false);
1258 String proxy = getGlobalProxyIfNeeded(defaultPull, false, null);
1259 if(proxy != null){
1260 List<String> env = new ArrayList<String>();
1261 env.add(GIT_PROXY_ENV + proxy);
1262 list = execEnv(command, env);
1263 }else{
1264 list = exec(command);
1267 if (!list.isEmpty()) {
1268 if (isErrorNoDefaultPath(list.get(0))) {
1269 // Ignore
1270 } else if (isErrorNoRepository(list.get(0))) {
1271 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_REPOSITORY_ERR"), logger);
1272 } else if (isErrorAbort(list.get(0)) || isErrorAbort(list.get(list.size() - 1))) {
1273 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
1276 return list;
1279 private static String handleRevDates(String from, String to){
1280 // Check for Date range:
1281 Date fromDate = null;
1282 Date toDate = null;
1283 Date currentDate = new Date(); // Current Date
1284 Date epochPlusOneDate = null;
1286 try {
1287 epochPlusOneDate = new SimpleDateFormat("yyyy-MM-dd").parse(GIT_EPOCH_PLUS_ONE_YEAR); // NOI18N
1288 } catch (ParseException ex) {
1289 // Ignore invalid dates
1292 // Set From date
1293 try {
1294 if(from != null)
1295 fromDate = new SimpleDateFormat("yyyy-MM-dd").parse(from); // NOI18N
1296 } catch (ParseException ex) {
1297 // Ignore invalid dates
1300 // Set To date
1301 try {
1302 if(to != null)
1303 toDate = new SimpleDateFormat("yyyy-MM-dd").parse(to); // NOI18N
1304 } catch (ParseException ex) {
1305 // Ignore invalid dates
1308 // If From date is set, but To date is not - default To date to current date
1309 if( fromDate != null && toDate == null && to == null){
1310 toDate = currentDate;
1311 to = new SimpleDateFormat("yyyy-MM-dd").format(toDate);
1313 // If To date is set, but From date is not - default From date to 1971-01-01
1314 if (fromDate == null && from == null && toDate != null) {
1315 fromDate = epochPlusOneDate;
1316 from = GIT_EPOCH_PLUS_ONE_YEAR; // NOI18N
1319 // If using dates make sure both From and To are set to dates
1320 if( (fromDate != null && toDate == null && to != null) ||
1321 (fromDate == null && from != null && toDate != null)){
1322 GitUtils.warningDialog(GitCommand.class,"MSG_SEARCH_HISTORY_TITLE",// NOI18N
1323 "MSG_SEARCH_HISTORY_WARN_BOTHDATES_NEEDED_TEXT"); // NOI18N
1324 return null;
1327 if(fromDate != null && toDate != null){
1328 // Check From date - default to 1971-01-01 if From date is earlier than this
1329 if(epochPlusOneDate != null && fromDate.before(epochPlusOneDate)){
1330 fromDate = epochPlusOneDate;
1331 from = GIT_EPOCH_PLUS_ONE_YEAR; // NOI18N
1333 // Set To date - default to current date if To date is later than this
1334 if(currentDate != null && toDate.after(currentDate)){
1335 toDate = currentDate;
1336 to = new SimpleDateFormat("yyyy-MM-dd").format(toDate);
1339 // Make sure the From date is before the To date
1340 if( fromDate.after(toDate)){
1341 GitUtils.warningDialog(GitCommand.class,"MSG_SEARCH_HISTORY_TITLE",// NOI18N
1342 "MSG_SEARCH_HISTORY_WARN_FROM_BEFORE_TODATE_NEEDED_TEXT"); // NOI18N
1343 return null;
1345 return from + " to " + to; // NOI18N
1347 return null;
1350 private static String handleIncomingRevNumber(String to) {
1351 int toInt = -1;
1353 // Handle users entering head or tip for revision, instead of a number
1354 if (to != null && (to.equalsIgnoreCase(GIT_STATUS_FLAG_TIP_CMD) || to.equalsIgnoreCase(GIT_HEAD_STR))) {
1355 to = GIT_STATUS_FLAG_TIP_CMD;
1357 try {
1358 toInt = Integer.parseInt(to);
1359 } catch (NumberFormatException e) {
1360 // ignore invalid numbers
1363 return (toInt > -1) ? to : GIT_STATUS_FLAG_TIP_CMD;
1366 private static String handleRevNumbers(String from, String to, String headRev){
1367 int fromInt = -1;
1368 int toInt = -1;
1369 int headRevInt = -1;
1371 // Handle users entering head or tip for revision, instead of a number
1372 if(from != null && (from.equalsIgnoreCase(GIT_STATUS_FLAG_TIP_CMD) || from.equalsIgnoreCase(GIT_HEAD_STR)))
1373 from = headRev;
1374 if(to != null && (to.equalsIgnoreCase(GIT_STATUS_FLAG_TIP_CMD) || to.equalsIgnoreCase(GIT_HEAD_STR)))
1375 to = headRev;
1377 try{
1378 fromInt = Integer.parseInt(from);
1379 }catch (NumberFormatException e){
1380 // ignore invalid numbers
1382 try{
1383 toInt = Integer.parseInt(to);
1384 }catch (NumberFormatException e){
1385 // ignore invalid numbers
1387 try{
1388 headRevInt = Integer.parseInt(headRev);
1389 }catch (NumberFormatException e){
1390 // ignore invalid numbers
1393 // Handle out of range revisions
1394 if (headRevInt > -1 && toInt > headRevInt) {
1395 to = headRev;
1396 toInt = headRevInt;
1398 if (headRevInt > -1 && fromInt > headRevInt) {
1399 from = headRev;
1400 fromInt = headRevInt;
1403 // Handle revision ranges
1404 String revStr = null;
1405 if (fromInt > -1 && toInt > -1){
1406 revStr = from + ":" + to;
1407 }else if (fromInt > -1){
1408 revStr = from + (headRevInt != -1 ? ":" + headRevInt: "");
1409 }else if (toInt > -1){
1410 revStr = "0:" + to;
1413 return revStr;
1416 * Retrieves the base revision of the specified file to the
1417 * specified output file.
1419 * @param File repository of the Git repository's root directory
1420 * @param File file in the Git repository
1421 * @param File outFile to contain the contents of the file
1422 * @throws org.netbeans.modules.git.GitException
1424 public static void doCat(File repository, File file, File outFile, OutputLogger logger) throws GitException {
1425 doCat(repository, file, outFile, "tip", false, logger); //NOI18N
1429 * Retrieves the specified revision of the specified file to the
1430 * specified output file.
1432 * @param File repository of the Git repository's root directory
1433 * @param File file in the Git repository
1434 * @param File outFile to contain the contents of the file
1435 * @param String of revision for the revision of the file to be
1436 * printed to the output file.
1437 * @return List<String> list of all the log entries
1438 * @throws org.netbeans.modules.git.GitException
1440 public static void doCat(File repository, File file, File outFile, String revision, OutputLogger logger) throws GitException {
1441 doCat(repository, file, outFile, revision, true, logger); //NOI18N
1444 public static void doCat(File repository, File file, File outFile, String revision, boolean retry, OutputLogger logger) throws GitException {
1445 if (repository == null) return;
1446 if (file == null) return;
1448 List<String> command = new ArrayList<String>();
1450 command.add(getGitCommand());
1451 command.add(GIT_CAT_CMD);
1452 command.add(GIT_OPT_REPOSITORY);
1453 command.add(repository.getAbsolutePath());
1454 command.add(GIT_FLAG_OUTPUT_CMD);
1455 command.add(outFile.getAbsolutePath());
1457 if (revision != null) {
1458 command.add(GIT_FLAG_REV_CMD);
1459 command.add(revision);
1461 command.add(file.getAbsolutePath());
1462 List<String> list = exec(command);
1464 if (!list.isEmpty()) {
1465 if (isErrorNoRepository(list.get(0))) {
1466 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_REPOSITORY_ERR"), logger);
1467 } else if (isErrorAbort(list.get(0))) {
1468 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
1471 if (outFile.length() == 0 && retry) {
1472 // Perhaps the file has changed its name
1473 String newRevision = Integer.toString(Integer.parseInt(revision)+1);
1474 File prevFile = getPreviousName(repository, file, newRevision);
1475 if (prevFile != null) {
1476 doCat(repository, prevFile, outFile, revision, false, logger); //NOI18N
1482 * Initialize a new repository in the given directory. If the given
1483 * directory does not exist, it is created. Will throw a GitException
1484 * if the repository already exists.
1486 * @param root for the Git repository
1487 * @return void
1488 * @throws org.netbeans.modules.git.GitException
1490 public static void doCreate(File root, OutputLogger logger) throws GitException {
1491 if (root == null ) return;
1492 List<String> command = new ArrayList<String>();
1494 command.add(getGitCommand());
1495 command.add(GIT_CREATE_CMD);
1496 command.add(root.getAbsolutePath());
1498 List<String> list = exec(command);
1499 if (!list.isEmpty())
1500 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_CREATE_FAILED"), logger);
1504 * Clone an exisiting repository to the specified target directory
1506 * @param File repository of the Git repository's root directory
1507 * @param target directory to clone to
1508 * @return clone output
1509 * @throws org.netbeans.modules.git.GitException
1511 public static List<String> doClone(File repository, String target, OutputLogger logger) throws GitException {
1512 if (repository == null) return null;
1513 return doClone(repository.getAbsolutePath(), target, logger);
1517 * Clone a repository to the specified target directory
1519 * @param String repository of the Git repository
1520 * @param target directory to clone to
1521 * @return clone output
1522 * @throws org.netbeans.modules.git.GitException
1524 public static List<String> doClone(String repository, String target, OutputLogger logger) throws GitException {
1525 if (repository == null || target == null) return null;
1527 // Ensure that parent directory of target exists, creating if necessary
1528 File fileTarget = new File (target);
1529 File parentTarget = fileTarget.getParentFile();
1530 try {
1531 if (!parentTarget.mkdir()) {
1532 if (!parentTarget.isDirectory()) {
1533 Git.LOG.log(Level.WARNING, "File.mkdir() failed for : " + parentTarget.getAbsolutePath()); // NOI18N
1534 throw (new GitException (NbBundle.getMessage(GitCommand.class, "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
1537 } catch (SecurityException e) {
1538 Git.LOG.log(Level.WARNING, "File.mkdir() for : " + parentTarget.getAbsolutePath() + " threw SecurityException " + e.getMessage()); // NOI18N
1539 throw (new GitException (NbBundle.getMessage(GitCommand.class, "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
1541 List<String> command = new ArrayList<String>();
1543 command.add(getGitCommand());
1544 command.add(GIT_CLONE_CMD);
1545 command.add(GIT_VERBOSE_CMD);
1547 // TODO: Remove this for Git Port
1548 if (repository.startsWith("file://")) {
1549 command.add(repository.substring(7));
1550 } else {
1551 command.add(repository);
1553 command.add(target);
1555 List<String> list = exec(command);
1556 if (!list.isEmpty()) {
1557 if (isErrorNoRepository(list.get(0))){
1558 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_REPOSITORY_ERR"), logger);
1559 }else if (isErrorNoResponse(list.get(list.size() -1))){
1560 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_RESPONSE_ERR"), logger);
1561 }else if (isErrorAbort(list.get(list.size() -1))){
1562 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ABORTED"), logger);
1565 return list;
1569 * Commits the list of Locally Changed files to the Git Repository
1571 * @param File repository of the Git repository's root directory
1572 * @param List<files> of files to be committed to Git
1573 * @param String for commitMessage
1574 * @return void
1575 * @throws org.netbeans.modules.git.GitException
1577 public static void doCommit(File repository, List<File> commitFiles, String commitMessage, OutputLogger logger) throws GitException {
1578 List<String> command = new ArrayList<String>();
1580 command.add(getGitCommand());
1581 command.add(GIT_COMMIT_CMD);
1582 command.add(GIT_OPT_REPOSITORY);
1583 command.add(repository.getAbsolutePath());
1585 String projectUserName = new GitConfigFiles(repository).getUserName(false);
1586 String globalUsername = GitConfigFiles.getInstance().getUserName();
1587 String username = null;
1588 if(projectUserName != null && projectUserName.length() > 0)
1589 username = projectUserName;
1590 else if (globalUsername != null && globalUsername.length() > 0)
1591 username = globalUsername;
1593 if(username != null ){
1594 command.add(GIT_OPT_USERNAME);
1595 command.add(username);
1598 File tempfile = null;
1600 try {
1601 if (commitMessage == null || commitMessage.length() == 0) {
1602 commitMessage = GIT_COMMIT_DEFAULT_MESSAGE;
1604 // Create temporary file.
1605 tempfile = File.createTempFile(GIT_COMMIT_TEMPNAME, GIT_COMMIT_TEMPNAME_SUFFIX);
1607 // Write to temp file
1608 BufferedWriter out = new BufferedWriter(new FileWriter(tempfile));
1609 out.write(commitMessage);
1610 out.close();
1612 command.add(GIT_COMMIT_OPT_LOGFILE_CMD);
1613 command.add(tempfile.getAbsolutePath());
1615 for(File f: commitFiles){
1616 command.add(f.getAbsolutePath());
1618 List<String> list = exec(command);
1620 if (!list.isEmpty()
1621 && (isErrorNotTracked(list.get(0)) || isErrorCannotReadCommitMsg(list.get(0))))
1622 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_COMMIT_FAILED"), logger);
1624 }catch (IOException ex){
1625 throw new GitException(NbBundle.getMessage(GitCommand.class, "MSG_FAILED_TO_READ_COMMIT_MESSAGE"));
1626 }finally{
1627 if (commitMessage != null && tempfile != null){
1628 tempfile.delete();
1635 * Rename a source file to a destination file.
1636 * git mv
1638 * @param File repository of the Git repository's root directory
1639 * @param File of sourceFile which was renamed
1640 * @param File of destFile to which sourceFile has been renaned
1641 * @param boolean whether to do a rename --after
1642 * @return void
1643 * @throws org.netbeans.modules.git.GitException
1645 public static void doRename(File repository, File sourceFile, File destFile, OutputLogger logger) throws GitException {
1646 doRename(repository, sourceFile, destFile, false, logger);
1649 private static void doRename(File repository, File sourceFile, File destFile, boolean bAfter, OutputLogger logger) throws GitException {
1650 if (repository == null) return;
1652 List<String> command = new ArrayList<String>();
1654 command.add(getGitCommand());
1655 command.add(GIT_RENAME_CMD);
1656 if (bAfter) command.add(GIT_RENAME_AFTER_CMD);
1657 command.add(GIT_OPT_REPOSITORY);
1658 command.add(repository.getAbsolutePath());
1659 command.add(GIT_OPT_CWD_CMD);
1660 command.add(repository.getAbsolutePath());
1662 command.add(sourceFile.getAbsolutePath().substring(repository.getAbsolutePath().length()+1));
1663 command.add(destFile.getAbsolutePath().substring(repository.getAbsolutePath().length()+1));
1665 List<String> list = exec(command);
1666 if (!list.isEmpty() &&
1667 isErrorAbort(list.get(list.size() -1))) {
1668 if (!bAfter || !isErrorAbortNoFilesToCopy(list.get(list.size() -1))) {
1669 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_RENAME_FAILED"), logger);
1675 * Mark a source file as having been renamed to a destination file.
1676 * git mv -A.
1678 * @param File repository of the Git repository's root directory
1679 * @param File of sourceFile which was renamed
1680 * @param File of destFile to which sourceFile has been renaned
1681 * @return void
1682 * @throws org.netbeans.modules.git.GitException
1684 public static void doRenameAfter(File repository, File sourceFile, File destFile, OutputLogger logger) throws GitException {
1685 doRename(repository, sourceFile, destFile, true, logger);
1689 * Adds the list of Locally New files to the Git Repository.
1690 * Their status will change to added and they will be added on the next
1691 * git add.
1693 * @param File repository of the Git repository's root directory
1694 * @param List<Files> of files to be added to Git
1695 * @return void
1696 * @throws org.netbeans.modules.git.GitException
1698 public static void doAdd(File repository, List<File> addFiles, OutputLogger logger) throws GitException {
1699 if (repository == null) return;
1700 if (addFiles.size() == 0) return;
1701 List<String> command = new ArrayList<String>();
1703 command.add(getGitCommand());
1704 command.add(GIT_ADD_CMD);
1705 command.add(GIT_OPT_REPOSITORY);
1706 command.add(repository.getAbsolutePath());
1708 for(File f: addFiles){
1709 if(f.isDirectory())
1710 continue;
1711 // We do not look for files to ignore as we should not here
1712 // with a file to be ignored.
1713 command.add(f.getAbsolutePath());
1715 List<String> list = exec(command);
1716 if (!list.isEmpty() && isErrorAlreadyTracked(list.get(0)))
1717 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_ALREADY_TRACKED"), logger);
1721 * Reverts the list of files in the Git Repository to the specified revision
1723 * @param File repository of the Git repository's root directory
1724 * @param List<Files> of files to be reverted
1725 * @param String revision to be reverted to
1726 * @return void
1727 * @throws org.netbeans.modules.git.GitException
1729 public static void doRevert(File repository, List<File> revertFiles,
1730 String revision, boolean doBackup, OutputLogger logger) throws GitException {
1731 if (repository == null) return;
1732 if (revertFiles.size() == 0) return;
1734 List<String> command = new ArrayList<String>();
1736 command.add(getGitCommand());
1737 command.add(GIT_REVERT_CMD);
1738 if(!doBackup){
1739 command.add(GIT_REVERT_NOBACKUP_CMD);
1741 command.add(GIT_OPT_REPOSITORY);
1742 command.add(repository.getAbsolutePath());
1743 if (revision != null){
1744 command.add(GIT_FLAG_REV_CMD);
1745 command.add(revision);
1748 for(File f: revertFiles){
1749 command.add(f.getAbsolutePath());
1751 List<String> list = exec(command);
1752 if (!list.isEmpty() && isErrorNoChangeNeeded(list.get(0)))
1753 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_REVERT_FAILED"), logger);
1757 * Adds a Locally New file to the Git Repository
1758 * The status will change to added and they will be added on the next
1759 * git commit.
1761 * @param File repository of the Git repository's root directory
1762 * @param File of file to be added to Git
1763 * @return void
1764 * @throws org.netbeans.modules.git.GitException
1766 public static void doAdd(File repository, File file, OutputLogger logger) throws GitException {
1767 if (repository == null) return;
1768 if (file == null) return;
1769 if (file.isDirectory()) return;
1770 // We do not look for file to ignore as we should not here
1771 // with a file to be ignored.
1773 List<String> command = new ArrayList<String>();
1775 command.add(getGitCommand());
1776 command.add(GIT_ADD_CMD);
1777 command.add(GIT_OPT_REPOSITORY);
1778 command.add(repository.getAbsolutePath());
1780 command.add(file.getAbsolutePath());
1781 List<String> list = exec(command);
1782 if (!list.isEmpty() && isErrorAlreadyTracked(list.get(0)))
1783 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_ALREADY_TRACKED"), logger);
1787 * Get the annotations for the specified file
1789 * @param File repository of the Git repository
1790 * @param File file to be annotated
1791 * @param String revision of the file to be annotated
1792 * @return List<String> list of the annotated lines of the file
1793 * @throws org.netbeans.modules.git.GitException
1795 public static List<String> doAnnotate(File repository, File file, String revision, OutputLogger logger) throws GitException {
1796 if (repository == null) return null;
1797 List<String> command = new ArrayList<String>();
1799 command.add(getGitCommand());
1800 command.add(GIT_ANNOTATE_CMD);
1801 command.add(GIT_OPT_REPOSITORY);
1802 command.add(repository.getAbsolutePath());
1804 if (revision != null) {
1805 command.add(GIT_FLAG_REV_CMD);
1806 command.add(revision);
1808 command.add(GIT_ANNOTATE_FLAGN_CMD);
1809 command.add(GIT_ANNOTATE_FLAGU_CMD);
1810 command.add(GIT_OPT_FOLLOW);
1811 command.add(file.getAbsolutePath());
1812 List<String> list = exec(command);
1813 if (!list.isEmpty()) {
1814 if (isErrorNoRepository(list.get(0))) {
1815 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_REPOSITORY_ERR"), logger);
1816 } else if (isErrorNoSuchFile(list.get(0))) {
1817 // This can happen if we have multiple heads and the wrong
1818 // one was picked by default git annotation
1819 if (revision == null) {
1820 String rev = getLastRevision(repository, file);
1821 if (rev != null) {
1822 list = doAnnotate(repository, file, rev, logger);
1823 } else {
1824 list = null;
1826 } else {
1827 list = null;
1831 return list;
1834 public static List<String> doAnnotate(File repository, File file, OutputLogger logger) throws GitException {
1835 return doAnnotate(repository, file, null, logger);
1839 * Get the revisions this file has been modified in.
1841 * @param File repository of the Git repository's root directory
1842 * @param files to query revisions for
1843 * @param Int limit on nunmber of revisions (-1 for no limit)
1844 * @return List<String> list of the revisions of the file - {<rev>:<short cset hash>}
1845 * or null if no commits made yet.
1847 public static List<String> getRevisionsForFile(File repository, File[] files, int limit) {
1848 if (repository == null) return null;
1849 List<String> command = new ArrayList<String>();
1851 command.add(getGitCommand());
1852 command.add(GIT_LOG_CMD);
1853 if (limit >= 0) {
1854 command.add(GIT_LOG_LIMIT_CMD);
1855 command.add(Integer.toString(limit));
1857 command.add(GIT_OPT_REPOSITORY);
1858 command.add(repository.getAbsolutePath());
1859 command.add(GIT_CSET_TARGET_TEMPLATE_CMD);
1860 if(files != null) {
1861 for (File file : files) {
1862 command.add(file.getAbsolutePath());
1866 List<String> list = new ArrayList<String>();
1867 try {
1868 list = exec(command);
1869 } catch (GitException ex) {
1870 // Ignore Exception
1872 return list == null || list.isEmpty()? null: list;
1876 * Get the revisions for a repository
1878 * @param File repository of the Git repository's root directory
1879 * @return List<String> list of the revisions of the repository - {<rev>:<short cset hash>}
1880 * or null if no commits made yet.
1882 public static List<String> getRevisions(File repository, int limit) {
1883 if (repository == null) return null;
1884 return getRevisionsForFile(repository, null, limit);
1888 * Get the pull default for the specified repository, i.e. the default
1889 * destination for git pull commmands.
1891 * @param File repository of the Git repository's root directory
1892 * @return String for pull default
1894 public static String getPullDefault(File repository) {
1895 return getPathDefault(repository, GIT_PATH_DEFAULT_OPT);
1899 * Get the push default for the specified repository, i.e. the default
1900 * destination for git push commmands.
1902 * @param File repository of the Git repository's root directory
1903 * @return String for push default
1905 public static String getPushDefault(File repository) {
1906 return getPathDefault(repository, GIT_PATH_DEFAULT_PUSH_OPT);
1909 private static String getPathDefault(File repository, String type) {
1910 if (repository == null) return null;
1911 List<String> command = new ArrayList<String>();
1913 command.add(getGitCommand());
1914 command.add(GIT_PATH_DEFAULT_CMD);
1915 command.add(GIT_OPT_REPOSITORY);
1916 command.add(repository.getAbsolutePath());
1917 command.add(type);
1919 String res = null;
1921 List<String> list = new LinkedList<String>();
1922 try {
1923 list = exec(command);
1924 } catch (GitException ex) {
1925 // Ignore Exception
1927 if( !list.isEmpty()
1928 && (!isErrorNotFound(list.get(0)))) {
1929 res = list.get(0);
1931 return res;
1935 * Returns the Git branch name if any for a repository
1937 * @param File repository of the git repository's root directory
1938 * @return String branch name or null if not named
1939 * @throws org.netbeans.modules.git.GitException
1941 public static String getBranchName(File repository) throws GitException {
1942 if (repository == null) return null;
1944 List<String> command = new ArrayList<String>();
1946 command.add(getGitCommand());
1947 command.add(GIT_BRANCH_CMD);
1948 command.add(GIT_OPT_REPOSITORY);
1949 command.add(repository.getAbsolutePath());
1951 List<String> list = exec(command);
1952 if (!list.isEmpty()){
1953 return list.get(0);
1954 }else{
1955 return null;
1960 * Returns the Git branch revision for a repository
1962 * @param File repository of the Git repository's root directory
1963 * @return int value of revision for repository tip
1964 * @throws org.netbeans.modules.git.GitException
1966 public static int getBranchRev(File repository) throws GitException {
1967 if (repository == null) return -1;
1969 List<String> command = new ArrayList<String>();
1971 command.add(getGitCommand());
1972 command.add(GIT_BRANCH_REV_CMD);
1973 command.add(GIT_BRANCH_REV_TEMPLATE_CMD);
1974 command.add(GIT_OPT_REPOSITORY);
1975 command.add(repository.getAbsolutePath());
1977 List<String> list = exec(command);
1978 if (!list.isEmpty()){
1979 return Integer.parseInt(list.get(0));
1980 }else{
1981 return -1;
1986 * Returns the Git branch name if any for a repository
1988 * @param File repository of the git repository's root directory
1989 * @return String branch short change set hash
1990 * @throws org.netbeans.modules.git.GitException
1992 public static String getBranchShortChangesetHash(File repository) throws GitException {
1993 if (repository == null) return null;
1995 List<String> command = new ArrayList<String>();
1997 command.add(getGitCommand());
1998 command.add(GIT_BRANCH_REV_CMD);
1999 command.add(GIT_BRANCH_SHORT_CS_TEMPLATE_CMD);
2000 command.add(GIT_OPT_REPOSITORY);
2001 command.add(repository.getAbsolutePath());
2003 List<String> list = exec(command);
2004 if (!list.isEmpty()){
2005 return list.get(0);
2006 }else{
2007 return null;
2011 * Returns the Git branch info for a repository
2013 * @param File repository of the git repository's root directory
2014 * @return String of form :<branch>:<rev>:<shortchangeset>:
2015 * @throws org.netbeans.modules.git.GitException
2017 public static String getBranchInfo(File repository) throws GitException {
2018 if (repository == null) return null;
2020 List<String> command = new ArrayList<String>();
2022 command.add(getGitCommand());
2023 command.add(GIT_BRANCH_REV_CMD);
2024 command.add(GIT_BRANCH_INFO_TEMPLATE_CMD);
2025 command.add(GIT_OPT_REPOSITORY);
2026 command.add(repository.getAbsolutePath());
2028 List<String> list = exec(command);
2029 if (!list.isEmpty()){
2030 return list.get(0);
2031 }else{
2032 return null;
2037 * Returns the revision number for the heads in a repository
2039 * @param File repository of the Git repository's root directory
2040 * @return List<String> of revision numbers.
2041 * @throws org.netbeans.modules.git.GitException
2043 public static List<String> getHeadRevisions(File repository) throws GitException {
2044 return getHeadInfo(repository, GIT_REV_TEMPLATE_CMD);
2048 * Returns the revision number for the heads in a repository
2050 * @param String repository of the Git repository
2051 * @return List<String> of revision numbers.
2052 * @throws org.netbeans.modules.git.GitException
2054 public static List<String> getHeadRevisions(String repository) throws GitException {
2055 return getHeadInfo(repository, GIT_REV_TEMPLATE_CMD);
2059 * Returns the changeset for the the heads in a repository
2061 * @param File repository of the Git repository's root directory
2062 * @param File file of the file whose last changeset is to be returned.
2063 * @return List<String> of changeset ids.
2064 * @throws org.netbeans.modules.git.GitException
2066 public static List<String> getHeadChangeSetIds(File repository) throws GitException {
2067 return getHeadInfo(repository, GIT_CSET_TARGET_TEMPLATE_CMD);
2070 private static List<String> getHeadInfo(String repository, String template) throws GitException {
2071 if (repository == null) return null;
2073 List<String> command = new ArrayList<String>();
2075 command.add(getGitCommand());
2076 command.add(GIT_HEADS_CMD);
2077 command.add(GIT_OPT_REPOSITORY);
2078 command.add(repository);
2079 command.add(template);
2081 return exec(command);
2084 private static List<String> getHeadInfo(File repository, String template) throws GitException {
2085 if (repository == null) return null;
2086 return getHeadInfo(repository.getAbsolutePath(), template);
2090 * Returns the revision number for the last change to a file
2092 * @param File repository of the Git repository's root directory
2093 * @param File file of the file whose last revision number is to be returned, if null test for repo
2094 * @return String in the form of a revision number.
2095 * @throws org.netbeans.modules.git.GitException
2097 public static String getLastRevision(File repository, File file) throws GitException {
2098 return getLastChange(repository, file, GIT_REV_TEMPLATE_CMD);
2102 * Returns the changeset for the last change to a file
2104 * @param File repository of the Git repository's root directory
2105 * @param File file of the file whose last changeset is to be returned.
2106 * @return String in the form of a changeset id.
2107 * @throws org.netbeans.modules.git.GitException
2109 public static String getLastChangeSetId(File repository, File file) throws GitException {
2110 return getLastChange(repository, file, GIT_CSET_TEMPLATE_CMD);
2113 private static String getLastChange(File repository, File file, String template) throws GitException {
2115 if (repository == null) return null;
2117 List<String> command = new ArrayList<String>();
2119 command.add(getGitCommand());
2120 command.add(GIT_LOG_CMD);
2121 command.add(GIT_LOG_LIMIT_ONE_CMD);
2122 command.add(GIT_OPT_REPOSITORY);
2123 command.add(repository.getAbsolutePath());
2124 command.add(template);
2125 if( file != null)
2126 command.add(file.getAbsolutePath());
2128 List<String> list = exec(command);
2129 if (!list.isEmpty()) {
2130 return new StringBuffer(list.get(0)).toString();
2131 } else {
2132 return null;
2138 * Returns the Git status for a given file
2140 * @param File repository of the git repository's root directory
2141 * @param cwd current working directory containing file to be checked
2142 * @param filename name of file whose status is to be checked
2143 * @return FileInformation for the given filename
2144 * @throws org.netbeans.modules.git.GitException
2146 public static FileInformation getSingleStatus(File repository, String cwd, String filename) throws GitException{
2147 FileInformation info = null;
2148 List<String> list = doSingleStatusCmd(repository, cwd, filename);
2149 if(list == null || list.isEmpty())
2150 return new FileInformation(FileInformation.STATUS_UNKNOWN,null, false);
2152 info = getFileInformationFromStatusLine(list.get(0));
2153 // Handles Copy status
2154 // Could save copy source in FileStatus but for now we don't need it.
2155 // FileStatus used in Fileinformation.java:getStatusText() and getShortStatusText() to check if
2156 // file is Locally Copied when it's status is Locally Added
2157 if(list.size() == 2) {
2158 if (list.get(1).length() > 0){
2159 if (list.get(1).charAt(0) == ' '){
2161 info = new FileInformation(FileInformation.STATUS_VERSIONED_ADDEDLOCALLY,
2162 new FileStatus(new File(new File(cwd), filename), true), false);
2163 Git.LOG.log(Level.FINE, "getSingleStatus() - Copied: Locally Added {0}, Copy Source {1}", // NOI18N
2164 new Object[] {list.get(0), list.get(1)} );
2166 } else {
2167 Git.LOG.log(Level.FINE, "getSingleStatus() - Second line empty: first line: {0}", list.get(0)); // NOI18N
2171 // Handle Conflict Status
2172 // TODO: remove this if Git status supports Conflict marker
2173 if(existsConflictFile(cwd + File.separator + filename)){
2174 info = new FileInformation(FileInformation.STATUS_VERSIONED_CONFLICT, null, false);
2175 Git.LOG.log(Level.FINE, "getSingleStatus(): CONFLICT StatusLine: {0} Status: {1} {2} RepoPath:{3} cwd:{4} CONFLICT {5}", // NOI18N
2176 new Object[] {list.get(0), info.getStatus(), filename, repository.getAbsolutePath(), cwd,
2177 cwd + File.separator + filename + GitCommand.GIT_STR_CONFLICT_EXT} );
2180 Git.LOG.log(Level.FINE, "getSingleStatus(): StatusLine: {0} Status: {1} {2} RepoPath:{3} cwd:{4}", // NOI18N
2181 new Object[] {list.get(0), info.getStatus(), filename, repository.getAbsolutePath(), cwd} );
2182 return info;
2186 * Returns the Git status for all files in a given subdirectory of
2187 * a repository
2189 * @param File repository of the git repository's root directory
2190 * @param File dir of the subdirectoy of interest.
2191 * @return Map of files and status for all files in the specified subdirectory
2192 * @throws org.netbeans.modules.git.GitException
2194 public static Map<File, FileInformation> getAllStatus(File repository, File dir) throws GitException{
2195 return getDirStatusWithFlags(repository, dir, GIT_STATUS_FLAG_ALL_CMD, true);
2199 * Returns the git status for all files in a given repository
2201 * @param File repository of the Git repository's root directory
2202 * @return Map of files and status for all files under the repository root
2203 * @throws org.netbeans.modules.git.GitException
2205 public static Map<File, FileInformation> getAllStatus(File repository) throws GitException{
2206 return getAllStatusWithFlags(repository, GIT_STATUS_FLAG_ALL_CMD, true);
2210 * Returns the git status for only files of interest to us in a given repository
2211 * that is modified, locally added, locally removed, locally deleted, locally new and ignored.
2213 * @param File repository of the Git repository's root directory
2214 * @return Map of files and status for all files of interest under the repository root
2215 * @throws org.netbeans.modules.git.GitException
2217 public static Map<File, FileInformation> getAllInterestingStatus(File repository) throws GitException{
2218 return getAllStatusWithFlags(repository, GIT_STATUS_FLAG_INTERESTING_CMD, true);
2222 * Returns the git status for only files of interest to us in a given directory in a repository
2223 * that is modified, locally added, locally removed, locally deleted, locally new and ignored.
2225 * @param File repository of the Git repository's root directory
2226 * @param File dir of the directory of interest
2227 * @return Map of files and status for all files of interest in the directory of interest
2228 * @throws org.netbeans.modules.git.GitException
2230 public static Map<File, FileInformation> getInterestingStatus(File repository, File dir) throws GitException{
2231 return getDirStatusWithFlags(repository, dir, GIT_STATUS_FLAG_INTERESTING_CMD, true);
2235 * Returns the git status for only files of interest to us in a given repository
2236 * that is modified, locally added, locally removed, locally deleted, locally new and ignored.
2238 * @param File repository of the Git repository's root directory
2239 * @return Map of files and status for all files of interest under the repository root
2240 * @throws org.netbeans.modules.git.GitException
2242 public static Map<File, FileInformation> getAllRemovedDeletedStatus(File repository) throws GitException{
2243 return getAllStatusWithFlags(repository, GIT_STATUS_FLAG_REM_DEL_CMD, true);
2247 * Returns the git status for only files of interest to us in a given directory in a repository
2248 * that is modified, locally added, locally removed, locally deleted, locally new and ignored.
2250 * @param File repository of the Git repository's root directory
2251 * @param File dir of the directory of interest
2252 * @return Map of files and status for all files of interest in the specified directory
2253 * @throws org.netbeans.modules.git.GitException
2255 public static Map<File, FileInformation> getRemovedDeletedStatus(File repository, File dir) throws GitException{
2256 return getDirStatusWithFlags(repository, dir, GIT_STATUS_FLAG_REM_DEL_CMD, true);
2260 * Returns the unknown files in a specified directory under a Git repository root
2262 * @param File of the Git repository's root directory
2263 * @param File of the directory whose files are required
2264 * @return Map of files and status for all files under the repository root
2265 * @throws org.netbeans.modules.git.GitException
2267 public static Map<File, FileInformation> getUnknownStatus(File repository, File dir) throws GitException{
2268 Map<File, FileInformation> files = getDirStatusWithFlags(repository, dir, GIT_STATUS_FLAG_UNKNOWN_CMD, false);
2269 int share = SharabilityQuery.getSharability(dir == null ? repository : dir);
2270 for (Iterator i = files.keySet().iterator(); i.hasNext();) {
2271 File file = (File) i.next();
2272 if((share == SharabilityQuery.MIXED && SharabilityQuery.getSharability(file) == SharabilityQuery.NOT_SHARABLE) ||
2273 (share == SharabilityQuery.NOT_SHARABLE)) {
2274 i.remove();
2277 return files;
2281 * Returns the unknown files under a Git repository root
2283 * @param File repository of the Git repository's root directory
2284 * @return Map of files and status for all files under the repository root
2285 * @throws org.netbeans.modules.git.GitException
2287 public static Map<File, FileInformation> getAllUnknownStatus(File repository) throws GitException{
2288 return getUnknownStatus(repository, null);
2292 * Remove the specified file from the Git Repository
2294 * @param File repository of the Git repository's root directory
2295 * @param List<Files> of files to be added to Git
2296 * @param f path to be removed from the repository
2297 * @throws org.netbeans.modules.git.GitException
2299 public static void doRemove(File repository, List<File> removeFiles, OutputLogger logger) throws GitException {
2300 List<String> command = new ArrayList<String>();
2302 command.add(getGitCommand());
2303 command.add(GIT_REMOVE_CMD);
2304 command.add(GIT_OPT_REPOSITORY);
2305 command.add(repository.getAbsolutePath());
2306 command.add(GIT_REMOVE_FLAG_FORCE_CMD);
2307 for(File f: removeFiles){
2308 command.add(f.getAbsolutePath());
2311 List<String> list = exec(command);
2312 if (!list.isEmpty() && isErrorAlreadyTracked(list.get(0)))
2313 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_ALREADY_TRACKED"), logger);
2317 * Remove the specified files from the Git Repository
2319 * @param File repository of the git repository's root directory
2320 * @param f path to be removed from the repository
2321 * @throws org.netbeans.modules.git.GitException
2323 public static void doRemove(File repository, File f, OutputLogger logger) throws GitException {
2324 List<String> command = new ArrayList<String>();
2326 command.add(getGitCommand());
2327 command.add(GIT_REMOVE_CMD);
2328 command.add(GIT_OPT_REPOSITORY);
2329 command.add(repository.getAbsolutePath());
2330 command.add(GIT_REMOVE_FLAG_FORCE_CMD);
2331 command.add(f.getAbsolutePath());
2333 List<String> list = exec(command);
2334 if (!list.isEmpty() && isErrorAlreadyTracked(list.get(0)))
2335 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_ALREADY_TRACKED"), logger);
2339 * Export the diffs for the specified revision to the specified output file
2341 * Export the diffs for the specified revision to the specified output file
2343 * @param File repository of the Git repository's root directory
2344 * @param revStr the revision whose diffs are to be exported
2345 * @param outputFileName path of the output file
2346 * @throws org.netbeans.modules.git.GitException
2348 public static List<String> doExport(File repository, String revStr, String outputFileName, OutputLogger logger) throws GitException {
2349 // Ensure that parent directory of target exists, creating if necessary
2350 File fileTarget = new File (outputFileName);
2351 File parentTarget = fileTarget.getParentFile();
2352 try {
2353 if (!parentTarget.mkdir()) {
2354 if (!parentTarget.isDirectory()) {
2355 Git.LOG.log(Level.WARNING, "File.mkdir() failed for : " + parentTarget.getAbsolutePath()); // NOI18N
2356 throw (new GitException (NbBundle.getMessage(GitCommand.class, "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
2359 } catch (SecurityException e) {
2360 Git.LOG.log(Level.WARNING, "File.mkdir() for : " + parentTarget.getAbsolutePath() + " threw SecurityException " + e.getMessage()); // NOI18N
2361 throw (new GitException (NbBundle.getMessage(GitCommand.class, "MSG_UNABLE_TO_CREATE_PARENT_DIR"))); // NOI18N
2363 List<String> command = new ArrayList<String>();
2365 command.add(getGitCommand());
2366 command.add(GIT_EXPORT_CMD);
2367 command.add(GIT_VERBOSE_CMD);
2368 command.add(GIT_OPT_REPOSITORY);
2369 command.add(repository.getAbsolutePath());
2370 command.add(GIT_FLAG_OUTPUT_CMD);
2371 command.add(outputFileName);
2372 command.add(revStr);
2374 List<String> list = exec(command);
2375 if (!list.isEmpty() &&
2376 isErrorAbort(list.get(list.size() -1))) {
2377 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_EXPORT_FAILED"), logger);
2379 return list;
2383 * Imports the diffs from the specified file
2385 * @param File repository of the Git repository's root directory
2386 * @param File patchFile of the patch file
2387 * @throws org.netbeans.modules.git.GitException
2389 public static List<String> doImport(File repository, File patchFile, OutputLogger logger) throws GitException {
2390 List<String> command = new ArrayList<String>();
2392 command.add(getGitCommand());
2393 command.add(GIT_IMPORT_CMD);
2394 command.add(GIT_VERBOSE_CMD);
2395 command.add(GIT_OPT_REPOSITORY);
2396 command.add(repository.getAbsolutePath());
2397 command.add(GIT_OPT_CWD_CMD);
2398 command.add(repository.getAbsolutePath());
2399 command.add(patchFile.getAbsolutePath());
2401 List<String> list = exec(command);
2402 if (!list.isEmpty() &&
2403 isErrorAbort(list.get(list.size() -1))) {
2404 logger.output(list); // need the failure info from import
2405 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_IMPORT_FAILED"), logger);
2407 return list;
2411 * Returns Map of Git file and status for files in a given repository as specified by the status flags
2413 private static Map<File, FileInformation> getAllStatusWithFlags(File repository, String statusFlags, boolean bIgnoreUnversioned) throws GitException{
2414 return getDirStatusWithFlags(repository, null, statusFlags, bIgnoreUnversioned);
2417 private static Map<File, FileInformation> getDirStatusWithFlags(File repository, File dir, String statusFlags, boolean bIgnoreUnversioned) throws GitException{
2418 if (repository == null) return null;
2420 List<FileStatus> statusList = new ArrayList<FileStatus>();
2421 FileInformation prev_info = null;
2422 List<String> list = doRepositoryDirStatusCmd(repository, dir, statusFlags);
2424 Map<File, FileInformation> repositoryFiles = new HashMap<File, FileInformation>(list.size());
2426 StringBuffer filePath = null;
2427 for(String statusLine: list){
2428 FileInformation info = getFileInformationFromStatusLine(statusLine);
2429 Git.LOG.log(Level.FINE, "getDirStatusWithFlags(): status line {0} info {1}", new Object[]{statusLine, info}); // NOI18N
2430 if (statusLine.length() > 0) {
2431 if (statusLine.charAt(0) == ' ') {
2432 // Locally Added but Copied
2433 if (filePath != null) {
2434 prev_info = new FileInformation(FileInformation.STATUS_VERSIONED_ADDEDLOCALLY,
2435 new FileStatus(new File(filePath.toString()), true), false);
2436 Git.LOG.log(Level.FINE, "getDirStatusWithFlags(): prev_info {0} filePath {1}", new Object[]{prev_info, filePath.toString()}); // NOI18N
2437 } else {
2438 Git.LOG.log(Level.FINE, "getDirStatusWithFlags(): repository path: {0} status flags: {1} status line {2} filepath == nullfor prev_info ", new Object[]{repository.getAbsolutePath(), statusFlags, statusLine}); // NOI18N
2440 break;
2441 } else {
2442 if (filePath != null) {
2443 repositoryFiles.put(new File(filePath.toString()), prev_info);
2447 if(bIgnoreUnversioned){
2448 if(info.getStatus() == FileInformation.STATUS_NOTVERSIONED_NOTMANAGED ||
2449 info.getStatus() == FileInformation.STATUS_UNKNOWN) continue;
2450 }else{
2451 if(info.getStatus() == FileInformation.STATUS_UNKNOWN) continue;
2453 filePath = new StringBuffer(repository.getAbsolutePath()).append(File.separatorChar);
2454 StringBuffer sb = new StringBuffer(statusLine);
2455 sb.delete(0,2); // Strip status char and following 2 spaces: [MARC\?\!I][ ][ ]
2456 filePath.append(sb.toString());
2458 // Handle Conflict Status
2459 // TODO: remove this if Git status supports Conflict marker
2460 if (existsConflictFile(filePath.toString())) {
2461 info = new FileInformation(FileInformation.STATUS_VERSIONED_CONFLICT, null, false);
2462 Git.LOG.log(Level.FINE, "getDirStatusWithFlags(): CONFLICT repository path: {0} status flags: {1} status line {2} CONFLICT {3}", new Object[]{repository.getAbsolutePath(), statusFlags, statusLine, filePath.toString() + GitCommand.GIT_STR_CONFLICT_EXT}); // NOI18N
2464 prev_info = info;
2466 if (prev_info != null) {
2467 repositoryFiles.put(new File(filePath.toString()), prev_info);
2470 if (list.size() < 10) {
2471 Git.LOG.log(Level.FINE, "getDirStatusWithFlags(): repository path: {0} status flags: {1} status list {2}", // NOI18N
2472 new Object[] {repository.getAbsolutePath(), statusFlags, list} );
2473 } else {
2474 Git.LOG.log(Level.FINE, "getDirStatusWithFlags(): repository path: {0} status flags: {1} status list has {2} elements", // NOI18N
2475 new Object[] {repository.getAbsolutePath(), statusFlags, list.size()} );
2477 return repositoryFiles;
2481 * Gets file information for a given git status output status line
2483 private static FileInformation getFileInformationFromStatusLine(String status){
2484 FileInformation info = null;
2485 if (status == null || (status.length() == 0)) return new FileInformation(FileInformation.STATUS_VERSIONED_UPTODATE, null, false);
2487 char c0 = status.charAt(0);
2488 char c1 = status.charAt(1);
2489 switch(c0 + c1) {
2490 case GIT_STATUS_CODE_MODIFIED:
2491 info = new FileInformation(FileInformation.STATUS_VERSIONED_MODIFIEDLOCALLY,null, false);
2492 break;
2493 case GIT_STATUS_CODE_ADDED:
2494 info = new FileInformation(FileInformation.STATUS_VERSIONED_ADDEDLOCALLY,null, false);
2495 break;
2496 case GIT_STATUS_CODE_REMOVED:
2497 info = new FileInformation(FileInformation.STATUS_VERSIONED_REMOVEDLOCALLY,null, false);
2498 break;
2499 case GIT_STATUS_CODE_CLEAN:
2500 info = new FileInformation(FileInformation.STATUS_VERSIONED_UPTODATE,null, false);
2501 break;
2502 case GIT_STATUS_CODE_DELETED:
2503 info = new FileInformation(FileInformation.STATUS_VERSIONED_DELETEDLOCALLY,null, false);
2504 break;
2505 case GIT_STATUS_CODE_IGNORED:
2506 info = new FileInformation(FileInformation.STATUS_NOTVERSIONED_EXCLUDED,null, false);
2507 break;
2508 case GIT_STATUS_CODE_NOTTRACKED:
2509 info = new FileInformation(FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY,null, false);
2510 break;
2511 // Leave this here for whenever Git status suports conflict markers
2512 case GIT_STATUS_CODE_CONFLICT:
2513 info = new FileInformation(FileInformation.STATUS_VERSIONED_CONFLICT,null, false);
2514 break;
2515 case GIT_STATUS_CODE_ABORT:
2516 info = new FileInformation(FileInformation.STATUS_NOTVERSIONED_NOTMANAGED,null, false);
2517 break;
2518 default:
2519 info = new FileInformation(FileInformation.STATUS_UNKNOWN,null, false);
2520 break;
2523 return info;
2527 * Gets git status command output line for a given file
2529 private static List<String> doSingleStatusCmd(File repository, String cwd, String filename) throws GitException{
2530 String statusLine = null;
2532 List<String> command = new ArrayList<String>();
2534 command.add(getGitCommand());
2535 command.add(GIT_STATUS_CMD);
2536 command.add(GIT_STATUS_FLAG_ALL_CMD);
2537 command.add(GIT_OPT_REPOSITORY);
2538 command.add(repository.getAbsolutePath());
2539 command.add(GIT_OPT_CWD_CMD);
2540 command.add(repository.getAbsolutePath());
2543 command.add(new File(cwd, filename).getAbsolutePath().substring(repository.getAbsolutePath().length()+1));
2545 return exec(command);
2549 * Gets git status command output list for the specified status flags for a given repository and directory
2551 private static List<String> doRepositoryDirStatusCmd(File repository, File dir, String statusFlags) throws GitException{
2552 List<String> command = new ArrayList<String>();
2554 command.add(getGitCommand());
2555 command.add(GIT_STATUS_CMD);
2557 command.add(statusFlags);
2558 command.add(GIT_OPT_REPOSITORY);
2559 command.add(repository.getAbsolutePath());
2560 command.add(GIT_OPT_CWD_CMD);
2561 command.add(repository.getAbsolutePath());
2562 if (dir != null) {
2563 command.add(dir.getAbsolutePath());
2564 } else {
2565 command.add(repository.getAbsolutePath());
2568 List<String> list = exec(command);
2569 if (!list.isEmpty() && isErrorNoRepository(list.get(0))) {
2570 OutputLogger logger = OutputLogger.getLogger(repository.getAbsolutePath());
2571 try {
2572 handleError(command, list, NbBundle.getMessage(GitCommand.class, "MSG_NO_REPOSITORY_ERR"), logger);
2573 } finally {
2574 logger.closeLog();
2577 return list;
2581 * Returns the ouput from the given command
2583 * @param command to execute
2584 * @return List of the command's output or an exception if one occured
2586 private static List<String> execEnv(List<String> command, List<String> env) throws GitException{
2587 if( EventQueue.isDispatchThread()){
2588 Git.LOG.log(Level.FINE, "WARNING execEnv(): calling Git command in AWT Thread - could stall UI"); // NOI18N
2589 } assert ( command != null && command.size() > 0);
2590 List<String> list = new ArrayList<String>();
2591 BufferedReader input = null;
2592 Process proc = null;
2593 try{
2594 if (command.size() > 10) {
2595 List<String> smallCommand = new ArrayList<String>();
2596 int count = 0;
2597 for (Iterator i = command.iterator(); i.hasNext();) {
2598 smallCommand.add((String)i.next());
2599 if (count++ > 10) break;
2601 Git.LOG.log(Level.FINE, "execEnv(): " + smallCommand); // NOI18N
2602 } else {
2603 Git.LOG.log(Level.FINE, "execEnv(): " + command); // NOI18N
2605 if(env != null && env.size() > 0){
2606 ProcessBuilder pb = new ProcessBuilder(command);
2607 Map<String, String> envOrig = pb.environment();
2608 for(String s: env){
2609 envOrig.put(s.substring(0,s.indexOf('=')), s.substring(s.indexOf('=')+1));
2611 proc = pb.start();
2612 }else{
2613 proc = new ProcessBuilder(command).start();
2616 input = new BufferedReader(new InputStreamReader(proc.getInputStream()));
2618 String line;
2619 while ((line = input.readLine()) != null){
2620 list.add(line);
2622 input.close();
2623 input = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
2624 while ((line = input.readLine()) != null){
2625 list.add(line);
2627 input.close();
2628 input = null;
2629 try {
2630 proc.waitFor();
2631 // By convention we assume that 255 (or -1) is a serious error.
2632 // For instance, the command line could be too long.
2633 if (proc.exitValue() == 255) {
2634 Git.LOG.log(Level.FINE, "execEnv(): process returned 255"); // NOI18N
2635 if (list.isEmpty()) {
2636 Git.LOG.log(Level.SEVERE, "command: " + command); // NOI18N
2637 throw new GitException(NbBundle.getMessage(GitCommand.class, "MSG_UNABLE_EXECUTE_COMMAND"));
2640 } catch (InterruptedException e) {
2641 Git.LOG.log(Level.FINE, "execEnv(): process interrupted " + e); // NOI18N
2643 }catch(InterruptedIOException e){
2644 // We get here is we try to cancel so kill the process
2645 Git.LOG.log(Level.FINE, "execEnv(): execEnv(): InterruptedIOException " + e); // NOI18N
2646 if (proc != null) {
2647 try {
2648 proc.getInputStream().close();
2649 proc.getOutputStream().close();
2650 proc.getErrorStream().close();
2651 } catch (IOException ioex) {
2652 //Just ignore. Closing streams.
2654 proc.destroy();
2656 throw new GitException(NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_CANCELLED"));
2657 }catch(IOException e){
2658 // Git does not seem to be returning error status != 0
2659 // even when it fails when for instance adding an already tracked file to
2660 // the repository - we will have to examine the output in the context of the
2661 // calling func and raise exceptions there if needed
2662 Git.LOG.log(Level.SEVERE, "execEnv(): execEnv(): IOException " + e); // NOI18N
2664 // Handle low level Git failures
2665 if (isErrorArgsTooLong(e.getMessage())){
2666 assert(command.size()> 2);
2667 throw new GitException(NbBundle.getMessage(GitCommand.class, "MSG_ARG_LIST_TOO_LONG_ERR",
2668 command.get(1), command.size() -2 ));
2669 }else if (isErrorNoGit(e.getMessage()) || isErrorCannotRun(e.getMessage())){
2670 throw new GitException(NbBundle.getMessage(Git.class, "MSG_VERSION_NONE_MSG"));
2671 }else{
2672 throw new GitException(NbBundle.getMessage(GitCommand.class, "MSG_UNABLE_EXECUTE_COMMAND"));
2674 }finally{
2675 if (input != null) {
2676 try {
2677 input.close();
2678 } catch (IOException ioex) {
2679 //Just ignore. Closing streams.
2681 input = null;
2684 return list;
2688 * Returns the ouput from the given command
2690 * @param command to execute
2691 * @return List of the command's output or an exception if one occured
2693 private static List<String> exec(List<String> command) throws GitException{
2694 if(!Git.getInstance().isGoodVersion()){
2695 return new ArrayList<String>();
2697 return execEnv(command, null);
2699 private static List<String> execForVersionCheck() throws GitException{
2700 List<String> command = new ArrayList<String>();
2701 command.add(getGitCommand());
2702 command.add(GIT_VERSION_CMD);
2704 return execEnv(command, null);
2707 private static String getGitCommand() {
2708 String defaultPath = GitModuleConfig.getDefault().getExecutableBinaryPath();
2709 if (defaultPath == null || defaultPath.length() == 0)
2710 return GIT_COMMAND;
2711 else
2712 return defaultPath + File.separatorChar + GIT_COMMAND;
2715 private static void handleError(List<String> command, List<String> list, String message, OutputLogger logger) throws GitException{
2716 if (command != null && list != null && logger != null){
2717 Git.LOG.log(Level.WARNING, "command: " + GitUtils.replaceHttpPassword(command)); // NOI18N
2718 Git.LOG.log(Level.WARNING, "output: " + GitUtils.replaceHttpPassword(list)); // NOI18N
2719 logger.outputInRed(NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_ERR")); // NOI18N
2720 logger.output(NbBundle.getMessage(GitCommand.class, "MSG_COMMAND_INFO_ERR",
2721 GitUtils.replaceHttpPassword(command), GitUtils.replaceHttpPassword(list))); // NOI18N
2724 if (list != null && (isErrorPossibleProxyIssue(list.get(0)) || isErrorPossibleProxyIssue(list.get(list.size() - 1)))) {
2725 boolean bConfirmSetProxy;
2726 bConfirmSetProxy = GitUtils.confirmDialog(GitCommand.class, "MSG_POSSIBLE_PROXY_ISSUE_TITLE", "MSG_POSSIBLE_PROXY_ISSUE_QUERY"); // NOI18N
2727 if(bConfirmSetProxy){
2728 OptionsDisplayer.getDefault().open("General"); // NOI18N
2730 } else {
2731 throw new GitException(message);
2735 public static boolean isMergeNeededMsg(String msg) {
2736 return msg.indexOf(GIT_MERGE_NEEDED_ERR) > -1; // NOI18N
2739 public static boolean isBackoutMergeNeededMsg(String msg) {
2740 return msg.indexOf(GIT_BACKOUT_MERGE_NEEDED_ERR) > -1; // NOI18N
2743 public static boolean isMergeConflictMsg(String msg) {
2744 if(Utilities.isWindows() ) {
2745 return (msg.indexOf(GIT_MERGE_CONFLICT_WIN1_ERR) > -1) && // NOI18N
2746 (msg.indexOf(GIT_MERGE_CONFLICT_WIN2_ERR) > -1); // NOI18N
2747 }else{
2748 return msg.indexOf(GIT_MERGE_CONFLICT_ERR) > -1; // NOI18N
2752 public static boolean isMergeUnavailableMsg(String msg) {
2753 return msg.indexOf(GIT_MERGE_UNAVAILABLE_ERR) > -1; // NOI18N
2756 public static boolean isMergeAbortMultipleHeadsMsg(String msg) {
2757 return msg.indexOf(GIT_MERGE_MULTIPLE_HEADS_ERR) > -1; // NOI18N
2759 public static boolean isMergeAbortUncommittedMsg(String msg) {
2760 return msg.indexOf(GIT_MERGE_UNCOMMITTED_ERR) > -1; // NOI18N
2763 public static boolean isNoChanges(String msg) {
2764 return msg.indexOf(GIT_NO_CHANGES_ERR) > -1; // NOI18N
2767 private static boolean isErrorNoDefaultPush(String msg) {
2768 return msg.indexOf(GIT_ABORT_NO_DEFAULT_PUSH_ERR) > -1; // NOI18N
2771 private static boolean isErrorNoDefaultPath(String msg) {
2772 return msg.indexOf(GIT_ABORT_NO_DEFAULT_ERR) > -1; // NOI18N
2775 private static boolean isErrorPossibleProxyIssue(String msg) {
2776 return msg.indexOf(GIT_ABORT_POSSIBLE_PROXY_ERR) > -1; // NOI18N
2779 private static boolean isErrorNoRepository(String msg) {
2780 return msg.indexOf(GIT_NO_REPOSITORY_ERR) > -1 ||
2781 msg.indexOf(GIT_NOT_REPOSITORY_ERR) > -1 ||
2782 (msg.indexOf(GIT_REPOSITORY) > -1 && msg.indexOf(GIT_NOT_FOUND_ERR) > -1); // NOI18N
2785 private static boolean isErrorNoGit(String msg) {
2786 return msg.indexOf(GIT_NO_GIT_CMD_FOUND_ERR) > -1; // NOI18N
2789 private static boolean isErrorArgsTooLong(String msg) {
2790 return msg.indexOf(GIT_ARG_LIST_TOO_LONG_ERR) > -1; // NOI18N
2793 private static boolean isErrorCannotRun(String msg) {
2794 return msg.indexOf(GIT_CANNOT_RUN_ERR) > -1; // NOI18N
2797 private static boolean isErrorUpdateSpansBranches(String msg) {
2798 return msg.indexOf(GIT_UPDATE_SPAN_BRANCHES_ERR) > -1; // NOI18N
2801 private static boolean isErrorAlreadyTracked(String msg) {
2802 return msg.indexOf(GIT_ALREADY_TRACKED_ERR) > -1; // NOI18N
2805 private static boolean isErrorNotTracked(String msg) {
2806 return msg.indexOf(GIT_NOT_TRACKED_ERR) > -1; // NOI18N
2809 private static boolean isErrorNotFound(String msg) {
2810 return msg.indexOf(GIT_NOT_FOUND_ERR) > -1; // NOI18N
2813 private static boolean isErrorCannotReadCommitMsg(String msg) {
2814 return msg.indexOf(GIT_CANNOT_READ_COMMIT_MESSAGE_ERR) > -1; // NOI18N
2817 private static boolean isErrorAbort(String msg) {
2818 return msg.indexOf(GIT_ABORT_ERR) > -1; // NOI18N
2821 public static boolean isErrorAbortPush(String msg) {
2822 return msg.indexOf(GIT_ABORT_PUSH_ERR) > -1; // NOI18N
2825 public static boolean isErrorAbortNoFilesToCopy(String msg) {
2826 return msg.indexOf(GIT_ABORT_NO_FILES_TO_COPY_ERR) > -1; // NOI18N
2829 private static boolean isErrorNoChangeNeeded(String msg) {
2830 return msg.indexOf(GIT_NO_CHANGE_NEEDED_ERR) > -1; // NOI18N
2833 public static boolean isCreateNewBranch(String msg) {
2834 return msg.indexOf(GIT_CREATE_NEW_BRANCH_ERR) > -1; // NOI18N
2837 public static boolean isHeadsCreated(String msg) {
2838 return msg.indexOf(GIT_HEADS_CREATED_ERR) > -1; // NOI18N
2841 public static boolean isNoRollbackPossible(String msg) {
2842 return msg.indexOf(GIT_NO_ROLLBACK_ERR) > -1; // NOI18N
2844 public static boolean isNoRevStrip(String msg) {
2845 return msg.indexOf(GIT_NO_REV_STRIP_ERR) > -1; // NOI18N
2847 public static boolean isLocalChangesStrip(String msg) {
2848 return msg.indexOf(GIT_LOCAL_CHANGES_STRIP_ERR) > -1; // NOI18N
2850 public static boolean isMultipleHeadsStrip(String msg) {
2851 return msg.indexOf(GIT_MULTIPLE_HEADS_STRIP_ERR) > -1; // NOI18N
2853 public static boolean isUncommittedChangesBackout(String msg) {
2854 return msg.indexOf(GIT_ABORT_UNCOMMITTED_CHANGES_ERR) > -1; // NOI18N
2856 public static boolean isMergeChangesetBackout(String msg) {
2857 return msg.indexOf(GIT_ABORT_BACKOUT_MERGE_CSET_ERR) > -1; // NOI18N
2860 public static boolean isNoUpdates(String msg) {
2861 return msg.indexOf(GIT_NO_UPDATES_ERR) > -1; // NOI18N
2864 private static boolean isErrorNoView(String msg) {
2865 return msg.indexOf(GIT_NO_VIEW_ERR) > -1; // NOI18N
2868 private static boolean isErrorGitkNotFound(String msg) {
2869 return msg.indexOf(GIT_GITK_NOT_FOUND_ERR) > -1; // NOI18N
2872 private static boolean isErrorNoSuchFile(String msg) {
2873 return msg.indexOf(GIT_NO_SUCH_FILE_ERR) > -1; // NOI18N
2876 private static boolean isErrorNoResponse(String msg) {
2877 return msg.indexOf(GIT_NO_RESPONSE_ERR) > -1; // NOI18N
2880 public static void createConflictFile(String path) {
2881 try {
2882 File file = new File(path + GIT_STR_CONFLICT_EXT);
2884 boolean success = file.createNewFile();
2885 Git.LOG.log(Level.FINE, "createConflictFile(): File: {0} {1}", // NOI18N
2886 new Object[] {path + GIT_STR_CONFLICT_EXT, success? "Created": "Not Created"} ); // NOI18N
2887 } catch (IOException e) {
2891 public static void deleteConflictFile(String path) {
2892 boolean success = (new File(path + GIT_STR_CONFLICT_EXT)).delete();
2894 Git.LOG.log(Level.FINE, "deleteConflictFile(): File: {0} {1}", // NOI18N
2895 new Object[] {path + GIT_STR_CONFLICT_EXT, success? "Deleted": "Not Deleted"} ); // NOI18N
2898 public static boolean existsConflictFile(String path) {
2899 File file = new File(path + GIT_STR_CONFLICT_EXT);
2900 boolean bExists = file.canWrite();
2902 if (bExists) {
2903 Git.LOG.log(Level.FINE, "existsConflictFile(): File: {0} {1}", // NOI18N
2904 new Object[] {path + GIT_STR_CONFLICT_EXT, "Exists"} ); // NOI18N
2906 return bExists;
2910 * This utility class should not be instantiated anywhere.
2912 private GitCommand() {