GitInterceptor: Replace GitCommand.doRenameAfter calls with Exceptions
[nbgit.git] / src / org / nbgit / util / GitCommand.java
blob6b7df105a738a0047009b5504623c4cbbfb65fe9
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common
8 * Development and Distribution License("CDDL") (collectively, the
9 * "License"). You may not use this file except in compliance with the
10 * License. You can obtain a copy of the License at
11 * http://www.netbeans.org/cddl-gplv2.html
12 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13 * specific language governing permissions and limitations under the
14 * License. When distributing the software, include this License Header
15 * Notice in each file and include the License file at
16 * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17 * particular file as subject to the "Classpath" exception as provided
18 * by Sun in the GPL Version 2 section of the License file that
19 * accompanied this code. If applicable, add the following below the
20 * License Header, with the fields enclosed by brackets [] replaced by
21 * your own identifying information:
22 * "Portions Copyrighted [year] [name of copyright owner]"
24 * Contributor(s):
26 * The Original Software is NetBeans. The Initial Developer of the Original
27 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28 * Microsystems, Inc. All Rights Reserved.
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;
45 import java.io.File;
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;
52 import java.util.Map;
53 import java.util.Set;
54 import org.nbgit.Git;
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;
79 /**
82 public class GitCommand {
84 static public void doAdd(File root, List<File> addCandidates, OutputLogger logger) {
85 try {
86 Repository repo = Git.getInstance().getRepository(root);
87 GitIndex index = repo.getIndex();
89 for (File dstFile : addCandidates) {
90 Entry entry = index.add(root, dstFile);
91 entry.setAssumeValid(false);
94 index.write();
96 } catch (Exception ex) {
97 logger.output(ex.getMessage());
101 public static void doMove(File root, File srcFile, File dstFile, OutputLogger logger) {
102 try {
103 Repository repo = Git.getInstance().getRepository(root);
104 GitIndex index = repo.getIndex();
105 Entry entry = index.add(root, dstFile);
107 entry.setAssumeValid(false);
108 index.remove(root, srcFile);
109 index.write();
111 } catch (Exception ex) {
112 logger.output(ex.getMessage());
116 public static void doCat(File root, File base, File tempFile, String revision) {
117 OutputLogger logger = OutputLogger.getLogger(root.getAbsolutePath());
118 try {
119 Repository repo = Git.getInstance().getRepository(root);
120 Commit commit = repo.mapCommit(revision);
122 if (commit == null) {
123 logger.output("Revision " + revision + " not found");
124 return;
127 String relative = toGitPath(getRelative(root, base));
129 ObjectId[] ids = {commit.getTree().getId()};
130 TreeWalk walk = TreeWalk.forPath(repo, relative, ids);
132 ObjectId blobId = walk.getObjectId(0);
134 ObjectLoader blob = repo.openBlob(blobId);
135 if (blob == null) {
136 logger.output("Failed to open blob " + blobId);
137 return;
140 FileOutputStream out = new FileOutputStream(tempFile);
141 out.write(blob.getCachedBytes());
143 out.close();
145 } catch (Exception ex) {
146 // FIXME: Do unload, delete, ... here
147 logger.output(ex.getMessage());
151 public static void doRevert(File root, List<File> files, String revStr, boolean doBackup, OutputLogger logger) {
152 Repository repo = Git.getInstance().getRepository(root);
154 try {
155 GitIndex index = new GitIndex(repo);
156 Tree tree = repo.mapTree(revStr);
157 index.readTree(tree);
159 for (File file : files) {
160 String path = getRelative(root, file);
161 Entry entry = index.getEntry(path);
163 if (entry == null) {
164 continue;
166 if (doBackup) {
167 file.renameTo(new File(file.getAbsolutePath() + ".orig"));
169 index.checkoutEntry(root, entry);
172 } catch (IOException ex) {
176 public static RepositoryRevision.Walk getLogMessages(String rootPath, Set<File> files, String fromRevision, String toRevision, boolean showMerges, OutputLogger logger) {
177 File root = new File(rootPath);
178 Repository repo = Git.getInstance().getRepository(root);
179 RepositoryRevision.Walk walk = new RepositoryRevision.Walk(repo);
181 try {
182 if (fromRevision == null) {
183 fromRevision = Constants.HEAD;
185 ObjectId from = repo.resolve(fromRevision);
186 if (from == null) {
187 return null;
189 walk.markStart(walk.parseCommit(from));
190 ObjectId to = toRevision != null ? repo.resolve(toRevision) : null;
191 if (to != null) {
192 walk.markUninteresting(walk.parseCommit(to));
194 List<PathFilter> paths = new ArrayList<PathFilter>();
195 for (File file : files) {
196 String path = getRelative(root, file);
198 if (!(path.length() == 0)) {
199 paths.add(PathFilter.create(path));
203 if (!paths.isEmpty()) {
204 walk.setTreeFilter(PathFilterGroup.create(paths));
206 if (!showMerges) {
207 walk.setRevFilter(RevFilter.NO_MERGES);
209 } catch (IOException ioe) {
210 return null;
213 return walk;
216 private static String buildReflogMessage(String commitMessage) {
217 String firstLine = commitMessage;
218 int newlineIndex = commitMessage.indexOf("\n");
220 if (newlineIndex > 0) {
221 firstLine = commitMessage.substring(0, newlineIndex);
223 return "\tcommit: " + firstLine;
226 private static void prepareTrees(File root, List<File> selectedItems,
227 HashMap<Repository, Tree> treeMap)
228 throws IOException, UnsupportedEncodingException {
229 Repository repo = Git.getInstance().getRepository(root);
231 for (File file : selectedItems) {
232 Tree projTree = treeMap.get(repo);
233 if (projTree == null) {
234 projTree = repo.mapTree("HEAD");
235 if (projTree == null) {
236 projTree = new Tree(repo);
238 treeMap.put(repo, projTree);
239 System.out.println("Orig tree id: " + projTree.getId());
242 GitIndex index = repo.getIndex();
243 String repoRelativePath = getRelative(root, file);
244 String string = repoRelativePath;
246 TreeEntry treeMember = projTree.findBlobMember(repoRelativePath);
247 // we always want to delete it from the current tree, since if it's
248 // updated, we'll add it again
249 if (treeMember != null) {
250 treeMember.delete();
252 Entry idxEntry = index.getEntry(string);
253 if (true /* if modified on disk or missing */) {
254 if (!file.isFile()) {
255 index.remove(root, file);
256 index.write();
257 System.out.println("Phantom file, so removing from index");
258 continue;
261 if (idxEntry.update(file)) {
262 index.write();
266 if (idxEntry != null) {
267 projTree.addFile(repoRelativePath);
268 TreeEntry newMember = projTree.findBlobMember(repoRelativePath);
270 newMember.setId(idxEntry.getObjectId());
271 System.out.println("New member id for " + repoRelativePath + ": " + newMember.getId() + " idx id: " + idxEntry.getObjectId());
276 private static void writeTreeWithSubTrees(Tree tree) throws IOException {
277 if (tree.getId() == null) {
278 System.out.println("writing tree for: " + tree.getFullName());
280 for (TreeEntry entry : tree.members()) {
281 if (entry.isModified()) {
282 if (entry instanceof Tree) {
283 writeTreeWithSubTrees((Tree) entry);
284 } else {
285 System.out.println("BAD JUJU: " + entry.getFullName());
289 ObjectWriter writer = new ObjectWriter(tree.getRepository());
290 tree.setId(writer.writeTree(tree));
294 public static void doCommit(File root, List<File> commitCandidates, String message, OutputLogger logger) throws IOException {
295 HashMap<Repository, Tree> treeMap = new HashMap<Repository, Tree>();
297 prepareTrees(root, commitCandidates, treeMap);
299 for (java.util.Map.Entry<Repository, Tree> entry : treeMap.entrySet()) {
301 Tree tree = entry.getValue();
302 Repository repo = tree.getRepository();
303 PersonIdent personIdent = new PersonIdent(repo);
305 writeTreeWithSubTrees(tree);
307 final RefUpdate ru = repo.updateRef(Constants.HEAD);
308 ObjectId[] parentIds;
309 if (ru.getOldObjectId() != null) {
310 parentIds = new ObjectId[]{ru.getOldObjectId()};
311 } else {
312 parentIds = new ObjectId[0];
314 Commit commit = new Commit(repo, parentIds);
315 commit.setTree(tree);
316 message = message.replaceAll("\r", "\n");
318 commit.setMessage(message);
319 commit.setAuthor(personIdent);
320 commit.setCommitter(personIdent);
322 ObjectWriter writer = new ObjectWriter(repo);
323 commit.setCommitId(writer.writeCommit(commit));
325 ru.setNewObjectId(commit.getCommitId());
326 ru.setRefLogMessage(buildReflogMessage(message), false);
327 ru.update();
328 boolean ok;
329 if (ru.getOldObjectId() != null) {
330 ok = ru.getResult() == RefUpdate.Result.FAST_FORWARD;
331 } else {
332 ok = ru.getResult() == RefUpdate.Result.NEW;
334 if (!ok) {
335 logger.output("Failed to update " + ru.getName() + " to commit " + commit.getCommitId() + ".");
340 public static void doRemove(File root, List<File> deleteCandidates, OutputLogger logger) {
341 try {
342 Repository repo = Git.getInstance().getRepository(root);
343 GitIndex index = repo.getIndex();
344 boolean dirty = false;
346 for (File srcFile : deleteCandidates) {
347 if (index.remove(root, srcFile)) {
348 dirty = true;
352 if (dirty) {
353 index.write();
355 } catch (Exception ex) {
356 logger.output(ex.getMessage());
361 public static void doRemove(File root, File srcFile, OutputLogger logger) {
362 try {
363 Repository repo = Git.getInstance().getRepository(root);
364 GitIndex index = repo.getIndex();
366 if (index.remove(root, srcFile)) {
367 index.write();
369 } catch (Exception ex) {
370 logger.output(ex.getMessage());
375 public static List<String[]> getRevisions(File root, int limit) {
376 return getRevisionsForFile(root, null, limit);
379 public static List<String[]> getRevisionsForFile(File root, File[] files, int limit) {
380 Repository repo = Git.getInstance().getRepository(root);
381 RevWalk walk = new RevWalk(repo);
382 List<String[]> revs = new ArrayList<String[]>();
384 try {
385 ObjectId from = repo.resolve(Constants.HEAD);
386 if (from == null) {
387 return null;
389 walk.markStart(walk.parseCommit(from));
391 if (files != null) {
392 List<PathFilter> paths = new ArrayList<PathFilter>();
393 for (File file : files) {
394 String path = getRelative(root, file);
396 if (!(path.length() == 0)) {
397 paths.add(PathFilter.create(path));
401 if (!paths.isEmpty()) {
402 walk.setTreeFilter(PathFilterGroup.create(paths));
406 for (RevCommit rev : walk) {
407 revs.add(new String[]{rev.getShortMessage(), rev.getId().name()});
408 if (--limit <= 0) {
409 break;
413 } catch (IOException ioe) {
416 return revs;
419 private static String getRelative(File root, File dir) {
420 return getRelative(root.getAbsolutePath(), dir.getAbsolutePath());
423 private static String getRelative(String root, String dir) {
424 if (dir.equals(root)) {
425 return "";
427 return dir.replace(root + File.separator, "");
430 private static String toGitPath(String path) {
431 return File.separatorChar == '/'
432 ? path : path.replace(File.separator, "/");
435 private static void put(Set<String> set, String relPath,
436 Map<File, StatusInfo> files, File root, int status) {
437 for (String path : set) {
438 if (relPath.length() > 0 && !path.startsWith(relPath)) {
439 continue;
441 File file = new File(root, path);
442 files.put(file, new StatusInfo(status, null, false));
448 * m odified
449 * a dded
450 * r emoved
451 * d eleted
452 * u nknown
453 * C opies
455 * i gnored
456 * c lean
458 public static Map<File, StatusInfo> getAllStatus(File root, File dir) throws IOException {
459 String relPath = getRelative(root, dir);
461 Repository repo = Git.getInstance().getRepository(root);
462 Map<File, StatusInfo> files = new HashMap<File, StatusInfo>();
464 try {
465 repo.refreshFromDisk();
466 IndexDiff index = new IndexDiff(repo);
467 index.diff();
469 put(index.getAdded(), relPath, files, root,
470 StatusInfo.STATUS_VERSIONED_ADDEDLOCALLY);
471 put(index.getRemoved(), relPath, files, root,
472 StatusInfo.STATUS_VERSIONED_REMOVEDLOCALLY);
473 put(index.getMissing(), relPath, files, root,
474 StatusInfo.STATUS_VERSIONED_DELETEDLOCALLY);
475 put(index.getChanged(), relPath, files, root,
476 StatusInfo.STATUS_VERSIONED_MODIFIEDLOCALLY);
477 put(index.getModified(), relPath, files, root,
478 StatusInfo.STATUS_VERSIONED_MODIFIEDLOCALLY);
480 final FileTreeIterator workTree = new FileTreeIterator(repo.getWorkDir());
481 final TreeWalk walk = new TreeWalk(repo);
483 walk.reset(); // drop the first empty tree
484 walk.setRecursive(true);
485 walk.addTree(workTree);
487 int share = SharabilityQuery.getSharability(dir);
488 if (share == SharabilityQuery.NOT_SHARABLE) {
489 return files;
491 while (walk.next()) {
492 String path = walk.getPathString();
494 if (relPath.length() > 0 && !path.startsWith(relPath)) {
495 continue;
497 if (index.getAdded().contains(path) ||
498 index.getRemoved().contains(path) ||
499 index.getMissing().contains(path) ||
500 index.getChanged().contains(path) ||
501 index.getModified().contains(path)) {
502 continue;
504 Entry entry = repo.getIndex().getEntry(path);
505 File file = new File(root, path);
507 int status;
508 if (entry != null) {
509 status = StatusInfo.STATUS_VERSIONED_UPTODATE;
510 } else {
511 if (share == SharabilityQuery.MIXED &&
512 SharabilityQuery.getSharability(file) == SharabilityQuery.NOT_SHARABLE) {
513 continue;
515 status = StatusInfo.STATUS_NOTVERSIONED_NEWLOCALLY;
518 files.put(file, new StatusInfo(status, null, false));
521 } catch (IOException ex) {
522 Exceptions.printStackTrace(ex);
525 return files;
529 * m odified
530 * a dded
531 * r emoved
532 * d eleted
533 * u nknown
534 * C opies
536 public static Map<File, StatusInfo> getInterestingStatus(File root, File dir) {
537 String relPath = getRelative(root, dir);
539 Repository repo = Git.getInstance().getRepository(root);
540 IndexDiff index;
542 Map<File, StatusInfo> files = new HashMap<File, StatusInfo>();
544 try {
545 repo.refreshFromDisk();
546 index = new IndexDiff(repo);
547 index.diff();
549 put(index.getAdded(), relPath, files, root,
550 StatusInfo.STATUS_VERSIONED_ADDEDLOCALLY);
551 put(index.getRemoved(), relPath, files, root,
552 StatusInfo.STATUS_VERSIONED_REMOVEDLOCALLY);
553 put(index.getMissing(), relPath, files, root,
554 StatusInfo.STATUS_VERSIONED_DELETEDLOCALLY);
555 put(index.getChanged(), relPath, files, root,
556 StatusInfo.STATUS_VERSIONED_MODIFIEDLOCALLY);
557 put(index.getModified(), relPath, files, root,
558 StatusInfo.STATUS_VERSIONED_MODIFIEDLOCALLY);
560 final FileTreeIterator workTree = new FileTreeIterator(repo.getWorkDir());
561 final TreeWalk walk = new TreeWalk(repo);
563 walk.reset(); // drop the first empty tree
564 walk.setRecursive(true);
565 walk.addTree(workTree);
567 while (walk.next()) {
568 String path = walk.getPathString();
570 if (relPath.length() > 0 && !path.startsWith(relPath)) {
571 continue;
573 if (index.getAdded().contains(path) ||
574 index.getRemoved().contains(path) ||
575 index.getMissing().contains(path) ||
576 index.getChanged().contains(path) ||
577 index.getModified().contains(path)) {
578 continue;
580 Entry entry = repo.getIndex().getEntry(path);
581 if (entry != null) {
582 continue;
584 int status = StatusInfo.STATUS_NOTVERSIONED_NEWLOCALLY;
585 File file = new File(root, path);
586 files.put(file, new StatusInfo(status, null, false));
589 } catch (IOException ex) {
590 Exceptions.printStackTrace(ex);
593 return files;
596 public static StatusInfo getSingleStatus(File root, File file) {
597 Repository repo = Git.getInstance().getRepository(root);
598 IndexDiff index;
600 int share = SharabilityQuery.getSharability(file.getParentFile());
601 if (share == SharabilityQuery.NOT_SHARABLE ||
602 (share == SharabilityQuery.MIXED &&
603 SharabilityQuery.getSharability(file) == SharabilityQuery.NOT_SHARABLE)) {
604 return new StatusInfo(StatusInfo.STATUS_NOTVERSIONED_EXCLUDED, null, false);
606 int status = StatusInfo.STATUS_UNKNOWN;
607 String name = getRelative(root, file);
609 try {
610 repo.refreshFromDisk();
611 index = new IndexDiff(repo);
612 index.diff();
613 } catch (IOException ex) {
614 Exceptions.printStackTrace(ex);
615 return new StatusInfo(status, null, false);
618 if (index.getAdded().contains(name)) {
619 status = StatusInfo.STATUS_VERSIONED_ADDEDLOCALLY;
620 } else if (index.getRemoved().contains(name)) {
621 status = StatusInfo.STATUS_VERSIONED_REMOVEDLOCALLY;
622 } else if (index.getMissing().contains(name)) {
623 status = StatusInfo.STATUS_VERSIONED_DELETEDLOCALLY;
624 } else if (index.getChanged().contains(name)) {
625 status = StatusInfo.STATUS_VERSIONED_MODIFIEDLOCALLY;
626 } else if (index.getModified().contains(name)) {
627 status = StatusInfo.STATUS_VERSIONED_MODIFIEDLOCALLY;
628 } else {
629 status = StatusInfo.STATUS_VERSIONED_UPTODATE;
631 StatusInfo info = new StatusInfo(status, null, false);
633 return info;