Avoid refresh on up-to-date pull operation
[egit/eclipse.git] / org.eclipse.egit.core / src / org / eclipse / egit / core / op / CommitOperation.java
blob54359e050e20cb1d663493b9dfc6879089f7ff75
1 /*******************************************************************************
2 * Copyright (c) 2010-2012, SAP AG and others.
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
11 * Contributors:
12 * Stefan Lay (SAP AG) - initial implementation
13 * Jens Baumgart (SAP AG)
14 * Robin Stocker (independent)
15 *******************************************************************************/
16 package org.eclipse.egit.core.op;
18 import java.io.IOException;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Date;
22 import java.util.HashSet;
23 import java.util.TimeZone;
24 import java.util.regex.Pattern;
26 import org.eclipse.core.resources.IFile;
27 import org.eclipse.core.resources.IWorkspace;
28 import org.eclipse.core.resources.IWorkspaceRunnable;
29 import org.eclipse.core.resources.ResourcesPlugin;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IProgressMonitor;
32 import org.eclipse.core.runtime.SubMonitor;
33 import org.eclipse.core.runtime.jobs.ISchedulingRule;
34 import org.eclipse.egit.core.Activator;
35 import org.eclipse.egit.core.RepositoryUtil;
36 import org.eclipse.egit.core.internal.CoreText;
37 import org.eclipse.egit.core.internal.job.RuleUtil;
38 import org.eclipse.egit.core.project.RepositoryMapping;
39 import org.eclipse.jgit.api.AddCommand;
40 import org.eclipse.jgit.api.CommitCommand;
41 import org.eclipse.jgit.api.Git;
42 import org.eclipse.jgit.api.errors.GitAPIException;
43 import org.eclipse.jgit.api.errors.JGitInternalException;
44 import org.eclipse.jgit.lib.ObjectId;
45 import org.eclipse.jgit.lib.PersonIdent;
46 import org.eclipse.jgit.lib.Repository;
47 import org.eclipse.jgit.lib.RepositoryState;
48 import org.eclipse.jgit.revwalk.RevCommit;
49 import org.eclipse.jgit.revwalk.RevWalk;
50 import org.eclipse.jgit.util.RawParseUtils;
51 import org.eclipse.osgi.util.NLS;
52 import org.eclipse.team.core.TeamException;
54 /**
55 * This class implements the commit of a list of files.
57 public class CommitOperation implements IEGitOperation {
59 private static final Pattern LEADING_WHITESPACE = Pattern
60 .compile("^[\\h\\v]+"); //$NON-NLS-1$
62 Collection<String> commitFileList;
64 private boolean commitWorkingDirChanges = false;
66 private final String author;
68 private final String committer;
70 private final String message;
72 private boolean amending = false;
74 private boolean commitAll = false;
76 private Repository repo;
78 Collection<String> notTracked;
80 private boolean createChangeId;
82 private boolean commitIndex;
84 RevCommit commit = null;
86 /**
87 * @param filesToCommit
88 * a list of files which will be included in the commit
89 * @param notTracked
90 * a list of all untracked files
91 * @param author
92 * the author of the commit
93 * @param committer
94 * the committer of the commit
95 * @param message
96 * the commit message
97 * @throws CoreException
99 public CommitOperation(IFile[] filesToCommit, Collection<IFile> notTracked,
100 String author, String committer, String message) throws CoreException {
101 this.author = author;
102 this.committer = committer;
103 this.message = stripLeadingWhitespace(message);
104 if (filesToCommit != null && filesToCommit.length > 0)
105 setRepository(filesToCommit[0]);
106 if (filesToCommit != null)
107 commitFileList = buildFileList(Arrays.asList(filesToCommit));
108 if (notTracked != null)
109 this.notTracked = buildFileList(notTracked);
113 * @param repository
114 * @param filesToCommit
115 * a list of files which will be included in the commit
116 * @param notTracked
117 * a list of all untracked files
118 * @param author
119 * the author of the commit
120 * @param committer
121 * the committer of the commit
122 * @param message
123 * the commit message
124 * @throws CoreException
126 public CommitOperation(Repository repository, Collection<String> filesToCommit, Collection<String> notTracked,
127 String author, String committer, String message) throws CoreException {
128 this.repo = repository;
129 this.author = author;
130 this.committer = committer;
131 this.message = stripLeadingWhitespace(message);
132 if (filesToCommit != null)
133 commitFileList = new HashSet<String>(filesToCommit);
134 if (notTracked != null)
135 this.notTracked = new HashSet<String>(notTracked);
139 * Constructs a CommitOperation that commits the index
140 * @param repository
141 * @param author
142 * @param committer
143 * @param message
144 * @throws CoreException
146 public CommitOperation(Repository repository, String author, String committer,
147 String message) throws CoreException {
148 this.repo = repository;
149 this.author = author;
150 this.committer = committer;
151 this.message = stripLeadingWhitespace(message);
152 this.commitIndex = true;
155 private String stripLeadingWhitespace(String text) {
156 return text == null ? "" //$NON-NLS-1$
157 : LEADING_WHITESPACE.matcher(text).replaceFirst(""); //$NON-NLS-1$
160 private void setRepository(IFile file) throws CoreException {
161 RepositoryMapping mapping = RepositoryMapping.getMapping(file);
162 if (mapping == null)
163 throw new CoreException(Activator.error(NLS.bind(
164 CoreText.CommitOperation_couldNotFindRepositoryMapping,
165 file), null));
166 repo = mapping.getRepository();
170 * @param repository
172 public void setRepository(Repository repository) {
173 repo = repository;
176 private Collection<String> buildFileList(Collection<IFile> files) throws CoreException {
177 Collection<String> result = new HashSet<String>();
178 for (IFile file : files) {
179 RepositoryMapping mapping = RepositoryMapping.getMapping(file);
180 if (mapping == null)
181 throw new CoreException(Activator.error(NLS.bind(CoreText.CommitOperation_couldNotFindRepositoryMapping, file), null));
182 String repoRelativePath = mapping.getRepoRelativePath(file);
183 result.add(repoRelativePath);
185 return result;
188 @Override
189 public void execute(IProgressMonitor monitor) throws CoreException {
190 IWorkspaceRunnable action = new IWorkspaceRunnable() {
192 @Override
193 public void run(IProgressMonitor actMonitor) throws CoreException {
194 if (commitAll)
195 commitAll();
196 else if (amending || commitFileList != null
197 && commitFileList.size() > 0 || commitIndex) {
198 SubMonitor progress = SubMonitor.convert(actMonitor);
199 progress.setTaskName(
200 CoreText.CommitOperation_PerformingCommit);
201 addUntracked();
202 commit();
203 } else if (commitWorkingDirChanges) {
204 // TODO commit -a
205 } else {
206 // TODO commit
211 ResourcesPlugin.getWorkspace().run(action, getSchedulingRule(),
212 IWorkspace.AVOID_UPDATE, monitor);
215 private void addUntracked() throws CoreException {
216 if (notTracked == null || notTracked.size() == 0) {
217 return;
219 try (Git git = new Git(repo)) {
220 AddCommand addCommand = git.add();
221 boolean fileAdded = false;
222 for (String path : notTracked)
223 if (commitFileList.contains(path)) {
224 addCommand.addFilepattern(path);
225 fileAdded = true;
227 if (fileAdded) {
228 addCommand.call();
230 } catch (GitAPIException e) {
231 throw new CoreException(Activator.error(e.getMessage(), e));
235 @Override
236 public ISchedulingRule getSchedulingRule() {
237 return RuleUtil.getRule(repo);
240 private void commit() throws TeamException {
241 try (Git git = new Git(repo)) {
242 CommitCommand commitCommand = git.commit();
243 setAuthorAndCommitter(commitCommand);
244 commitCommand.setAmend(amending)
245 .setMessage(message)
246 .setInsertChangeId(createChangeId);
247 if (!commitIndex)
248 for(String path:commitFileList)
249 commitCommand.setOnly(path);
250 commit = commitCommand.call();
251 } catch (Exception e) {
252 throw new TeamException(
253 CoreText.MergeOperation_InternalError, e);
259 * @param amending
261 public void setAmending(boolean amending) {
262 this.amending = amending;
267 * @param commitAll
269 public void setCommitAll(boolean commitAll) {
270 this.commitAll = commitAll;
274 * @param createChangeId
275 * <code>true</code> if a Change-Id should be inserted
277 public void setComputeChangeId(boolean createChangeId) {
278 this.createChangeId = createChangeId;
282 * @return the newly created commit if committing was successful, null otherwise.
284 public RevCommit getCommit() {
285 return commit;
288 // TODO: can the commit message be change by the user in case of a merge commit?
289 private void commitAll() throws TeamException {
290 try (Git git = new Git(repo)) {
291 CommitCommand commitCommand = git.commit();
292 setAuthorAndCommitter(commitCommand);
293 commit = commitCommand.setAll(true).setMessage(message)
294 .setInsertChangeId(createChangeId).call();
295 } catch (JGitInternalException e) {
296 throw new TeamException(CoreText.MergeOperation_InternalError, e);
297 } catch (GitAPIException e) {
298 throw new TeamException(e.getLocalizedMessage(), e);
302 private void setAuthorAndCommitter(CommitCommand commitCommand) throws TeamException {
303 final Date commitDate = new Date();
304 final TimeZone timeZone = TimeZone.getDefault();
306 final PersonIdent enteredAuthor = RawParseUtils.parsePersonIdent(author);
307 final PersonIdent enteredCommitter = RawParseUtils.parsePersonIdent(committer);
308 if (enteredAuthor == null)
309 throw new TeamException(NLS.bind(
310 CoreText.CommitOperation_errorParsingPersonIdent, author));
311 if (enteredCommitter == null)
312 throw new TeamException(
313 NLS.bind(CoreText.CommitOperation_errorParsingPersonIdent,
314 committer));
316 PersonIdent authorIdent;
317 if (repo.getRepositoryState().equals(
318 RepositoryState.CHERRY_PICKING_RESOLVED)) {
319 try (RevWalk rw = new RevWalk(repo)) {
320 ObjectId cherryPickHead = repo.readCherryPickHead();
321 authorIdent = rw.parseCommit(cherryPickHead)
322 .getAuthorIdent();
323 } catch (IOException e) {
324 Activator.logError(
325 CoreText.CommitOperation_ParseCherryPickCommitFailed,
327 throw new IllegalStateException(e);
329 } else {
330 authorIdent = new PersonIdent(enteredAuthor, commitDate, timeZone);
333 final PersonIdent committerIdent = new PersonIdent(enteredCommitter, commitDate, timeZone);
335 if (amending) {
336 RepositoryUtil repoUtil = Activator.getDefault().getRepositoryUtil();
337 RevCommit headCommit = repoUtil.parseHeadCommit(repo);
338 if (headCommit != null) {
339 final PersonIdent headAuthor = headCommit.getAuthorIdent();
340 authorIdent = new PersonIdent(enteredAuthor,
341 headAuthor.getWhen(), headAuthor.getTimeZone());
345 commitCommand.setAuthor(authorIdent);
346 commitCommand.setCommitter(committerIdent);