summaryrefslogtreecommitdiff
path: root/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPusher.java
blob: e3c91e0103d92c6d34a1bf19300f0bf987adae7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.zmlx.hg4idea.push;

import com.intellij.dvcs.push.PushSpec;
import com.intellij.dvcs.push.Pusher;
import com.intellij.dvcs.push.VcsPushOptionValue;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.action.HgCommandResultNotifier;
import org.zmlx.hg4idea.command.HgPushCommand;
import org.zmlx.hg4idea.execution.HgCommandResult;
import org.zmlx.hg4idea.execution.HgCommandResultHandler;
import org.zmlx.hg4idea.repo.HgRepository;

import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HgPusher extends Pusher<HgRepository, HgPushSource, HgTarget> {

  private static final Logger LOG = Logger.getInstance(HgPusher.class);
  private static final String ONE = "one";
  private static Pattern PUSH_COMMITS_PATTERN = Pattern.compile(".*(?:added|pushed) (\\d+|" + ONE + ") changeset.*");
  // hg push command has definite exit values for some cases:
  // mercurial returns 0 if push was successful, 1 if nothing to push. see hg push --help
  static int PUSH_SUCCEEDED_EXIT_VALUE = 0;
  static int NOTHING_TO_PUSH_EXIT_VALUE = 1;

  @Override
  public void push(@NotNull Map<HgRepository, PushSpec<HgPushSource, HgTarget>> pushSpecs,
                   @Nullable VcsPushOptionValue vcsPushOptionValue, boolean force) {
    for (Map.Entry<HgRepository, PushSpec<HgPushSource, HgTarget>> entry : pushSpecs.entrySet()) {
      HgRepository repository = entry.getKey();
      PushSpec<HgPushSource, HgTarget> hgSpec = entry.getValue();
      HgTarget destination = hgSpec.getTarget();
      HgPushSource source = hgSpec.getSource();
      Project project = repository.getProject();
      final HgPushCommand pushCommand = new HgPushCommand(project, repository.getRoot(), destination.myTarget);
      pushCommand.setIsNewBranch(true); // set always true, because it just allow mercurial to create a new one if needed
      pushCommand.setForce(force);
      String branchName = source.getBranch();
      if (branchName.equals(repository.getCurrentBookmark())) {
        if (vcsPushOptionValue == HgVcsPushOptionValue.Current) {
          pushCommand.setBookmarkName(branchName);
        }
        else {
          pushCommand.setRevision(branchName);
        }
      }
      else {
        pushCommand.setBranchName(branchName);
      }
      push(project, pushCommand);
    }
  }

  public static void push(@NotNull final Project project, @NotNull HgPushCommand command) {
    final VirtualFile repo = command.getRepo();
    command.execute(new HgCommandResultHandler() {
      @Override
      public void process(@Nullable HgCommandResult result) {
        if (result == null) {
          return;
        }

        if (result.getExitValue() == PUSH_SUCCEEDED_EXIT_VALUE) {
          int commitsNum = getNumberOfPushedCommits(result);
          String successTitle = "Pushed successfully";
          String successDescription = String.format("Pushed %d %s [%s]", commitsNum, StringUtil.pluralize("commit", commitsNum),
                                                    repo.getPresentableName());
          VcsNotifier.getInstance(project).notifySuccess(successTitle, successDescription);
        }
        else if (result.getExitValue() == NOTHING_TO_PUSH_EXIT_VALUE) {
          VcsNotifier.getInstance(project).notifySuccess("Nothing to push");
        }
        else {
          new HgCommandResultNotifier(project).notifyError(result, "Push failed",
                                                           "Failed to push to [" + repo.getPresentableName() + "]");
        }
      }
    });
  }

  static int getNumberOfPushedCommits(@NotNull HgCommandResult result) {
    int numberOfCommitsInAllSubrepos = 0;
    final List<String> outputLines = result.getOutputLines();
    for (String outputLine : outputLines) {
      outputLine = outputLine.trim();
      final Matcher matcher = PUSH_COMMITS_PATTERN.matcher(outputLine);
      if (matcher.matches()) {
        try {
          String numberOfCommits = matcher.group(1);
          numberOfCommitsInAllSubrepos += ONE.equals(numberOfCommits) ? 1 : Integer.parseInt(numberOfCommits);
        }
        catch (NumberFormatException e) {
          LOG.error("getNumberOfPushedCommits ", e);
          return -1;
        }
      }
    }
    return numberOfCommitsInAllSubrepos;
  }
}