940163a37870e4a270e7ba986fab63a1ffeefac7
[idea/community.git] / platform / dvcs-impl / src / com / intellij / dvcs / cherrypick / VcsCherryPickManager.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.dvcs.cherrypick;
3
4 import com.google.common.collect.Lists;
5 import com.intellij.openapi.application.ApplicationManager;
6 import com.intellij.openapi.components.ServiceManager;
7 import com.intellij.openapi.diagnostic.Logger;
8 import com.intellij.openapi.progress.BackgroundTaskQueue;
9 import com.intellij.openapi.progress.ProgressIndicator;
10 import com.intellij.openapi.progress.Task;
11 import com.intellij.openapi.project.Project;
12 import com.intellij.openapi.vcs.AbstractVcs;
13 import com.intellij.openapi.vcs.ProjectLevelVcsManager;
14 import com.intellij.openapi.vcs.VcsKey;
15 import com.intellij.openapi.vcs.VcsNotifier;
16 import com.intellij.openapi.vcs.changes.ChangeListManager;
17 import com.intellij.openapi.vcs.changes.ChangeListManagerEx;
18 import com.intellij.util.containers.ContainerUtil;
19 import com.intellij.util.containers.MultiMap;
20 import com.intellij.vcs.log.CommitId;
21 import com.intellij.vcs.log.VcsFullCommitDetails;
22 import com.intellij.vcs.log.VcsLog;
23 import org.jetbrains.annotations.NotNull;
24 import org.jetbrains.annotations.Nullable;
25
26 import java.util.*;
27
28 public class VcsCherryPickManager {
29   private static final Logger LOG = Logger.getInstance(VcsCherryPickManager.class);
30   @NotNull private final Project myProject;
31   @NotNull private final ProjectLevelVcsManager myProjectLevelVcsManager;
32   @NotNull private final Set<CommitId> myIdsInProgress = ContainerUtil.newConcurrentSet();
33   @NotNull private final BackgroundTaskQueue myTaskQueue;
34
35   public VcsCherryPickManager(@NotNull Project project, @NotNull ProjectLevelVcsManager projectLevelVcsManager) {
36     myProject = project;
37     myProjectLevelVcsManager = projectLevelVcsManager;
38     myTaskQueue = new BackgroundTaskQueue(project, "Cherry-picking");
39   }
40
41   public void cherryPick(@NotNull VcsLog log) {
42     log.requestSelectedDetails( details -> myTaskQueue.run(new CherryPickingTask(ContainerUtil.reverse(details))));
43   }
44
45   public boolean isCherryPickAlreadyStartedFor(@NotNull List<? extends CommitId> commits) {
46     for (CommitId commit : commits) {
47       if (myIdsInProgress.contains(commit)) {
48         return true;
49       }
50     }
51     return false;
52   }
53
54   @Nullable
55   private VcsCherryPicker getCherryPickerForCommit(@NotNull VcsFullCommitDetails commitDetails) {
56     AbstractVcs vcs = myProjectLevelVcsManager.getVcsFor(commitDetails.getRoot());
57     if (vcs == null) return null;
58     VcsKey key = vcs.getKeyInstanceMethod();
59     return getCherryPickerFor(key);
60   }
61
62   @Nullable
63   public VcsCherryPicker getCherryPickerFor(@NotNull final VcsKey key) {
64     return ContainerUtil.find(VcsCherryPicker.EXTENSION_POINT_NAME.getExtensions(myProject),
65                               picker -> picker.getSupportedVcs().equals(key));
66   }
67
68   private class CherryPickingTask extends Task.Backgroundable {
69     @NotNull private final List<? extends VcsFullCommitDetails> myAllDetailsInReverseOrder;
70     @NotNull private final ChangeListManagerEx myChangeListManager;
71
72     CherryPickingTask(@NotNull List<? extends VcsFullCommitDetails> detailsInReverseOrder) {
73       super(VcsCherryPickManager.this.myProject, "Cherry-Picking");
74       myAllDetailsInReverseOrder = detailsInReverseOrder;
75       myChangeListManager = (ChangeListManagerEx)ChangeListManager.getInstance(myProject);
76       myChangeListManager.blockModalNotifications();
77     }
78
79     @Nullable
80     private VcsCherryPicker getCherryPickerOrReportError(@NotNull VcsFullCommitDetails details) {
81       CommitId commitId = new CommitId(details.getId(), details.getRoot());
82       if (myIdsInProgress.contains(commitId)) {
83         showError("Cherry pick process is already started for commit " +
84                   commitId.getHash().toShortString() +
85                   " from root " +
86                   commitId.getRoot().getName());
87         return null;
88       }
89       myIdsInProgress.add(commitId);
90
91       VcsCherryPicker cherryPicker = getCherryPickerForCommit(details);
92       if (cherryPicker == null) {
93         showError(
94           "Cherry pick is not supported for commit " + details.getId().toShortString() + " from root " + details.getRoot().getName());
95         return null;
96       }
97       return cherryPicker;
98     }
99
100     public void showError(@NotNull String message) {
101       VcsNotifier.getInstance(myProject).notifyWeakError(message);
102       LOG.warn(message);
103     }
104
105     @Override
106     public void run(@NotNull ProgressIndicator indicator) {
107       try {
108         boolean isOk = true;
109         MultiMap<VcsCherryPicker, VcsFullCommitDetails> groupedCommits = createArrayMultiMap();
110         for (VcsFullCommitDetails details : myAllDetailsInReverseOrder) {
111           VcsCherryPicker cherryPicker = getCherryPickerOrReportError(details);
112           if (cherryPicker == null) {
113             isOk = false;
114             break;
115           }
116           groupedCommits.putValue(cherryPicker, details);
117         }
118
119         if (isOk) {
120           for (Map.Entry<VcsCherryPicker, Collection<VcsFullCommitDetails>> entry : groupedCommits.entrySet()) {
121             entry.getKey().cherryPick(Lists.newArrayList(entry.getValue()));
122           }
123         }
124       }
125       finally {
126         ApplicationManager.getApplication().invokeLater(() -> {
127           myChangeListManager.unblockModalNotifications();
128           for (VcsFullCommitDetails details : myAllDetailsInReverseOrder) {
129             myIdsInProgress.remove(new CommitId(details.getId(), details.getRoot()));
130           }
131         });
132       }
133     }
134
135     @NotNull
136     public MultiMap<VcsCherryPicker, VcsFullCommitDetails> createArrayMultiMap() {
137       return new MultiMap<VcsCherryPicker, VcsFullCommitDetails>() {
138         @NotNull
139         @Override
140         protected Collection<VcsFullCommitDetails> createCollection() {
141           return new ArrayList<>();
142         }
143       };
144     }
145   }
146
147   public static VcsCherryPickManager getInstance(@NotNull Project project) {
148     return ServiceManager.getService(project, VcsCherryPickManager.class);
149   }
150 }