Remove System.out.println from RevWalkFilterTest
[jgit.git] / org.spearce.egit.ui / src / org / spearce / egit / ui / internal / actions / CommitAction.java
blob8415002eb4be09e0aec5b0eef6a53fc3778e0100
1 /*******************************************************************************
2 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3 * Copyright (C) 2007, Jing Xue <jingxue@digizenstudio.com>
4 * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com>
5 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
6 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v1.0
10 * See LICENSE for the full license text, also available.
11 *******************************************************************************/
12 package org.spearce.egit.ui.internal.actions;
14 import java.io.File;
15 import java.io.IOException;
16 import java.io.UnsupportedEncodingException;
17 import java.util.ArrayList;
18 import java.util.Date;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.TimeZone;
23 import org.eclipse.core.resources.IFile;
24 import org.eclipse.core.resources.IProject;
25 import org.eclipse.core.resources.IResource;
26 import org.eclipse.jface.action.IAction;
27 import org.eclipse.jface.dialogs.IDialogConstants;
28 import org.eclipse.jface.dialogs.MessageDialog;
29 import org.eclipse.team.core.TeamException;
30 import org.eclipse.team.internal.ui.Utils;
31 import org.spearce.egit.core.project.GitProjectData;
32 import org.spearce.egit.core.project.RepositoryMapping;
33 import org.spearce.egit.ui.internal.dialogs.CommitDialog;
34 import org.spearce.jgit.lib.Commit;
35 import org.spearce.jgit.lib.Constants;
36 import org.spearce.jgit.lib.GitIndex;
37 import org.spearce.jgit.lib.IndexDiff;
38 import org.spearce.jgit.lib.ObjectId;
39 import org.spearce.jgit.lib.ObjectWriter;
40 import org.spearce.jgit.lib.PersonIdent;
41 import org.spearce.jgit.lib.RefUpdate;
42 import org.spearce.jgit.lib.Repository;
43 import org.spearce.jgit.lib.RepositoryConfig;
44 import org.spearce.jgit.lib.Tree;
45 import org.spearce.jgit.lib.TreeEntry;
46 import org.spearce.jgit.lib.GitIndex.Entry;
48 /**
49 * Scan for modified resources in the same project as the selected resources.
51 public class CommitAction extends RepositoryAction {
53 private ArrayList<IFile> notIndexed;
54 private ArrayList<IFile> indexChanges;
55 private ArrayList<IFile> files;
57 private Commit previousCommit;
59 private boolean amendAllowed;
60 private boolean amending;
62 @Override
63 public void run(IAction act) {
64 resetState();
65 try {
66 buildIndexHeadDiffList();
67 } catch (IOException e) {
68 Utils.handleError(getTargetPart().getSite().getShell(), e, "Error during commit", "Error occurred computing diffs");
69 return;
72 Repository[] repos = getRepositoriesFor(getProjectsForSelectedResources());
73 Repository repository = null;
74 amendAllowed = repos.length == 1;
75 for (Repository repo : repos) {
76 repository = repo;
77 if (!repo.getRepositoryState().canCommit()) {
78 MessageDialog.openError(getTargetPart().getSite().getShell(),
79 "Cannot commit now", "Repository state:"
80 + repo.getRepositoryState().getDescription());
81 return;
85 if (files.isEmpty()) {
86 if (amendAllowed) {
87 boolean result = MessageDialog
88 .openQuestion(getTargetPart().getSite().getShell(),
89 "No files to commit",
90 "No changed items were selected. Do you wish to amend the last commit?");
91 if (!result)
92 return;
93 amending = true;
94 } else {
95 MessageDialog.openWarning(getTargetPart().getSite().getShell(), "No files to commit", "No changed items were selected.\n\nAmend is not possible as you have selected multiple repositories.");
96 return;
100 String author = null;
101 String committer = null;
102 if (repository != null) {
103 final RepositoryConfig config = repository.getConfig();
104 author = config.getAuthorName();
105 final String authorEmail = config.getAuthorEmail();
106 author = author + " <" + authorEmail + ">";
108 committer = config.getCommitterName();
109 final String committerEmail = config.getCommitterEmail();
110 committer = committer + " <" + committerEmail + ">";
113 loadPreviousCommit();
115 CommitDialog commitDialog = new CommitDialog(getTargetPart().getSite().getShell());
116 commitDialog.setAmending(amending);
117 commitDialog.setAmendAllowed(amendAllowed);
118 commitDialog.setFileList(files);
119 commitDialog.setAuthor(author);
120 commitDialog.setCommitter(committer);
122 if (previousCommit != null) {
123 commitDialog.setPreviousCommitMessage(previousCommit.getMessage());
124 PersonIdent previousAuthor = previousCommit.getAuthor();
125 commitDialog.setPreviousAuthor(previousAuthor.getName() + " <" + previousAuthor.getEmailAddress() + ">");
128 if (commitDialog.open() != IDialogConstants.OK_ID)
129 return;
131 String commitMessage = commitDialog.getCommitMessage();
132 amending = commitDialog.isAmending();
133 try {
134 performCommit(commitDialog, commitMessage);
135 } catch (TeamException e) {
136 Utils.handleError(getTargetPart().getSite().getShell(), e, "Error during commit", "Error occurred while committing");
140 private void resetState() {
141 files = new ArrayList<IFile>();
142 notIndexed = new ArrayList<IFile>();
143 indexChanges = new ArrayList<IFile>();
144 amending = false;
145 previousCommit = null;
148 private void loadPreviousCommit() {
149 IProject project = getProjectsForSelectedResources()[0];
151 Repository repo = RepositoryMapping.getMapping(project).getRepository();
152 try {
153 ObjectId parentId = repo.resolve(Constants.HEAD);
154 if (parentId != null)
155 previousCommit = repo.mapCommit(parentId);
156 } catch (IOException e) {
157 Utils.handleError(getTargetPart().getSite().getShell(), e, "Error during commit", "Error occurred retrieving last commit");
161 private void performCommit(CommitDialog commitDialog, String commitMessage)
162 throws TeamException {
163 // System.out.println("Commit Message: " + commitMessage);
164 IFile[] selectedItems = commitDialog.getSelectedFiles();
166 HashMap<Repository, Tree> treeMap = new HashMap<Repository, Tree>();
167 try {
168 prepareTrees(selectedItems, treeMap);
169 } catch (IOException e) {
170 throw new TeamException("Preparing trees", e);
173 try {
174 doCommits(commitDialog, commitMessage, treeMap);
175 } catch (IOException e) {
176 throw new TeamException("Committing changes", e);
178 for (IProject proj : getProjectsForSelectedResources()) {
179 RepositoryMapping.getMapping(proj).fireRepositoryChanged();
183 private void doCommits(CommitDialog commitDialog, String commitMessage,
184 HashMap<Repository, Tree> treeMap) throws IOException, TeamException {
186 final String author = commitDialog.getAuthor();
187 final String committer = commitDialog.getCommitter();
188 final Date commitDate = new Date();
189 final TimeZone timeZone = TimeZone.getDefault();
191 final PersonIdent authorIdent = new PersonIdent(author);
192 final PersonIdent committerIdent = new PersonIdent(committer);
194 for (java.util.Map.Entry<Repository, Tree> entry : treeMap.entrySet()) {
195 Tree tree = entry.getValue();
196 Repository repo = tree.getRepository();
197 writeTreeWithSubTrees(tree);
199 ObjectId currentHeadId = repo.resolve(Constants.HEAD);
200 ObjectId[] parentIds;
201 if (amending) {
202 parentIds = previousCommit.getParentIds();
203 } else {
204 if (currentHeadId != null)
205 parentIds = new ObjectId[] { currentHeadId };
206 else
207 parentIds = new ObjectId[0];
209 Commit commit = new Commit(repo, parentIds);
210 commit.setTree(tree);
211 commit.setMessage(commitMessage);
212 commit.setAuthor(new PersonIdent(authorIdent, commitDate, timeZone));
213 commit.setCommitter(new PersonIdent(committerIdent, commitDate, timeZone));
215 ObjectWriter writer = new ObjectWriter(repo);
216 commit.setCommitId(writer.writeCommit(commit));
218 final RefUpdate ru = repo.updateRef(Constants.HEAD);
219 ru.setNewObjectId(commit.getCommitId());
220 ru.setRefLogMessage(buildReflogMessage(commitMessage), false);
221 if (ru.forceUpdate() == RefUpdate.Result.LOCK_FAILURE) {
222 throw new TeamException("Failed to update " + ru.getName()
223 + " to commit " + commit.getCommitId() + ".");
228 private void prepareTrees(IFile[] selectedItems,
229 HashMap<Repository, Tree> treeMap) throws IOException,
230 UnsupportedEncodingException {
231 if (selectedItems.length == 0) {
232 // amending commit - need to put something into the map
233 for (IProject proj : getProjectsForSelectedResources()) {
234 Repository repo = RepositoryMapping.getMapping(proj).getRepository();
235 if (!treeMap.containsKey(repo))
236 treeMap.put(repo, repo.mapTree(Constants.HEAD));
240 for (IFile file : selectedItems) {
241 // System.out.println("\t" + file);
243 IProject project = file.getProject();
244 RepositoryMapping repositoryMapping = RepositoryMapping.getMapping(project);
245 Repository repository = repositoryMapping.getRepository();
246 Tree projTree = treeMap.get(repository);
247 if (projTree == null) {
248 projTree = repository.mapTree(Constants.HEAD);
249 if (projTree == null)
250 projTree = new Tree(repository);
251 treeMap.put(repository, projTree);
252 System.out.println("Orig tree id: " + projTree.getId());
254 GitIndex index = repository.getIndex();
255 String repoRelativePath = repositoryMapping
256 .getRepoRelativePath(file);
257 String string = repoRelativePath;
259 TreeEntry treeMember = projTree.findBlobMember(repoRelativePath);
260 // we always want to delete it from the current tree, since if it's
261 // updated, we'll add it again
262 if (treeMember != null)
263 treeMember.delete();
265 Entry idxEntry = index.getEntry(string);
266 if (notIndexed.contains(file)) {
267 File thisfile = new File(repositoryMapping.getWorkDir(), idxEntry.getName());
268 if (!thisfile.isFile()) {
269 index.remove(repositoryMapping.getWorkDir(), thisfile);
270 index.write();
271 System.out.println("Phantom file, so removing from index");
272 continue;
273 } else {
274 if (idxEntry.update(thisfile))
275 index.write();
280 if (idxEntry != null) {
281 projTree.addFile(repoRelativePath);
282 TreeEntry newMember = projTree.findBlobMember(repoRelativePath);
284 newMember.setId(idxEntry.getObjectId());
285 System.out.println("New member id for " + repoRelativePath
286 + ": " + newMember.getId() + " idx id: "
287 + idxEntry.getObjectId());
292 private String buildReflogMessage(String commitMessage) {
293 String firstLine = commitMessage;
294 int newlineIndex = commitMessage.indexOf("\n");
295 if (newlineIndex > 0) {
296 firstLine = commitMessage.substring(0, newlineIndex);
298 String commitStr = amending ? "commit (amend):" : "commit: ";
299 String message = commitStr + firstLine;
300 return message;
303 private void writeTreeWithSubTrees(Tree tree) throws TeamException {
304 if (tree.getId() == null) {
305 System.out.println("writing tree for: " + tree.getFullName());
306 try {
307 for (TreeEntry entry : tree.members()) {
308 if (entry.isModified()) {
309 if (entry instanceof Tree) {
310 writeTreeWithSubTrees((Tree) entry);
311 } else {
312 // this shouldn't happen.... not quite sure what to
313 // do here :)
314 System.out.println("BAD JUJU: "
315 + entry.getFullName());
319 ObjectWriter writer = new ObjectWriter(tree.getRepository());
320 tree.setId(writer.writeTree(tree));
321 } catch (IOException e) {
322 throw new TeamException("Writing trees", e);
327 private void buildIndexHeadDiffList() throws IOException {
328 for (IProject project : getProjectsInRepositoryOfSelectedResources()) {
329 RepositoryMapping repositoryMapping = RepositoryMapping.getMapping(project);
330 assert repositoryMapping != null;
331 Repository repository = repositoryMapping.getRepository();
332 Tree head = repository.mapTree(Constants.HEAD);
333 GitIndex index = repository.getIndex();
334 IndexDiff indexDiff = new IndexDiff(head, index);
335 indexDiff.diff();
337 includeList(project, indexDiff.getAdded(), indexChanges);
338 includeList(project, indexDiff.getChanged(), indexChanges);
339 includeList(project, indexDiff.getRemoved(), indexChanges);
340 includeList(project, indexDiff.getMissing(), notIndexed);
341 includeList(project, indexDiff.getModified(), notIndexed);
345 private void includeList(IProject project, HashSet<String> added, ArrayList<IFile> category) {
346 String repoRelativePath = RepositoryMapping.getMapping(project).getRepoRelativePath(project);
347 if (repoRelativePath.length() > 0) {
348 repoRelativePath += "/";
351 for (String filename : added) {
352 try {
353 if (!filename.startsWith(repoRelativePath))
354 continue;
355 String projectRelativePath = filename.substring(repoRelativePath.length());
356 IResource member = project.getFile(projectRelativePath);
357 if (member != null && member instanceof IFile) {
358 if (!files.contains(member))
359 files.add((IFile) member);
360 category.add((IFile) member);
361 } else {
362 System.out.println("Couldn't find " + filename);
364 } catch (Exception t) {
365 t.printStackTrace();
366 continue;
367 } // if it's outside the workspace, bad things happen
371 boolean tryAddResource(IFile resource, GitProjectData projectData, ArrayList<IFile> category) {
372 if (files.contains(resource))
373 return false;
375 try {
376 RepositoryMapping repositoryMapping = projectData
377 .getRepositoryMapping(resource);
379 if (isChanged(repositoryMapping, resource)) {
380 files.add(resource);
381 category.add(resource);
382 return true;
384 } catch (Exception e) {
385 e.printStackTrace();
387 return false;
390 private boolean isChanged(RepositoryMapping map, IFile resource) {
391 try {
392 Repository repository = map.getRepository();
393 GitIndex index = repository.getIndex();
394 String repoRelativePath = map.getRepoRelativePath(resource);
395 Entry entry = index.getEntry(repoRelativePath);
396 if (entry != null)
397 return entry.isModified(map.getWorkDir());
398 return false;
399 } catch (UnsupportedEncodingException e) {
400 e.printStackTrace();
401 } catch (IOException e) {
402 e.printStackTrace();
404 return false;
407 @Override
408 public boolean isEnabled() {
409 return getProjectsInRepositoryOfSelectedResources().length > 0;