Continued work to remove a bunch of Hg-related commands.
[nbgit.git] / src / org / netbeans / modules / git / ui / pull / PullAction.java
blob37f488da395888793e09570501436f418a5531a4
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.
29 * Portions Copyright 2008 Alexander Coles (Ikonoklastik Productions).
31 * If you wish your version of this file to be governed by only the CDDL
32 * or only the GPL Version 2, indicate your decision by adding
33 * "[Contributor] elects to include this software in this distribution
34 * under the [CDDL or GPL Version 2] license." If you do not indicate a
35 * single choice of license, a recipient has the option to distribute
36 * your version of this file under either the CDDL, the GPL Version 2 or
37 * to extend the choice of license to its licensees as provided above.
38 * However, if you add GPL Version 2 code and therefore, elected the GPL
39 * Version 2 license, then the option applies only if the new code is
40 * made subject to such option by the copyright holder.
42 package org.netbeans.modules.git.ui.pull;
45 import java.awt.event.ActionEvent;
46 import java.io.File;
47 import java.util.ArrayList;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Set;
51 import javax.swing.Action;
52 import javax.swing.JOptionPane;
53 import org.netbeans.api.project.Project;
54 import org.netbeans.modules.git.FileInformation;
55 import org.netbeans.modules.git.FileStatusCache;
56 import org.netbeans.modules.git.Git;
57 import org.netbeans.modules.git.GitException;
58 import org.netbeans.modules.git.GitProgressSupport;
59 import org.netbeans.modules.git.OutputLogger;
60 import org.netbeans.modules.git.ui.actions.ContextAction;
61 import org.netbeans.modules.git.ui.merge.MergeAction;
62 import org.netbeans.modules.git.util.GitCommand;
63 import org.netbeans.modules.git.util.GitProjectUtils;
64 import org.netbeans.modules.git.util.GitUtils;
65 import org.netbeans.modules.versioning.spi.VCSContext;
66 import org.openide.DialogDisplayer;
67 import org.openide.NotifyDescriptor;
68 import org.openide.filesystems.FileObject;
69 import org.openide.filesystems.FileUtil;
70 import org.openide.util.NbBundle;
71 import org.openide.util.RequestProcessor;
72 import org.openide.windows.IOProvider;
73 import org.openide.windows.InputOutput;
74 import org.openide.windows.OutputWriter;
77 /**
78 * Pull action for Git:
79 * git pull - fetch from, and merge changes with the specified repository and
80 * branch.
82 * @author John Rice
84 public class PullAction extends ContextAction {
85 private static final String CHANGESET_FILES_PREFIX = "files:"; //NOI18N
87 public enum PullType {
88 LOCAL, OTHER
91 private final VCSContext context;
93 public PullAction(String name, VCSContext context) {
94 this.context = context;
95 putValue(Action.NAME, name);
98 public void performAction(ActionEvent e) {
99 final File root = GitUtils.getRootFile(context);
100 if (root == null) {
101 OutputLogger logger = OutputLogger.getLogger(Git.GIT_OUTPUT_TAB_TITLE);
102 logger.outputInRed( NbBundle.getMessage(PullAction.class,"MSG_PULL_TITLE")); // NOI18N
103 logger.outputInRed( NbBundle.getMessage(PullAction.class,"MSG_PULL_TITLE_SEP")); // NOI18N
104 logger.outputInRed(
105 NbBundle.getMessage(PullAction.class, "MSG_PULL_NOT_SUPPORTED_INVIEW_INFO")); // NOI18N
106 logger.output(""); // NOI18N
107 JOptionPane.showMessageDialog(null,
108 NbBundle.getMessage(PullAction.class, "MSG_PULL_NOT_SUPPORTED_INVIEW"),// NOI18N
109 NbBundle.getMessage(PullAction.class, "MSG_PULL_NOT_SUPPORTED_INVIEW_TITLE"),// NOI18N
110 JOptionPane.INFORMATION_MESSAGE);
111 logger.closeLog();
112 return;
114 pull(context);
117 public static boolean confirmWithLocalChanges(File rootFile, Class bundleLocation, String title, String query,
118 List<String> listIncoming, OutputLogger logger) {
119 FileStatusCache cache = Git.getInstance().getFileStatusCache();
120 File[] roots = new File[1];
121 roots[0] = rootFile;
122 File[] localModNewFiles = cache.listFiles(roots,
123 FileInformation.STATUS_VERSIONED_MODIFIEDLOCALLY |
124 FileInformation.STATUS_VERSIONED_CONFLICT |
125 FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY);
126 List<String> listIncomingAndLocalMod = new ArrayList<String>();
127 Set<String> setFiles = new HashSet<String>();
128 String filesStr;
129 String[] aFileStr;
130 String root = rootFile.getAbsolutePath();
132 for(String s: listIncoming){
133 if(s.indexOf(CHANGESET_FILES_PREFIX) == 0){
134 filesStr = (s.substring(CHANGESET_FILES_PREFIX.length())).trim();
135 aFileStr = filesStr.split(" ");
136 for(String fileStr: aFileStr){
137 setFiles.add(root + File.separator + fileStr);
138 break;
142 for(File f : localModNewFiles){
143 for(String s : setFiles){
144 if( s.equals(f.getAbsolutePath())){
145 listIncomingAndLocalMod.add(s);
150 if (listIncomingAndLocalMod != null && listIncomingAndLocalMod.size() > 0) {
151 logger.outputInRed(NbBundle.getMessage(PullAction.class, "MSG_PULL_OVERWRITE_LOCAL")); // NOI18N
152 logger.output(listIncomingAndLocalMod);
153 int response = JOptionPane.showOptionDialog(null,
154 NbBundle.getMessage(bundleLocation, query), NbBundle.getMessage(bundleLocation, title),
155 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null);
157 if (response == JOptionPane.NO_OPTION) {
158 return false;
161 return true;
165 static void annotateChangeSets(List<String> list, Class bundleLocation, String title) {
166 InputOutput io = IOProvider.getDefault().getIO(Git.GIT_OUTPUT_TAB_TITLE, false);
167 io.select();
168 OutputWriter out = io.getOut();
169 OutputWriter outRed = io.getErr();
170 outRed.println(NbBundle.getMessage(bundleLocation, title));
171 for (String s : list) {
172 if (s.indexOf(Git.CHANGESET_STR) == 0) {
173 outRed.println(s);
174 } else if (!s.equals("")) {
175 out.println(s);
178 out.println("");
179 out.close();
180 outRed.close();
183 public static void pull(final VCSContext ctx) {
184 final File root = GitUtils.getRootFile(ctx);
185 if (root == null) return;
186 String repository = root.getAbsolutePath();
188 RequestProcessor rp = Git.getInstance().getRequestProcessor(repository);
189 GitProgressSupport support = new GitProgressSupport() {
190 public void perform() { getDefaultAndPerformPull(ctx, root, this.getLogger()); } };
192 support.start(rp, repository, org.openide.util.NbBundle.getMessage(PullAction.class, "MSG_PULL_PROGRESS")); // NOI18N
195 @Override
196 public boolean isEnabled() {
197 return GitUtils.getRootFile(context) != null;
200 static void getDefaultAndPerformPull(VCSContext ctx, File root, OutputLogger logger) {
201 final String pullPath = GitCommand.getPullDefault(root);
202 // If the repository has no default pull path then inform user
203 if(pullPath == null) {
204 logger.outputInRed( NbBundle.getMessage(PullAction.class,"MSG_PULL_TITLE")); // NOI18N
205 logger.outputInRed( NbBundle.getMessage(PullAction.class,"MSG_PULL_TITLE_SEP")); // NOI18N
206 logger.output(NbBundle.getMessage(PullAction.class, "MSG_NO_DEFAULT_PULL_SET_MSG")); // NOI18N
207 logger.outputInRed(NbBundle.getMessage(PullAction.class, "MSG_PULL_DONE")); // NOI18N
208 logger.output(""); // NOI18N
209 JOptionPane.showMessageDialog(null,
210 NbBundle.getMessage(PullAction.class,"MSG_NO_DEFAULT_PULL_SET"),
211 NbBundle.getMessage(PullAction.class,"MSG_PULL_TITLE"),
212 JOptionPane.INFORMATION_MESSAGE);
213 return;
215 // We assume that if fromPrjName is null that it is a remote pull.
216 // This is not true as a project which is in a subdirectory of a
217 // repository will report a project name of null. This does no harm.
218 final String fromPrjName = GitProjectUtils.getProjectName(new File(pullPath));
219 Project proj = GitUtils.getProject(ctx);
220 final String toPrjName = GitProjectUtils.getProjectName(proj);
221 performPull(fromPrjName != null ? PullType.LOCAL : PullType.OTHER, ctx, root, pullPath, fromPrjName, toPrjName, logger);
224 static void performPull(PullType type, VCSContext ctx, File root, String pullPath, String fromPrjName, String toPrjName, OutputLogger logger) {
225 if(root == null || pullPath == null) return;
226 File bundleFile = null;
228 try {
229 logger.outputInRed(NbBundle.getMessage(PullAction.class, "MSG_PULL_TITLE")); // NOI18N
230 logger.outputInRed(NbBundle.getMessage(PullAction.class, "MSG_PULL_TITLE_SEP")); // NOI18N
232 List<String> listIncoming;
233 if(type == PullType.LOCAL){
234 listIncoming = GitCommand.doIncoming(root, logger);
235 }else{
236 for (int i = 0; i < 10000; i++) {
237 if (!new File(root.getParentFile(), root.getName() + "_bundle" + i).exists()) { // NOI18N
238 bundleFile = new File(root.getParentFile(), root.getName() + "_bundle" + i); // NOI18N
239 break;
242 listIncoming = GitCommand.doIncoming(root, pullPath, bundleFile, logger);
244 if (listIncoming == null || listIncoming.isEmpty()) return;
246 boolean bNoChanges = GitCommand.isNoChanges(listIncoming.get(listIncoming.size() - 1));
248 // Warn User when there are Local Changes present that Pull will overwrite
249 if (!bNoChanges && !confirmWithLocalChanges(root, PullAction.class, "MSG_PULL_LOCALMODS_CONFIRM_TITLE", "MSG_PULL_LOCALMODS_CONFIRM_QUERY", listIncoming, logger)) { // NOI18N
250 logger.outputInRed(NbBundle.getMessage(PullAction.class, "MSG_PULL_LOCALMODS_CANCEL")); // NOI18N
251 logger.output(""); // NOI18N
252 return;
255 // Do Pull if there are changes to be pulled
256 List<String> list;
257 if (bNoChanges) {
258 list = listIncoming;
259 } else {
260 if(type == PullType.LOCAL){
261 list = GitCommand.doPull(root, logger);
262 }else{
263 //list = GitCommand.doUnbundle(root, bundleFile, logger);
264 list = GitCommand.doPull(root, logger);
268 if (list != null && !list.isEmpty()) {
270 if (!bNoChanges) {
271 annotateChangeSets(GitUtils.replaceHttpPassword(listIncoming), PullAction.class, "MSG_CHANGESETS_TO_PULL"); // NOI18N
274 logger.output(GitUtils.replaceHttpPassword(list));
275 if (fromPrjName != null) {
276 logger.outputInRed(NbBundle.getMessage(
277 PullAction.class, "MSG_PULL_FROM", fromPrjName, GitUtils.stripDoubleSlash(GitUtils.replaceHttpPassword(pullPath)))); // NOI18N
278 } else {
279 logger.outputInRed(NbBundle.getMessage(
280 PullAction.class, "MSG_PULL_FROM_NONAME", GitUtils.stripDoubleSlash(GitUtils.replaceHttpPassword(pullPath)))); // NOI18N
282 if (toPrjName != null) {
283 logger.outputInRed(NbBundle.getMessage(
284 PullAction.class, "MSG_PULL_TO", toPrjName, root)); // NOI18N
285 } else {
286 logger.outputInRed(NbBundle.getMessage(
287 PullAction.class, "MSG_PULL_TO_NONAME", root)); // NOI18N
290 // Handle Merge - both automatic and merge with conflicts
291 boolean bMergeNeededDueToPull = GitCommand.isMergeNeededMsg(list.get(list.size() - 1));
292 boolean bConfirmMerge = false;
293 if(bMergeNeededDueToPull){
294 bConfirmMerge = GitUtils.confirmDialog(
295 PullAction.class, "MSG_PULL_MERGE_CONFIRM_TITLE", "MSG_PULL_MERGE_CONFIRM_QUERY"); // NOI18N
296 } else {
297 boolean bOutStandingUncommittedMerges = GitCommand.isMergeAbortUncommittedMsg(list.get(list.size() - 1));
298 if(bOutStandingUncommittedMerges){
299 bConfirmMerge = GitUtils.confirmDialog(
300 PullAction.class, "MSG_PULL_MERGE_CONFIRM_TITLE", "MSG_PULL_MERGE_UNCOMMITTED_CONFIRM_QUERY"); // NOI18N
303 if (bConfirmMerge) {
304 logger.output(""); // NOI18N
305 logger.outputInRed(NbBundle.getMessage(PullAction.class, "MSG_PULL_MERGE_DO")); // NOI18N
306 MergeAction.doMergeAction(root, null, logger);
307 } else {
308 List<String> headRevList = GitCommand.getHeadRevisions(root);
309 if (headRevList != null && headRevList.size() > 1){
310 MergeAction.printMergeWarning(headRevList, logger);
315 if (!bNoChanges) {
316 GitUtils.forceStatusRefreshProject(ctx);
317 // refresh filesystem to take account of deleted files.
318 FileObject rootObj = FileUtil.toFileObject(root);
319 try {
320 rootObj.getFileSystem().refresh(true);
321 } catch (java.lang.Exception ex) {
325 } catch (GitException ex) {
326 NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(ex);
327 DialogDisplayer.getDefault().notifyLater(e);
328 } finally {
329 if (bundleFile != null) {
330 bundleFile.delete();
332 logger.outputInRed(NbBundle.getMessage(PullAction.class, "MSG_PULL_DONE")); // NOI18N
333 logger.output(""); // NOI18N