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
.IOException
;
47 import java
.io
.UnsupportedEncodingException
;
48 import java
.util
.ArrayList
;
49 import java
.util
.HashMap
;
50 import java
.util
.List
;
54 import org
.nbgit
.ui
.log
.RepositoryRevision
;
55 import org
.netbeans
.api
.queries
.SharabilityQuery
;
56 import org
.openide
.util
.Exceptions
;
57 import org
.spearce
.jgit
.lib
.Commit
;
58 import org
.spearce
.jgit
.lib
.Constants
;
59 import org
.spearce
.jgit
.lib
.GitIndex
;
60 import org
.spearce
.jgit
.lib
.GitIndex
.Entry
;
61 import org
.spearce
.jgit
.lib
.IndexDiff
;
62 import org
.spearce
.jgit
.lib
.ObjectId
;
63 import org
.spearce
.jgit
.lib
.ObjectWriter
;
64 import org
.spearce
.jgit
.lib
.PersonIdent
;
65 import org
.spearce
.jgit
.lib
.RefUpdate
;
66 import org
.spearce
.jgit
.lib
.Repository
;
67 import org
.spearce
.jgit
.lib
.Tree
;
68 import org
.spearce
.jgit
.lib
.TreeEntry
;
69 import org
.spearce
.jgit
.revwalk
.RevCommit
;
70 import org
.spearce
.jgit
.revwalk
.RevWalk
;
71 import org
.spearce
.jgit
.revwalk
.filter
.RevFilter
;
72 import org
.spearce
.jgit
.treewalk
.FileTreeIterator
;
73 import org
.spearce
.jgit
.treewalk
.TreeWalk
;
74 import org
.spearce
.jgit
.treewalk
.filter
.PathFilter
;
75 import org
.spearce
.jgit
.treewalk
.filter
.PathFilterGroup
;
80 public class GitCommand
{
82 public static RepositoryRevision
.Walk
getLogMessages(String rootPath
, Set
<File
> files
, String fromRevision
, String toRevision
, boolean showMerges
, OutputLogger logger
) {
83 File root
= new File(rootPath
);
84 Repository repo
= Git
.getInstance().getRepository(root
);
85 RepositoryRevision
.Walk walk
= new RepositoryRevision
.Walk(repo
);
88 if (fromRevision
== null) {
89 fromRevision
= Constants
.HEAD
;
91 ObjectId from
= repo
.resolve(fromRevision
);
95 walk
.markStart(walk
.parseCommit(from
));
96 ObjectId to
= toRevision
!= null ? repo
.resolve(toRevision
) : null;
98 walk
.markUninteresting(walk
.parseCommit(to
));
100 List
<PathFilter
> paths
= new ArrayList
<PathFilter
>();
101 for (File file
: files
) {
102 String path
= getRelative(root
, file
);
104 if (!(path
.length() == 0)) {
105 paths
.add(PathFilter
.create(path
));
109 if (!paths
.isEmpty()) {
110 walk
.setTreeFilter(PathFilterGroup
.create(paths
));
113 walk
.setRevFilter(RevFilter
.NO_MERGES
);
115 } catch (IOException ioe
) {
122 private static String
buildReflogMessage(String commitMessage
) {
123 String firstLine
= commitMessage
;
124 int newlineIndex
= commitMessage
.indexOf("\n");
126 if (newlineIndex
> 0) {
127 firstLine
= commitMessage
.substring(0, newlineIndex
);
129 return "\tcommit: " + firstLine
;
132 private static Tree
prepareTree(File root
, List
<File
> selectedItems
)
133 throws IOException
, UnsupportedEncodingException
{
134 Repository repo
= Git
.getInstance().getRepository(root
);
135 Tree projTree
= repo
.mapTree("HEAD");
136 if (projTree
== null)
137 projTree
= new Tree(repo
);
139 for (File file
: selectedItems
) {
140 GitIndex index
= repo
.getIndex();
141 String repoRelativePath
= getRelative(root
, file
);
142 String string
= repoRelativePath
;
144 TreeEntry treeMember
= projTree
.findBlobMember(repoRelativePath
);
145 // we always want to delete it from the current tree, since if it's
146 // updated, we'll add it again
147 if (treeMember
!= null) {
150 Entry idxEntry
= index
.getEntry(string
);
151 if (true /* if modified on disk or missing */) {
152 if (!file
.isFile()) {
153 index
.remove(root
, file
);
155 System
.out
.println("Phantom file, so removing from index");
159 if (idxEntry
.update(file
)) {
164 if (idxEntry
!= null) {
165 projTree
.addFile(repoRelativePath
);
166 TreeEntry newMember
= projTree
.findBlobMember(repoRelativePath
);
168 newMember
.setId(idxEntry
.getObjectId());
169 System
.out
.println("New member id for " + repoRelativePath
+ ": " + newMember
.getId() + " idx id: " + idxEntry
.getObjectId());
176 private static void writeTreeWithSubTrees(Tree tree
) throws IOException
{
177 if (tree
.getId() == null) {
178 System
.out
.println("writing tree for: " + tree
.getFullName());
180 for (TreeEntry entry
: tree
.members()) {
181 if (entry
.isModified()) {
182 if (entry
instanceof Tree
) {
183 writeTreeWithSubTrees((Tree
) entry
);
185 System
.out
.println("BAD JUJU: " + entry
.getFullName());
189 ObjectWriter writer
= new ObjectWriter(tree
.getRepository());
190 tree
.setId(writer
.writeTree(tree
));
194 public static void doCommit(File root
, List
<File
> commitCandidates
, String message
, OutputLogger logger
) throws IOException
{
195 Tree tree
= prepareTree(root
, commitCandidates
);
197 Repository repo
= tree
.getRepository();
198 PersonIdent personIdent
= new PersonIdent(repo
);
200 writeTreeWithSubTrees(tree
);
201 doCommit(repo
, tree
.getTreeId(), personIdent
, message
, logger
);
204 public static void doCommit(Repository repo
, ObjectId treeId
, PersonIdent personIdent
, String message
, OutputLogger logger
) throws IOException
{
205 final RefUpdate ru
= repo
.updateRef(Constants
.HEAD
);
206 ObjectId
[] parentIds
;
207 if (ru
.getOldObjectId() != null) {
208 parentIds
= new ObjectId
[]{ru
.getOldObjectId()};
210 parentIds
= new ObjectId
[0];
212 Commit commit
= new Commit(repo
, parentIds
);
213 commit
.setTreeId(treeId
);
214 message
= message
.replaceAll("\r", "\n");
216 commit
.setMessage(message
);
217 commit
.setAuthor(personIdent
);
218 commit
.setCommitter(personIdent
);
220 ObjectWriter writer
= new ObjectWriter(repo
);
221 commit
.setCommitId(writer
.writeCommit(commit
));
223 ru
.setNewObjectId(commit
.getCommitId());
224 ru
.setRefLogMessage(buildReflogMessage(message
), false);
227 if (ru
.getOldObjectId() != null) {
228 ok
= ru
.getResult() == RefUpdate
.Result
.FAST_FORWARD
;
230 ok
= ru
.getResult() == RefUpdate
.Result
.NEW
;
233 logger
.output("Failed to update " + ru
.getName() + " to commit " + commit
.getCommitId() + ".");
237 public static List
<String
[]> getRevisions(File root
, int limit
) {
238 return getRevisionsForFile(root
, null, limit
);
241 public static List
<String
[]> getRevisionsForFile(File root
, File
[] files
, int limit
) {
242 Repository repo
= Git
.getInstance().getRepository(root
);
243 RevWalk walk
= new RevWalk(repo
);
244 List
<String
[]> revs
= new ArrayList
<String
[]>();
247 ObjectId from
= repo
.resolve(Constants
.HEAD
);
251 walk
.markStart(walk
.parseCommit(from
));
254 List
<PathFilter
> paths
= new ArrayList
<PathFilter
>();
255 for (File file
: files
) {
256 String path
= getRelative(root
, file
);
258 if (!(path
.length() == 0)) {
259 paths
.add(PathFilter
.create(path
));
263 if (!paths
.isEmpty()) {
264 walk
.setTreeFilter(PathFilterGroup
.create(paths
));
268 for (RevCommit rev
: walk
) {
269 revs
.add(new String
[]{rev
.getShortMessage(), rev
.getId().name()});
275 } catch (IOException ioe
) {
281 private static String
getRelative(File root
, File dir
) {
282 return getRelative(root
.getAbsolutePath(), dir
.getAbsolutePath());
285 private static String
getRelative(String root
, String dir
) {
286 if (dir
.equals(root
)) {
289 return dir
.replace(root
+ File
.separator
, "");
292 private static void put(Set
<String
> set
, String relPath
,
293 Map
<File
, StatusInfo
> files
, File root
, int status
) {
294 for (String path
: set
) {
295 if (relPath
.length() > 0 && !path
.startsWith(relPath
)) {
298 File file
= new File(root
, path
);
299 files
.put(file
, new StatusInfo(status
, null, false));
315 public static Map
<File
, StatusInfo
> getAllStatus(File root
, File dir
) throws IOException
{
316 String relPath
= getRelative(root
, dir
);
318 Repository repo
= Git
.getInstance().getRepository(root
);
319 Map
<File
, StatusInfo
> files
= new HashMap
<File
, StatusInfo
>();
322 repo
.refreshFromDisk();
323 IndexDiff index
= new IndexDiff(repo
);
326 put(index
.getAdded(), relPath
, files
, root
,
327 StatusInfo
.STATUS_VERSIONED_ADDEDLOCALLY
);
328 put(index
.getRemoved(), relPath
, files
, root
,
329 StatusInfo
.STATUS_VERSIONED_REMOVEDLOCALLY
);
330 put(index
.getMissing(), relPath
, files
, root
,
331 StatusInfo
.STATUS_VERSIONED_DELETEDLOCALLY
);
332 put(index
.getChanged(), relPath
, files
, root
,
333 StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
);
334 put(index
.getModified(), relPath
, files
, root
,
335 StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
);
337 final FileTreeIterator workTree
= new FileTreeIterator(repo
.getWorkDir());
338 final TreeWalk walk
= new TreeWalk(repo
);
340 walk
.reset(); // drop the first empty tree
341 walk
.setRecursive(true);
342 walk
.addTree(workTree
);
344 int share
= SharabilityQuery
.getSharability(dir
);
345 if (share
== SharabilityQuery
.NOT_SHARABLE
) {
348 while (walk
.next()) {
349 String path
= walk
.getPathString();
351 if (relPath
.length() > 0 && !path
.startsWith(relPath
)) {
354 if (index
.getAdded().contains(path
) ||
355 index
.getRemoved().contains(path
) ||
356 index
.getMissing().contains(path
) ||
357 index
.getChanged().contains(path
) ||
358 index
.getModified().contains(path
)) {
361 Entry entry
= repo
.getIndex().getEntry(path
);
362 File file
= new File(root
, path
);
366 status
= StatusInfo
.STATUS_VERSIONED_UPTODATE
;
368 if (share
== SharabilityQuery
.MIXED
&&
369 SharabilityQuery
.getSharability(file
) == SharabilityQuery
.NOT_SHARABLE
) {
372 status
= StatusInfo
.STATUS_NOTVERSIONED_NEWLOCALLY
;
375 files
.put(file
, new StatusInfo(status
, null, false));
378 } catch (IOException ex
) {
379 Exceptions
.printStackTrace(ex
);
393 public static Map
<File
, StatusInfo
> getInterestingStatus(File root
, File dir
) {
394 String relPath
= getRelative(root
, dir
);
396 Repository repo
= Git
.getInstance().getRepository(root
);
399 Map
<File
, StatusInfo
> files
= new HashMap
<File
, StatusInfo
>();
402 repo
.refreshFromDisk();
403 index
= new IndexDiff(repo
);
406 put(index
.getAdded(), relPath
, files
, root
,
407 StatusInfo
.STATUS_VERSIONED_ADDEDLOCALLY
);
408 put(index
.getRemoved(), relPath
, files
, root
,
409 StatusInfo
.STATUS_VERSIONED_REMOVEDLOCALLY
);
410 put(index
.getMissing(), relPath
, files
, root
,
411 StatusInfo
.STATUS_VERSIONED_DELETEDLOCALLY
);
412 put(index
.getChanged(), relPath
, files
, root
,
413 StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
);
414 put(index
.getModified(), relPath
, files
, root
,
415 StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
);
417 final FileTreeIterator workTree
= new FileTreeIterator(repo
.getWorkDir());
418 final TreeWalk walk
= new TreeWalk(repo
);
420 walk
.reset(); // drop the first empty tree
421 walk
.setRecursive(true);
422 walk
.addTree(workTree
);
424 while (walk
.next()) {
425 String path
= walk
.getPathString();
427 if (relPath
.length() > 0 && !path
.startsWith(relPath
)) {
430 if (index
.getAdded().contains(path
) ||
431 index
.getRemoved().contains(path
) ||
432 index
.getMissing().contains(path
) ||
433 index
.getChanged().contains(path
) ||
434 index
.getModified().contains(path
)) {
437 Entry entry
= repo
.getIndex().getEntry(path
);
441 int status
= StatusInfo
.STATUS_NOTVERSIONED_NEWLOCALLY
;
442 File file
= new File(root
, path
);
443 files
.put(file
, new StatusInfo(status
, null, false));
446 } catch (IOException ex
) {
447 Exceptions
.printStackTrace(ex
);
453 public static StatusInfo
getSingleStatus(File root
, File file
) {
454 Repository repo
= Git
.getInstance().getRepository(root
);
457 int share
= SharabilityQuery
.getSharability(file
.getParentFile());
458 if (share
== SharabilityQuery
.NOT_SHARABLE
||
459 (share
== SharabilityQuery
.MIXED
&&
460 SharabilityQuery
.getSharability(file
) == SharabilityQuery
.NOT_SHARABLE
)) {
461 return new StatusInfo(StatusInfo
.STATUS_NOTVERSIONED_EXCLUDED
, null, false);
463 int status
= StatusInfo
.STATUS_UNKNOWN
;
464 String name
= getRelative(root
, file
);
467 repo
.refreshFromDisk();
468 index
= new IndexDiff(repo
);
470 } catch (IOException ex
) {
471 Exceptions
.printStackTrace(ex
);
472 return new StatusInfo(status
, null, false);
475 if (index
.getAdded().contains(name
)) {
476 status
= StatusInfo
.STATUS_VERSIONED_ADDEDLOCALLY
;
477 } else if (index
.getRemoved().contains(name
)) {
478 status
= StatusInfo
.STATUS_VERSIONED_REMOVEDLOCALLY
;
479 } else if (index
.getMissing().contains(name
)) {
480 status
= StatusInfo
.STATUS_VERSIONED_DELETEDLOCALLY
;
481 } else if (index
.getChanged().contains(name
)) {
482 status
= StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
;
483 } else if (index
.getModified().contains(name
)) {
484 status
= StatusInfo
.STATUS_VERSIONED_MODIFIEDLOCALLY
;
486 status
= StatusInfo
.STATUS_VERSIONED_UPTODATE
;
488 StatusInfo info
= new StatusInfo(status
, null, false);