git4idea: Implemeted forced merge/rebase and stash options for update operation
[fedora-idea.git] / plugins / git4idea / src / git4idea / GitVcs.java
blob60e1c3dd88993705cb25b1e80ce7361514eddfaf
1 package git4idea;
2 /*
3 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
4 * with the License. You may obtain a copy of the License at:
6 * http://www.apache.org/licenses/LICENSE-2.0
8 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
9 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
10 * the specific language governing permissions and limitations under the License.
12 * Copyright 2007 Decentrix Inc
13 * Copyright 2007 Aspiro AS
14 * Copyright 2008 MQSoftware
15 * Authors: gevession, Erlend Simonsen & Mark Scott
17 * This code was originally derived from the MKS & Mercurial IDEA VCS plugins
20 import com.intellij.execution.ui.ConsoleViewContentType;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator;
23 import com.intellij.openapi.editor.markup.TextAttributes;
24 import com.intellij.openapi.options.Configurable;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.ui.Messages;
27 import com.intellij.openapi.util.Disposer;
28 import com.intellij.openapi.vcs.*;
29 import com.intellij.openapi.vcs.changes.ChangeProvider;
30 import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
31 import com.intellij.openapi.vcs.diff.DiffProvider;
32 import com.intellij.openapi.vcs.diff.RevisionSelector;
33 import com.intellij.openapi.vcs.history.VcsHistoryProvider;
34 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
35 import com.intellij.openapi.vcs.merge.MergeProvider;
36 import com.intellij.openapi.vcs.rollback.RollbackEnvironment;
37 import com.intellij.openapi.vcs.update.UpdateEnvironment;
38 import com.intellij.openapi.vfs.VfsUtil;
39 import com.intellij.openapi.vfs.VirtualFile;
40 import com.intellij.util.EventDispatcher;
41 import git4idea.annotate.GitAnnotationProvider;
42 import git4idea.changes.GitChangeProvider;
43 import git4idea.changes.GitCommittedChangeListProvider;
44 import git4idea.changes.GitOutgoingChangesProvider;
45 import git4idea.checkin.GitCheckinEnvironment;
46 import git4idea.commands.GitHandler;
47 import git4idea.commands.GitSimpleHandler;
48 import git4idea.config.GitVcsConfigurable;
49 import git4idea.config.GitVcsSettings;
50 import git4idea.config.GitVersion;
51 import git4idea.diff.GitDiffProvider;
52 import git4idea.diff.GitTreeDiffProvider;
53 import git4idea.history.GitHistoryProvider;
54 import git4idea.i18n.GitBundle;
55 import git4idea.merge.GitMergeProvider;
56 import git4idea.rollback.GitRollbackEnvironment;
57 import git4idea.update.GitUpdateEnvironment;
58 import git4idea.vfs.*;
59 import org.jetbrains.annotations.NonNls;
60 import org.jetbrains.annotations.NotNull;
61 import org.jetbrains.annotations.Nullable;
63 import java.io.File;
64 import java.util.Collections;
65 import java.util.Date;
66 import java.util.List;
68 /**
69 * Git VCS implementation
71 public class GitVcs extends AbstractVcs {
72 /**
73 * the logger
75 private static final Logger log = Logger.getInstance(GitVcs.class.getName());
76 /**
77 * Vcs name
79 @NonNls public static final String NAME = "Git";
80 private static final VcsKey ourKey = createKey(NAME);
81 /**
82 * change provider
84 private final ChangeProvider myChangeProvider;
85 /**
86 * commit support
88 private final CheckinEnvironment myCheckinEnvironment;
89 /**
90 * rollback support
92 private final RollbackEnvironment myRollbackEnvironment;
93 /**
94 * update support
96 private final GitUpdateEnvironment myUpdateEnvironment;
97 /**
98 * annotate file support
100 private final GitAnnotationProvider myAnnotationProvider;
102 * diff provider
104 private final DiffProvider myDiffProvider;
106 * history provider
108 private final VcsHistoryProvider myHistoryProvider;
110 * cached instance of vcs manager for the project
112 private final ProjectLevelVcsManager myVcsManager;
114 * project vcs settings
116 private final GitVcsSettings mySettings;
118 * configuration support
120 private final Configurable myConfigurable;
122 * selector for revisions
124 private final RevisionSelector myRevSelector;
126 * merge provider
128 private final GitMergeProvider myMergeProvider;
130 * a VFS listener that tracks file addition, deletion, and renaming.
132 private GitVFSListener myVFSListener;
134 * The currently detected git version or null.
136 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) private GitVersion myVersion;
138 * Checking the version lock (used to prevent infinite recursion)
140 private final Object myCheckingVersion = new Object();
142 * The path to executable at the time of version check
144 private String myVersionCheckExcecutable = "";
146 * The changelist provider
148 private GitCommittedChangeListProvider myCommittedChangeListProvider;
150 * The tracker that checks validity of git roots
152 private GitRootTracker myRootTracker;
154 * The dispatcher object for root events
156 private EventDispatcher<GitRootsListener> myRootListeners = EventDispatcher.create(GitRootsListener.class);
158 * The dispatcher object for git configuration events
160 private EventDispatcher<GitConfigListener> myConfigListeners = EventDispatcher.create(GitConfigListener.class);
162 * Tracker for ignored files
164 private GitIgnoreTracker myGitIgnoreTracker;
166 * Configuration file tracker
168 private GitConfigTracker myConfigTracker;
170 private final TreeDiffProvider myTreeDiffProvider;
172 public static GitVcs getInstance(@NotNull Project project) {
173 return (GitVcs)ProjectLevelVcsManager.getInstance(project).findVcsByName(NAME);
176 public GitVcs(@NotNull Project project,
177 @NotNull final GitChangeProvider gitChangeProvider,
178 @NotNull final GitCheckinEnvironment gitCheckinEnvironment,
179 @NotNull final ProjectLevelVcsManager gitVcsManager,
180 @NotNull final GitAnnotationProvider gitAnnotationProvider,
181 @NotNull final GitDiffProvider gitDiffProvider,
182 @NotNull final GitHistoryProvider gitHistoryProvider,
183 @NotNull final GitRollbackEnvironment gitRollbackEnvironment,
184 @NotNull final GitVcsSettings gitSettings) {
185 super(project, NAME);
186 myVcsManager = gitVcsManager;
187 mySettings = gitSettings;
188 myChangeProvider = gitChangeProvider;
189 myCheckinEnvironment = gitCheckinEnvironment;
190 myAnnotationProvider = gitAnnotationProvider;
191 myDiffProvider = gitDiffProvider;
192 myHistoryProvider = gitHistoryProvider;
193 myRollbackEnvironment = gitRollbackEnvironment;
194 myRevSelector = new GitRevisionSelector();
195 myConfigurable = new GitVcsConfigurable(mySettings, myProject);
196 myUpdateEnvironment = new GitUpdateEnvironment(myProject, this, mySettings);
197 myMergeProvider = new GitMergeProvider(myProject);
198 myCommittedChangeListProvider = new GitCommittedChangeListProvider(myProject);
199 myOutgoingChangesProvider = new GitOutgoingChangesProvider(myProject);
200 myTreeDiffProvider = new GitTreeDiffProvider(myProject);
204 * Add listener for git roots
206 * @param listener the listener to add
208 public void addGitConfigListener(GitConfigListener listener) {
209 myConfigListeners.addListener(listener);
213 * Remove listener for git roots
215 * @param listener the listener to remove
217 public void removeGitConfigListener(GitConfigListener listener) {
218 myConfigListeners.removeListener(listener);
223 * Add listener for git roots
225 * @param listener the listener to add
227 public void addGitRootsListener(GitRootsListener listener) {
228 myRootListeners.addListener(listener);
232 * Remove listener for git roots
234 * @param listener the listener to remove
236 public void removeGitRootsListener(GitRootsListener listener) {
237 myRootListeners.removeListener(listener);
241 * {@inheritDoc}
243 @Override
244 public CommittedChangesProvider getCommittedChangesProvider() {
245 // TODO Temporary disabled: return myCommittedChangeListProvider;
246 return null;
250 * {@inheritDoc}
252 @Override
253 public String getRevisionPattern() {
254 // return the full commit hash pattern, possibly other revision formats should be supported as well
255 return "[0-9a-fA-F]{40}";
259 * {@inheritDoc}
261 @Override
262 @NotNull
263 public CheckinEnvironment getCheckinEnvironment() {
264 return myCheckinEnvironment;
268 * {@inheritDoc}
270 @NotNull
271 @Override
272 public MergeProvider getMergeProvider() {
273 return myMergeProvider;
277 * {@inheritDoc}
279 @Override
280 @NotNull
281 public RollbackEnvironment getRollbackEnvironment() {
282 return myRollbackEnvironment;
286 * {@inheritDoc}
288 @Override
289 @NotNull
290 public VcsHistoryProvider getVcsHistoryProvider() {
291 return myHistoryProvider;
295 * {@inheritDoc}
297 @Override
298 @NotNull
299 public String getDisplayName() {
300 return NAME;
304 * {@inheritDoc}
306 @Override
307 @Nullable
308 public UpdateEnvironment getUpdateEnvironment() {
309 return myUpdateEnvironment;
313 * {@inheritDoc}
315 @Override
316 @NotNull
317 public GitAnnotationProvider getAnnotationProvider() {
318 return myAnnotationProvider;
322 * {@inheritDoc}
324 @Override
325 @NotNull
326 public DiffProvider getDiffProvider() {
327 return myDiffProvider;
331 * {@inheritDoc}
333 @Override
334 @Nullable
335 public RevisionSelector getRevisionSelector() {
336 return myRevSelector;
340 * {@inheritDoc}
342 @SuppressWarnings({"deprecation"})
343 @Override
344 @Nullable
345 public VcsRevisionNumber parseRevisionNumber(String revision, FilePath path) {
346 if (revision == null || revision.length() == 0) return null;
347 if (revision.length() > 40) { // date & revision-id encoded string
348 String dateString = revision.substring(0, revision.indexOf("["));
349 String rev = revision.substring(revision.indexOf("[") + 1, 40);
350 Date d = new Date(Date.parse(dateString));
351 return new GitRevisionNumber(rev, d);
353 if (path != null) {
354 try {
355 VirtualFile root = GitUtil.getGitRoot(path);
356 return GitRevisionNumber.resolve(myProject, root, revision);
358 catch (VcsException e) {
359 log.error("Unexpected problem with resolving the git revision number: ", e);
362 return new GitRevisionNumber(revision);
367 * {@inheritDoc}
369 @SuppressWarnings({"deprecation"})
370 @Override
371 @Nullable
372 public VcsRevisionNumber parseRevisionNumber(String revision) {
373 return parseRevisionNumber(revision, null);
377 * {@inheritDoc}
379 @Override
380 public boolean isVersionedDirectory(VirtualFile dir) {
381 return dir.isDirectory() && GitUtil.gitRootOrNull(dir) != null;
385 * {@inheritDoc}
387 @Override
388 protected void start() throws VcsException {
392 * {@inheritDoc}
394 @Override
395 protected void shutdown() throws VcsException {
399 * {@inheritDoc}
401 @Override
402 protected void activate() {
403 if (!myProject.isDefault() && myRootTracker == null) {
404 myRootTracker = new GitRootTracker(this, myProject, myRootListeners.getMulticaster());
406 if (myVFSListener == null) {
407 myVFSListener = new GitVFSListener(myProject, this);
409 if (myConfigTracker == null) {
410 myConfigTracker = new GitConfigTracker(myProject, this, myConfigListeners.getMulticaster());
412 if (myGitIgnoreTracker == null) {
413 myGitIgnoreTracker = new GitIgnoreTracker(myProject, this);
418 * {@inheritDoc}
420 @Override
421 protected void deactivate() {
422 if (myRootTracker != null) {
423 myRootTracker.dispose();
424 myRootTracker = null;
426 if (myVFSListener != null) {
427 Disposer.dispose(myVFSListener);
428 myVFSListener = null;
430 if (myGitIgnoreTracker != null) {
431 myGitIgnoreTracker.dispose();
432 myGitIgnoreTracker = null;
434 if (myConfigTracker != null) {
435 myConfigTracker.dispose();
436 myConfigTracker = null;
441 * {@inheritDoc}
443 @NotNull
444 @Override
445 public synchronized Configurable getConfigurable() {
446 return myConfigurable;
450 * {@inheritDoc}
452 @Nullable
453 public ChangeProvider getChangeProvider() {
454 return myChangeProvider;
458 * Show errors as popup and as messages in vcs view.
460 * @param list a list of errors
461 * @param action an action
463 public void showErrors(@NotNull List<VcsException> list, @NotNull String action) {
464 if (list.size() > 0) {
465 StringBuffer buffer = new StringBuffer();
466 buffer.append("\n");
467 buffer.append(GitBundle.message("error.list.title", action));
468 for (final VcsException exception : list) {
469 buffer.append("\n");
470 buffer.append(exception.getMessage());
472 String msg = buffer.toString();
473 Messages.showErrorDialog(myProject, msg, GitBundle.getString("error.dialog.title"));
478 * Show a plain message in vcs view
480 * @param message a message to show
482 public void showMessages(@NotNull String message) {
483 if (message.length() == 0) return;
484 showMessage(message, ConsoleViewContentType.NORMAL_OUTPUT.getAttributes());
488 * @return vcs settings for the current project
490 @NotNull
491 public GitVcsSettings getSettings() {
492 return mySettings;
496 * Show message in the VCS view
498 * @param message a message to show
499 * @param style a style to use
501 private void showMessage(@NotNull String message, final TextAttributes style) {
502 myVcsManager.addMessageToConsoleWindow(message, style);
506 * Check version and report problem
508 public void checkVersion() {
509 final String executable = mySettings.GIT_EXECUTABLE;
510 synchronized (myCheckingVersion) {
511 if (myVersion != null && myVersionCheckExcecutable.equals(executable)) {
512 return;
514 myVersionCheckExcecutable = executable;
515 // this assignment is done to prevent recursive version check
516 myVersion = GitVersion.INVALID;
517 final String version;
518 try {
519 version = version(myProject).trim();
521 catch (VcsException e) {
522 String reason = (e.getCause() != null ? e.getCause() : e).getMessage();
523 if (!myProject.isDefault()) {
524 showMessage(GitBundle.message("vcs.unable.to.run.git", executable, reason), ConsoleViewContentType.SYSTEM_OUTPUT.getAttributes());
526 return;
528 myVersion = GitVersion.parse(version);
529 if (!GitVersion.parse(version).isSupported() && !myProject.isDefault()) {
530 showMessage(GitBundle.message("vcs.unsupported.version", version, GitVersion.MIN),
531 ConsoleViewContentType.SYSTEM_OUTPUT.getAttributes());
537 * @return the configured version of git
539 public GitVersion version() {
540 checkVersion();
541 return myVersion;
545 * Get the version of configured git
547 * @param project the project
548 * @return a version of configured git
549 * @throws VcsException an error if there is a problem with running git
551 public static String version(Project project) throws VcsException {
552 final String s;
553 GitSimpleHandler h = new GitSimpleHandler(project, new File("."), GitHandler.VERSION);
554 h.setNoSSH(true);
555 h.setSilent(true);
556 s = h.run();
557 return s;
561 * Show command line
563 * @param cmdLine a command line text
565 public void showCommandLine(final String cmdLine) {
566 showMessage(cmdLine, ConsoleViewContentType.SYSTEM_OUTPUT.getAttributes());
570 * The error line
572 * @param line a line to show
574 public void showErrorMessages(final String line) {
575 showMessage(line, ConsoleViewContentType.ERROR_OUTPUT.getAttributes());
579 * {@inheritDoc}
581 @Override
582 public boolean allowsNestedRoots() {
583 return true;
587 * {@inheritDoc}
589 @Override
590 public List<VirtualFile> filterUniqueRoots(List<VirtualFile> in) {
591 Collections.sort(in, FilePathComparator.getInstance());
593 for (int i = 1; i < in.size(); i++) {
594 final VirtualFile child = in.get(i);
595 final VirtualFile childRoot = GitUtil.gitRootOrNull(child);
596 if (childRoot == null) {
597 // non-git file actually, skip it
598 continue;
600 for (int j = i - 1; j >= 0; --j) {
601 final VirtualFile parent = in.get(j);
602 // the method check both that parent is an ancestor of the child and that they share common git root
603 if (VfsUtil.isAncestor(parent, child, false) && VfsUtil.isAncestor(childRoot, parent, false)) {
604 in.remove(i);
605 //noinspection AssignmentToForLoopParameter
606 --i;
607 break;
611 return in;
615 * {@inheritDoc}
617 @Override
618 public RootsConvertor getCustomConvertor() {
619 return GitRootConverter.INSTANCE;
622 public static VcsKey getKey() {
623 return ourKey;
626 @Override
627 public VcsType getType() {
628 return VcsType.distibuted;
631 private final GitOutgoingChangesProvider myOutgoingChangesProvider;
632 @Override
633 protected VcsOutgoingChangesProvider getOutgoingProviderImpl() {
634 return myOutgoingChangesProvider;
637 @Override
638 public RemoteDifferenceStrategy getRemoteDifferenceStrategy() {
639 return RemoteDifferenceStrategy.ASK_TREE_PROVIDER;
642 @Override
643 protected TreeDiffProvider getTreeDiffProviderImpl() {
644 return myTreeDiffProvider;