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]"
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
;
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
;
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
;
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());
297 env
.add(GIT_MERGE_ENV
);
299 List
<String
> list
= execEnv(command
, env
);
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"));
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)
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
);
371 handleError(command
, list
, NbBundle
.getMessage(GitCommand
.class, "MSG_ROLLBACK_FAILED"), logger
);
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
);
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
);
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
);
405 list
= execEnv(command
, env
);
407 list
= exec(command
);
410 handleError(command
, list
, NbBundle
.getMessage(GitCommand
.class, "MSG_BACKOUT_FAILED"), logger
);
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
);
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
);
438 handleError(command
, list
, NbBundle
.getMessage(GitCommand
.class, "MSG_STRIP_FAILED"), logger
);
444 * Returns the version of Git, e.g. 1.5.3.7.
448 public static String
getGitVersion() {
449 List
<String
> list
= new LinkedList
<String
>();
451 list
= execForVersionCheck();
452 } catch (GitException ex
) {
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
);
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());
503 String defaultPull
= new GitConfigFiles(repository
).getDefaultPull(false);
504 String proxy
= getGlobalProxyIfNeeded(defaultPull
, true, logger
);
506 List
<String
> env
= new ArrayList
<String
>();
507 env
.add(GIT_PROXY_ENV
+ proxy
);
508 list
= execEnv(command
, env
);
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
);
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
);
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());
592 String defaultPull
= new GitConfigFiles(repository
).getDefaultPull(false);
593 String proxy
= getGlobalProxyIfNeeded(defaultPull
, false, null);
595 List
<String
> env
= new ArrayList
<String
>();
596 env
.add(GIT_PROXY_ENV
+ proxy
);
597 list
= execEnv(command
, env
);
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
);
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());
630 String defaultPush
= new GitConfigFiles(repository
).getDefaultPush(false);
631 String proxy
= getGlobalProxyIfNeeded(defaultPush
, false, null);
633 List
<String
> env
= new ArrayList
<String
>();
634 env
.add(GIT_PROXY_ENV
+ proxy
);
635 list
= execEnv(command
, env
);
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
);
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());
666 String defaultPush
= new GitConfigFiles(repository
).getDefaultPush(false);
667 String proxy
= getGlobalProxyIfNeeded(defaultPush
, true, logger
);
669 List
<String
> env
= new ArrayList
<String
>();
670 env
.add(GIT_PROXY_ENV
+ proxy
);
671 list
= execEnv(command
, env
);
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
);
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());
703 if(GitUtils
.isSolaris()){
704 env
.add(GIT_GITK_PATH_SOLARIS10_ENV
);
705 list
= execEnv(command
, env
);
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
);
723 static File
getRootFile(VCSContext ctx
) {
724 throw new UnsupportedOperationException("Not yet implemented");
727 private static String
getGlobalProxyIfNeeded(String defaultPath
, boolean bOutputDetails
, OutputLogger logger
){
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
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
751 if(proxy
!= null && bOutputDetails
){
752 logger
.output(NbBundle
.getMessage(GitCommand
.class, "MSG_USING_PROXY_INFO", proxy
)); // NOI18N
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());
774 String defaultPull
= new GitConfigFiles(repository
).getDefaultPull(false);
775 String proxy
= getGlobalProxyIfNeeded(defaultPull
, true, logger
);
777 List
<String
> env
= new ArrayList
<String
>();
778 env
.add(GIT_PROXY_ENV
+ proxy
);
779 list
= execEnv(command
, env
);
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
);
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) {
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;
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
);
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
);
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
);
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
);
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
);
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
);
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;
909 List
<String
> list
= getHeadRevisions(repository
);
911 if (!list
.isEmpty() && list
.size() > 1){
912 Git
.LOG
.log(Level
.FINE
, "isMergeRequired(): TRUE " + list
); // NOI18N
915 Git
.LOG
.log(Level
.FINE
, "isMergeRequired(): FALSE " + list
); // NOI18N
918 } catch (GitException e
) {
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());
941 List
<String
> list
= exec(command
);
942 if (!list
.isEmpty() && isErrorAbort(list
.get(0)))
945 return !list
.isEmpty();
946 } catch (GitException e
) {
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
);
982 list
= exec(command
);
983 if (!list
.isEmpty() && isErrorAbort(list
.get(0)))
985 } catch (GitException e
) {
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]);
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());
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
);
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;
1082 for (File f
: files
) {
1083 if (f
.isDirectory()) {
1090 command
.add(GIT_OPT_FOLLOW
);
1092 command
.add(GIT_OPT_REPOSITORY
);
1093 command
.add(repository
.getAbsolutePath());
1095 command
.add(GIT_LOG_DEBUG_CMD
);
1097 if(LOG_TEMPLATE
!= null){
1098 command
.add(LOG_TEMPLATE
);
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
);
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;
1139 for (File f
: files
) {
1140 if (f
.isDirectory()) {
1147 command
.add(GIT_OPT_FOLLOW
);
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
);
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
);
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());
1201 command
.add(GIT_LOG_NO_MERGES_CMD
);
1203 command
.add(GIT_LOG_DEBUG_CMD
);
1205 command
.add(GIT_LOG_TEMPLATE_HISTORY_CMD
);
1208 String defaultPush
= new GitConfigFiles(repository
).getDefaultPush(false);
1209 String proxy
= getGlobalProxyIfNeeded(defaultPush
, false, null);
1211 List
<String
> env
= new ArrayList
<String
>();
1212 env
.add(GIT_PROXY_ENV
+ proxy
);
1213 list
= execEnv(command
, env
);
1215 list
= exec(command
);
1217 if (!list
.isEmpty()) {
1218 if(isErrorNoDefaultPush(list
.get(0))){
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
);
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());
1246 command
.add(GIT_LOG_NO_MERGES_CMD
);
1248 command
.add(GIT_LOG_DEBUG_CMD
);
1249 String revStr
= handleIncomingRevNumber(to
);
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);
1260 List
<String
> env
= new ArrayList
<String
>();
1261 env
.add(GIT_PROXY_ENV
+ proxy
);
1262 list
= execEnv(command
, env
);
1264 list
= exec(command
);
1267 if (!list
.isEmpty()) {
1268 if (isErrorNoDefaultPath(list
.get(0))) {
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
);
1279 private static String
handleRevDates(String from
, String to
){
1280 // Check for Date range:
1281 Date fromDate
= null;
1283 Date currentDate
= new Date(); // Current Date
1284 Date epochPlusOneDate
= null;
1287 epochPlusOneDate
= new SimpleDateFormat("yyyy-MM-dd").parse(GIT_EPOCH_PLUS_ONE_YEAR
); // NOI18N
1288 } catch (ParseException ex
) {
1289 // Ignore invalid dates
1295 fromDate
= new SimpleDateFormat("yyyy-MM-dd").parse(from
); // NOI18N
1296 } catch (ParseException ex
) {
1297 // Ignore invalid dates
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
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
1345 return from
+ " to " + to
; // NOI18N
1350 private static String
handleIncomingRevNumber(String to
) {
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
;
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
){
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
)))
1374 if(to
!= null && (to
.equalsIgnoreCase(GIT_STATUS_FLAG_TIP_CMD
) || to
.equalsIgnoreCase(GIT_HEAD_STR
)))
1378 fromInt
= Integer
.parseInt(from
);
1379 }catch (NumberFormatException e
){
1380 // ignore invalid numbers
1383 toInt
= Integer
.parseInt(to
);
1384 }catch (NumberFormatException e
){
1385 // ignore invalid numbers
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
) {
1398 if (headRevInt
> -1 && fromInt
> headRevInt
) {
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){
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
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();
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));
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
);
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
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;
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
);
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
);
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"));
1627 if (commitMessage
!= null && tempfile
!= null){
1635 * Rename a source file to a destination file.
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
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.
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
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
1693 * @param File repository of the Git repository's root directory
1694 * @param List<Files> of files to be added to Git
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
){
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
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
);
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
1761 * @param File repository of the Git repository's root directory
1762 * @param File of file to be added to Git
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
);
1822 list
= doAnnotate(repository
, file
, rev
, logger
);
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
);
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
);
1861 for (File file
: files
) {
1862 command
.add(file
.getAbsolutePath());
1866 List
<String
> list
= new ArrayList
<String
>();
1868 list
= exec(command
);
1869 } catch (GitException ex
) {
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());
1921 List
<String
> list
= new LinkedList
<String
>();
1923 list
= exec(command
);
1924 } catch (GitException ex
) {
1928 && (!isErrorNotFound(list
.get(0)))) {
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()){
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));
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()){
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()){
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
);
2126 command
.add(file
.getAbsolutePath());
2128 List
<String
> list
= exec(command
);
2129 if (!list
.isEmpty()) {
2130 return new StringBuffer(list
.get(0)).toString();
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)} );
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
} );
2186 * Returns the Git status for all files in a given subdirectory of
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
)) {
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();
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
);
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
);
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
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
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;
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
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
} );
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);
2490 case GIT_STATUS_CODE_MODIFIED
:
2491 info
= new FileInformation(FileInformation
.STATUS_VERSIONED_MODIFIEDLOCALLY
,null, false);
2493 case GIT_STATUS_CODE_ADDED
:
2494 info
= new FileInformation(FileInformation
.STATUS_VERSIONED_ADDEDLOCALLY
,null, false);
2496 case GIT_STATUS_CODE_REMOVED
:
2497 info
= new FileInformation(FileInformation
.STATUS_VERSIONED_REMOVEDLOCALLY
,null, false);
2499 case GIT_STATUS_CODE_CLEAN
:
2500 info
= new FileInformation(FileInformation
.STATUS_VERSIONED_UPTODATE
,null, false);
2502 case GIT_STATUS_CODE_DELETED
:
2503 info
= new FileInformation(FileInformation
.STATUS_VERSIONED_DELETEDLOCALLY
,null, false);
2505 case GIT_STATUS_CODE_IGNORED
:
2506 info
= new FileInformation(FileInformation
.STATUS_NOTVERSIONED_EXCLUDED
,null, false);
2508 case GIT_STATUS_CODE_NOTTRACKED
:
2509 info
= new FileInformation(FileInformation
.STATUS_NOTVERSIONED_NEWLOCALLY
,null, false);
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);
2515 case GIT_STATUS_CODE_ABORT
:
2516 info
= new FileInformation(FileInformation
.STATUS_NOTVERSIONED_NOTMANAGED
,null, false);
2519 info
= new FileInformation(FileInformation
.STATUS_UNKNOWN
,null, false);
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());
2563 command
.add(dir
.getAbsolutePath());
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());
2572 handleError(command
, list
, NbBundle
.getMessage(GitCommand
.class, "MSG_NO_REPOSITORY_ERR"), logger
);
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;
2594 if (command
.size() > 10) {
2595 List
<String
> smallCommand
= new ArrayList
<String
>();
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
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();
2609 envOrig
.put(s
.substring(0,s
.indexOf('=')), s
.substring(s
.indexOf('=')+1));
2613 proc
= new ProcessBuilder(command
).start();
2616 input
= new BufferedReader(new InputStreamReader(proc
.getInputStream()));
2619 while ((line
= input
.readLine()) != null){
2623 input
= new BufferedReader(new InputStreamReader(proc
.getErrorStream()));
2624 while ((line
= input
.readLine()) != null){
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
2648 proc
.getInputStream().close();
2649 proc
.getOutputStream().close();
2650 proc
.getErrorStream().close();
2651 } catch (IOException ioex
) {
2652 //Just ignore. Closing streams.
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"));
2672 throw new GitException(NbBundle
.getMessage(GitCommand
.class, "MSG_UNABLE_EXECUTE_COMMAND"));
2675 if (input
!= null) {
2678 } catch (IOException ioex
) {
2679 //Just ignore. Closing streams.
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)
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
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
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
) {
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();
2903 Git
.LOG
.log(Level
.FINE
, "existsConflictFile(): File: {0} {1}", // NOI18N
2904 new Object
[] {path
+ GIT_STR_CONFLICT_EXT
, "Exists"} ); // NOI18N
2910 * This utility class should not be instantiated anywhere.
2912 private GitCommand() {