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.
30 * If you wish your version of this file to be governed by only the CDDL
31 * or only the GPL Version 2, indicate your decision by adding
32 * "[Contributor] elects to include this software in this distribution
33 * under the [CDDL or GPL Version 2] license." If you do not indicate a
34 * single choice of license, a recipient has the option to distribute
35 * your version of this file under either the CDDL, the GPL Version 2 or
36 * to extend the choice of license to its licensees as provided above.
37 * However, if you add GPL Version 2 code and therefore, elected the GPL
38 * Version 2 license, then the option applies only if the new code is
39 * made subject to such option by the copyright holder.
41 package org
.nbgit
.util
;
43 import org
.nbgit
.OutputLogger
;
44 import org
.nbgit
.StatusInfo
;
46 import java
.io
.FileOutputStream
;
47 import java
.io
.IOException
;
48 import java
.io
.UnsupportedEncodingException
;
49 import java
.util
.ArrayList
;
50 import java
.util
.HashMap
;
51 import java
.util
.List
;
55 import org
.nbgit
.ui
.log
.RepositoryRevision
;
56 import org
.netbeans
.api
.queries
.SharabilityQuery
;
57 import org
.openide
.util
.Exceptions
;
58 import org
.spearce
.jgit
.lib
.Commit
;
59 import org
.spearce
.jgit
.lib
.Constants
;
60 import org
.spearce
.jgit
.lib
.GitIndex
;
61 import org
.spearce
.jgit
.lib
.GitIndex
.Entry
;
62 import org
.spearce
.jgit
.lib
.IndexDiff
;
63 import org
.spearce
.jgit
.lib
.ObjectId
;
64 import org
.spearce
.jgit
.lib
.ObjectLoader
;
65 import org
.spearce
.jgit
.lib
.ObjectWriter
;
66 import org
.spearce
.jgit
.lib
.PersonIdent
;
67 import org
.spearce
.jgit
.lib
.RefUpdate
;
68 import org
.spearce
.jgit
.lib
.Repository
;
69 import org
.spearce
.jgit
.lib
.Tree
;
70 import org
.spearce
.jgit
.lib
.TreeEntry
;
71 import org
.spearce
.jgit
.revwalk
.RevCommit
;
72 import org
.spearce
.jgit
.revwalk
.RevWalk
;
73 import org
.spearce
.jgit
.revwalk
.filter
.RevFilter
;
74 import org
.spearce
.jgit
.treewalk
.FileTreeIterator
;
75 import org
.spearce
.jgit
.treewalk
.TreeWalk
;
76 import org
.spearce
.jgit
.treewalk
.filter
.PathFilter
;
77 import org
.spearce
.jgit
.treewalk
.filter
.PathFilterGroup
;
82 public class GitCommand
{
84 public static void deleteConflictFile(String absolutePath
)
86 // boolean success = (new File(path + GIT_STR_CONFLICT_EXT)).delete();
87 throw new UnsupportedOperationException("Not yet implemented");
90 static public void doAdd(File root
, List
<File
> addCandidates
, OutputLogger logger
)
93 Repository repo
= Git
.getInstance().getRepository(root
);
94 GitIndex index
= repo
.getIndex();
96 for (File dstFile
: addCandidates
) {
97 Entry entry
= index
.add(root
, dstFile
);
98 entry
.setAssumeValid(false);
103 } catch (Exception ex
) {
104 logger
.output(ex
.getMessage());
108 public static void doAdd(File root
, File dstFile
, OutputLogger logger
)
111 Repository repo
= Git
.getInstance().getRepository(root
);
112 GitIndex index
= repo
.getIndex();
113 Entry entry
= index
.add(root
, dstFile
);
115 entry
.setAssumeValid(false);
118 } catch (Exception ex
) {
119 logger
.output(ex
.getMessage());
123 public static void doCat(File root
, File base
, File tempFile
, String revision
)
125 OutputLogger logger
= Git
.getInstance().getLogger(root
.getAbsolutePath());
127 Repository repo
= Git
.getInstance().getRepository(root
);
128 Commit commit
= repo
.mapCommit(revision
);
130 if (commit
== null) {
131 logger
.output("Revision " + revision
+ " not found");
135 String relative
= toGitPath(getRelative(root
, base
));
137 ObjectId
[] ids
= { commit
.getTree().getId() };
138 TreeWalk walk
= TreeWalk
.forPath(repo
, relative
, ids
);
140 ObjectId blobId
= walk
.getObjectId(0);
142 ObjectLoader blob
= repo
.openBlob(blobId
);
144 logger
.output("Failed to open blob " + blobId
);
148 FileOutputStream out
= new FileOutputStream(tempFile
);
149 out
.write(blob
.getCachedBytes());
153 } catch (Exception ex
) {
154 // FIXME: Do unload, delete, ... here
155 logger
.output(ex
.getMessage());
159 public static void doRevert(File root
, List
<File
> files
, String revStr
, boolean doBackup
, OutputLogger logger
)
161 Repository repo
= Git
.getInstance().getRepository(root
);
164 GitIndex index
= new GitIndex(repo
);
165 Tree tree
= repo
.mapTree(revStr
);
166 index
.readTree(tree
);
168 for (File file
: files
) {
169 String path
= getRelative(root
, file
);
170 Entry entry
= index
.getEntry(path
);
175 file
.renameTo(new File(file
.getAbsolutePath() + ".orig"));
176 index
.checkoutEntry(root
, entry
);
179 } catch (IOException ex
) {
183 public static List
<String
> doUpdateAll(File root
, boolean doForcedUpdate
, String revStr
)
185 throw new UnsupportedOperationException("Not yet implemented");
188 public static RepositoryRevision
.Walk
getLogMessages(String rootPath
, Set
<File
> files
, String fromRevision
, String toRevision
, boolean showMerges
, OutputLogger logger
)
190 File root
= new File(rootPath
);
191 Repository repo
= Git
.getInstance().getRepository(root
);
192 RepositoryRevision
.Walk walk
= new RepositoryRevision
.Walk(repo
);
195 if (fromRevision
== null)
196 fromRevision
= Constants
.HEAD
;
197 ObjectId from
= repo
.resolve(fromRevision
);
200 walk
.markStart(walk
.parseCommit(from
));
201 ObjectId to
= toRevision
!= null ? repo
.resolve(toRevision
) : null;
203 walk
.markUninteresting(walk
.parseCommit(to
));
205 List
<PathFilter
> paths
= new ArrayList
<PathFilter
>();
206 for (File file
: files
) {
207 String path
= getRelative(root
, file
);
209 if (!(path
.length() == 0))
210 paths
.add(PathFilter
.create(path
));
213 if (!paths
.isEmpty())
214 walk
.setTreeFilter(PathFilterGroup
.create(paths
));
216 walk
.setRevFilter(RevFilter
.NO_MERGES
);
218 } catch (IOException ioe
) {
225 public static boolean isNoUpdates(String get
)
227 throw new UnsupportedOperationException("Not yet implemented");
230 private static String
buildReflogMessage(String commitMessage
)
232 String firstLine
= commitMessage
;
233 int newlineIndex
= commitMessage
.indexOf("\n");
235 if (newlineIndex
> 0)
236 firstLine
= commitMessage
.substring(0, newlineIndex
);
237 return "\tcommit: " + firstLine
;
240 private static void prepareTrees(File root
, List
<File
> selectedItems
,
241 HashMap
<Repository
, Tree
> treeMap
)
242 throws IOException
, UnsupportedEncodingException
244 Repository repo
= Git
.getInstance().getRepository(root
);
246 for (File file
: selectedItems
) {
247 Tree projTree
= treeMap
.get(repo
);
248 if (projTree
== null) {
249 projTree
= repo
.mapTree("HEAD");
250 if (projTree
== null)
251 projTree
= new Tree(repo
);
252 treeMap
.put(repo
, projTree
);
253 System
.out
.println("Orig tree id: " + projTree
.getId());
256 GitIndex index
= repo
.getIndex();
257 String repoRelativePath
= getRelative(root
, file
);
258 String string
= repoRelativePath
;
260 TreeEntry treeMember
= projTree
.findBlobMember(repoRelativePath
);
261 // we always want to delete it from the current tree, since if it's
262 // updated, we'll add it again
263 if (treeMember
!= null)
265 Entry idxEntry
= index
.getEntry(string
);
266 if (true /* if modified on disk or missing */) {
267 if (!file
.isFile()) {
268 index
.remove(root
, file
);
270 System
.out
.println("Phantom file, so removing from index");
274 if (idxEntry
.update(file
))
278 if (idxEntry
!= null) {
279 projTree
.addFile(repoRelativePath
);
280 TreeEntry newMember
= projTree
.findBlobMember(repoRelativePath
);
282 newMember
.setId(idxEntry
.getObjectId());
283 System
.out
.println("New member id for " + repoRelativePath
+ ": " + newMember
.getId() + " idx id: " + idxEntry
.getObjectId());
288 private static void writeTreeWithSubTrees(Tree tree
) throws IOException
290 if (tree
.getId() == null) {
291 System
.out
.println("writing tree for: " + tree
.getFullName());
293 for (TreeEntry entry
: tree
.members()) {
294 if (entry
.isModified())
295 if (entry
instanceof Tree
)
296 writeTreeWithSubTrees((Tree
) entry
);
298 System
.out
.println("BAD JUJU: " + entry
.getFullName());
300 ObjectWriter writer
= new ObjectWriter(tree
.getRepository());
301 tree
.setId(writer
.writeTree(tree
));
305 public static void doCommit(File root
, List
<File
> commitCandidates
, String message
, OutputLogger logger
) throws IOException
307 HashMap
<Repository
, Tree
> treeMap
= new HashMap
<Repository
, Tree
>();
309 prepareTrees(root
, commitCandidates
, treeMap
);
311 for (java
.util
.Map
.Entry
<Repository
, Tree
> entry
: treeMap
.entrySet()) {
313 Tree tree
= entry
.getValue();
314 Repository repo
= tree
.getRepository();
315 PersonIdent personIdent
= new PersonIdent(repo
);
317 writeTreeWithSubTrees(tree
);
319 final RefUpdate ru
= repo
.updateRef(Constants
.HEAD
);
320 ObjectId
[] parentIds
;
321 if (ru
.getOldObjectId() != null)
322 parentIds
= new ObjectId
[] { ru
.getOldObjectId() };
324 parentIds
= new ObjectId
[0];
325 Commit commit
= new Commit(repo
, parentIds
);
326 commit
.setTree(tree
);
327 message
= message
.replaceAll("\r", "\n");
329 commit
.setMessage(message
);
330 commit
.setAuthor(personIdent
);
331 commit
.setCommitter(personIdent
);
333 ObjectWriter writer
= new ObjectWriter(repo
);
334 commit
.setCommitId(writer
.writeCommit(commit
));
336 ru
.setNewObjectId(commit
.getCommitId());
337 ru
.setRefLogMessage(buildReflogMessage(message
), false);
340 if (ru
.getOldObjectId() != null)
341 ok
= ru
.getResult() == RefUpdate
.Result
.FAST_FORWARD
;
343 ok
= ru
.getResult() == RefUpdate
.Result
.NEW
;
346 logger
.output("Failed to update " + ru
.getName() + " to commit " + commit
.getCommitId() + ".");
350 public static void doRemove(File root
, List
<File
> deleteCandidates
, OutputLogger logger
)
353 Repository repo
= Git
.getInstance().getRepository(root
);
354 GitIndex index
= repo
.getIndex();
355 boolean dirty
= false;
357 for (File srcFile
: deleteCandidates
) {
358 if (index
.remove(root
, srcFile
))
364 } catch (Exception ex
) {
365 logger
.output(ex
.getMessage());
370 public static void doRemove(File root
, File srcFile
, OutputLogger logger
)
373 Repository repo
= Git
.getInstance().getRepository(root
);
374 GitIndex index
= repo
.getIndex();
376 if (index
.remove(root
, srcFile
))
378 } catch (Exception ex
) {
379 logger
.output(ex
.getMessage());
384 public static void doRenameAfter(File root
, File srcFile
, File dstFile
, OutputLogger logger
)
386 throw new UnsupportedOperationException("Not yet implemented");
389 public static List
<String
[]> getRevisions(File root
, int limit
)
391 return getRevisionsForFile(root
, null, limit
);
394 public static List
<String
[]> getRevisionsForFile(File root
, File
[] files
, int limit
)
396 Repository repo
= Git
.getInstance().getRepository(root
);
397 RevWalk walk
= new RevWalk(repo
);
398 List
<String
[]> revs
= new ArrayList
<String
[]>();
401 ObjectId from
= repo
.resolve(Constants
.HEAD
);
404 walk
.markStart(walk
.parseCommit(from
));
407 List
<PathFilter
> paths
= new ArrayList
<PathFilter
>();
408 for (File file
: files
) {
409 String path
= getRelative(root
, file
);
411 if (!(path
.length() == 0))
412 paths
.add(PathFilter
.create(path
));
415 if (!paths
.isEmpty())
416 walk
.setTreeFilter(PathFilterGroup
.create(paths
));
419 for (RevCommit rev
: walk
) {
420 revs
.add(new String
[] { rev
.getShortMessage(), rev
.getId().toString() });
425 } catch (IOException ioe
) {
431 private static String
getRelative(File root
, File dir
)
433 return getRelative(root
.getAbsolutePath(), dir
.getAbsolutePath());
436 private static String
getRelative(String root
, String dir
)
438 if (dir
.equals(root
))
440 return dir
.replace(root
+ File
.separator
, "");
443 private static String
toGitPath(String path
)
445 return File
.separatorChar
== '/'
446 ? path
: path
.replace(File
.separator
, "/");
449 private static void put(Set
<String
> set
, String relPath
,
450 Map
<File
, StatusInfo
> files
, File root
, int status
)
452 for (String path
: set
) {
453 if (relPath
.length() > 0 && !path
.startsWith(relPath
))
455 File file
= new File(root
, path
);
456 files
.put(file
, new StatusInfo(status
, null, false));
472 public static Map
<File
, StatusInfo
> getAllStatus(File root
, File dir
) throws IOException
474 String relPath
= getRelative(root
, dir
);
475 OutputLogger logger
= Git
.getInstance().getLogger(root
.getAbsolutePath());
477 Repository repo
= Git
.getInstance().getRepository(root
);
478 Map
<File
, StatusInfo
> files
= new HashMap
<File
, StatusInfo
>();
481 repo
.refreshFromDisk();
482 IndexDiff index
= new IndexDiff(repo
);
485 put(index
.getAdded(), relPath
, files
, root
,
486 StatusInfo
.STATUS_VERSIONED_ADDEDLOCALLY
);
487 put(index
.getRemoved(), relPath
, files
, root
,
488 StatusInfo
.STATUS_VERSIONED_REMOVEDLOCALLY
);
489 put(index
.getMissing(), relPath
, files
, root
,
490 StatusInfo
.STATUS_VERSIONED_DELETEDLOCALLY
);
491 put(index
.getChanged(), relPath
, files
, root
,
492 StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
);
493 put(index
.getModified(), relPath
, files
, root
,
494 StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
);
496 final FileTreeIterator workTree
= new FileTreeIterator(repo
.getWorkDir());
497 final TreeWalk walk
= new TreeWalk(repo
);
499 walk
.reset(); // drop the first empty tree
500 walk
.setRecursive(true);
501 walk
.addTree(workTree
);
503 int share
= SharabilityQuery
.getSharability(dir
);
504 if (share
== SharabilityQuery
.NOT_SHARABLE
)
507 while (walk
.next()) {
508 String path
= walk
.getPathString();
510 if (relPath
.length() > 0 && !path
.startsWith(relPath
))
512 if (index
.getAdded().contains(path
) ||
513 index
.getRemoved().contains(path
) ||
514 index
.getMissing().contains(path
) ||
515 index
.getChanged().contains(path
) ||
516 index
.getModified().contains(path
))
518 Entry entry
= repo
.getIndex().getEntry(path
);
519 File file
= new File(root
, path
);
523 status
= StatusInfo
.STATUS_VERSIONED_UPTODATE
;
525 if (share
== SharabilityQuery
.MIXED
&&
526 SharabilityQuery
.getSharability(file
) == SharabilityQuery
.NOT_SHARABLE
)
529 status
= StatusInfo
.STATUS_NOTVERSIONED_NEWLOCALLY
;
532 files
.put(file
, new StatusInfo(status
, null, false));
535 } catch (IOException ex
) {
536 Exceptions
.printStackTrace(ex
);
550 public static Map
<File
, StatusInfo
> getInterestingStatus(File root
, File dir
)
552 String relPath
= getRelative(root
, dir
);
554 Repository repo
= Git
.getInstance().getRepository(root
);
557 Map
<File
, StatusInfo
> files
= new HashMap
<File
, StatusInfo
>();
560 repo
.refreshFromDisk();
561 index
= new IndexDiff(repo
);
564 put(index
.getAdded(), relPath
, files
, root
,
565 StatusInfo
.STATUS_VERSIONED_ADDEDLOCALLY
);
566 put(index
.getRemoved(), relPath
, files
, root
,
567 StatusInfo
.STATUS_VERSIONED_REMOVEDLOCALLY
);
568 put(index
.getMissing(), relPath
, files
, root
,
569 StatusInfo
.STATUS_VERSIONED_DELETEDLOCALLY
);
570 put(index
.getChanged(), relPath
, files
, root
,
571 StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
);
572 put(index
.getModified(), relPath
, files
, root
,
573 StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
);
575 final FileTreeIterator workTree
= new FileTreeIterator(repo
.getWorkDir());
576 final TreeWalk walk
= new TreeWalk(repo
);
578 walk
.reset(); // drop the first empty tree
579 walk
.setRecursive(true);
580 walk
.addTree(workTree
);
582 while (walk
.next()) {
583 String path
= walk
.getPathString();
585 if (relPath
.length() > 0 && !path
.startsWith(relPath
))
587 if (index
.getAdded().contains(path
) ||
588 index
.getRemoved().contains(path
) ||
589 index
.getMissing().contains(path
) ||
590 index
.getChanged().contains(path
) ||
591 index
.getModified().contains(path
))
593 Entry entry
= repo
.getIndex().getEntry(path
);
596 int status
= StatusInfo
.STATUS_NOTVERSIONED_NEWLOCALLY
;
597 File file
= new File(root
, path
);
598 files
.put(file
, new StatusInfo(status
, null, false));
601 } catch (IOException ex
) {
602 Exceptions
.printStackTrace(ex
);
608 public static StatusInfo
getSingleStatus(File root
, File file
)
610 OutputLogger logger
= Git
.getInstance().getLogger(root
.getAbsolutePath());
611 Repository repo
= Git
.getInstance().getRepository(root
);
614 int share
= SharabilityQuery
.getSharability(file
.getParentFile());
615 if (share
== SharabilityQuery
.NOT_SHARABLE
||
616 (share
== SharabilityQuery
.MIXED
&&
617 SharabilityQuery
.getSharability(file
) == SharabilityQuery
.NOT_SHARABLE
))
618 return new StatusInfo(StatusInfo
.STATUS_NOTVERSIONED_EXCLUDED
, null, false);
620 int status
= StatusInfo
.STATUS_UNKNOWN
;
621 String name
= getRelative(root
, file
);
624 repo
.refreshFromDisk();
625 index
= new IndexDiff(repo
);
627 } catch (IOException ex
) {
628 Exceptions
.printStackTrace(ex
);
629 return new StatusInfo(status
, null, false);
632 if (index
.getAdded().contains(name
))
633 status
= StatusInfo
.STATUS_VERSIONED_ADDEDLOCALLY
;
634 else if (index
.getRemoved().contains(name
))
635 status
= StatusInfo
.STATUS_VERSIONED_REMOVEDLOCALLY
;
636 else if (index
.getMissing().contains(name
))
637 status
= StatusInfo
.STATUS_VERSIONED_DELETEDLOCALLY
;
638 else if (index
.getChanged().contains(name
))
639 status
= StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
;
640 else if (index
.getModified().contains(name
))
641 status
= StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
;
643 status
= StatusInfo
.STATUS_VERSIONED_UPTODATE
;
644 StatusInfo info
= new StatusInfo(status
, null, false);