@Override
  public void update(AnActionEvent e) {
    super.update(e);
    Presentation presentation = e.getPresentation();
    Project project = e.getProject();
    if (project == null) {
      presentation.setEnabled(false);
      presentation.setVisible(false);
      return;
    }

    VirtualFile[] vFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY);
    if (vFiles == null || vFiles.length != 1 || vFiles[0] == null) { // only 1 file for now
      presentation.setEnabled(false);
      presentation.setVisible(true);
      return;
    }

    GitRepositoryManager manager = GitUtil.getRepositoryManager(project);

    GitRepository repository = manager.getRepositoryForFile(vFiles[0]);
    if (repository == null || repository.isFresh() || noBranchesToCompare(repository)) {
      presentation.setEnabled(false);
      presentation.setVisible(true);
      return;
    }

    presentation.setEnabled(true);
    presentation.setVisible(true);
  }
示例#2
0
  /**
   * Fetches all specified roots. Once a root has failed, stops and displays the notification. If
   * needed, displays the successful notification at the end.
   *
   * @param roots roots to fetch.
   * @param errorNotificationTitle if specified, this notification title will be used instead of the
   *     standard "Fetch failed". Use this when fetch is a part of a compound process.
   * @param notifySuccess if set to {@code true} successful notification will be displayed.
   * @return true if all fetches were successful, false if at least one fetch failed.
   */
  public boolean fetchRootsAndNotify(
      @NotNull Collection<GitRepository> roots,
      @Nullable String errorNotificationTitle,
      boolean notifySuccess) {
    Map<VirtualFile, String> additionalInfo = new HashMap<VirtualFile, String>();
    for (GitRepository repository : roots) {
      LOG.info("fetching " + repository);
      GitFetchResult result = fetch(repository);
      String ai = result.getAdditionalInfo();
      if (!StringUtil.isEmptyOrSpaces(ai)) {
        additionalInfo.put(repository.getRoot(), ai);
      }
      if (!result.isSuccess()) {
        Collection<Exception> errors = new ArrayList<Exception>(getErrors());
        errors.addAll(result.getErrors());
        displayFetchResult(myProject, result, errorNotificationTitle, errors);
        return false;
      }
    }
    if (notifySuccess) {
      GitUIUtil.notifySuccess(myProject, "", "Fetched successfully");
    }

    String addInfo = makeAdditionalInfoByRoot(additionalInfo);
    if (!StringUtil.isEmptyOrSpaces(addInfo)) {
      Notificator.getInstance(myProject)
          .notify(
              GitVcs.MINOR_NOTIFICATION, "Fetch details", addInfo, NotificationType.INFORMATION);
    }

    return true;
  }
  @Override
  protected void save(@NotNull Collection<VirtualFile> rootsToSave) throws VcsException {
    LOG.info("saving " + rootsToSave);

    for (VirtualFile root : rootsToSave) {
      final String message = GitHandlerUtil.formatOperationName("Stashing changes from", root);
      LOG.info(message);
      final String oldProgressTitle = myProgressIndicator.getText();
      myProgressIndicator.setText(message);
      GitRepository repository = myRepositoryManager.getRepositoryForRoot(root);
      if (repository == null) {
        LOG.error("Repository is null for root " + root);
      } else {
        GitCommandResult result = myGit.stashSave(repository, myStashMessage);
        if (result.success() && somethingWasStashed(result)) {
          myStashedRoots.add(root);
        } else {
          String error =
              "stash " + repository.getRoot() + ": " + result.getErrorOutputAsJoinedString();
          if (!result.success()) {
            throw new VcsException(error);
          } else {
            LOG.warn(error);
          }
        }
      }
      myProgressIndicator.setText(oldProgressTitle);
    }
  }
 @NotNull
 private Set<VcsRef> readBranches(@NotNull GitRepository repository) {
   VirtualFile root = repository.getRoot();
   Collection<GitLocalBranch> localBranches = repository.getBranches().getLocalBranches();
   Collection<GitRemoteBranch> remoteBranches = repository.getBranches().getRemoteBranches();
   Set<VcsRef> refs = new HashSet<VcsRef>(localBranches.size() + remoteBranches.size());
   for (GitLocalBranch localBranch : localBranches) {
     refs.add(
         myVcsObjectsFactory.createRef(
             HashImpl.build(localBranch.getHash()),
             localBranch.getName(),
             GitRefManager.LOCAL_BRANCH,
             root));
   }
   for (GitRemoteBranch remoteBranch : remoteBranches) {
     refs.add(
         myVcsObjectsFactory.createRef(
             HashImpl.build(remoteBranch.getHash()),
             remoteBranch.getNameForLocalOperations(),
             GitRefManager.REMOTE_BRANCH,
             root));
   }
   String currentRevision = repository.getCurrentRevision();
   if (currentRevision != null) { // null => fresh repository
     refs.add(
         myVcsObjectsFactory.createRef(
             HashImpl.build(currentRevision), "HEAD", GitRefManager.HEAD, root));
   }
   return refs;
 }
示例#5
0
 @NotNull
 private GitFetchResult fetchAll(
     @NotNull GitRepository repository, @NotNull GitFetchResult fetchResult) {
   for (GitRemote remote : repository.getRemotes()) {
     String url = remote.getFirstUrl();
     if (url == null) {
       LOG.error("URL is null for remote " + remote.getName());
       continue;
     }
     if (GitHttpAdapter.shouldUseJGit(url)) {
       GitFetchResult res = GitHttpAdapter.fetch(repository, remote, url, null);
       res.addPruneInfo(fetchResult.getPrunedRefs());
       fetchResult = res;
       myErrors.addAll(fetchResult.getErrors());
       if (!fetchResult.isSuccess()) {
         break;
       }
     } else {
       GitFetchResult res = fetchNatively(repository.getRoot(), remote, null);
       res.addPruneInfo(fetchResult.getPrunedRefs());
       fetchResult = res;
       if (!fetchResult.isSuccess()) {
         break;
       }
     }
   }
   return fetchResult;
 }
示例#6
0
  /**
   * Returns absolute paths which have changed remotely comparing to the current branch, i.e.
   * performs <code>git diff --name-only master..origin/master</code>
   *
   * <p>Paths are absolute, Git-formatted (i.e. with forward slashes).
   */
  @NotNull
  public static Collection<String> getPathsDiffBetweenRefs(
      @NotNull Git git,
      @NotNull GitRepository repository,
      @NotNull String beforeRef,
      @NotNull String afterRef)
      throws VcsException {
    List<String> parameters = Arrays.asList("--name-only", "--pretty=format:");
    String range = beforeRef + ".." + afterRef;
    GitCommandResult result = git.diff(repository, parameters, range);
    if (!result.success()) {
      LOG.info(
          String.format(
              "Couldn't get diff in range [%s] for repository [%s]",
              range, repository.toLogString()));
      return Collections.emptyList();
    }

    final Collection<String> remoteChanges = new HashSet<String>();
    for (StringScanner s = new StringScanner(result.getOutputAsJoinedString()); s.hasMoreData(); ) {
      final String relative = s.line();
      if (StringUtil.isEmptyOrSpaces(relative)) {
        continue;
      }
      final String path = repository.getRoot().getPath() + "/" + unescapePath(relative);
      remoteChanges.add(path);
    }
    return remoteChanges;
  }
  /**
   * Checks if update is possible, saves local changes and updates all roots. In case of error shows
   * notification and returns false. If update completes without errors, returns true.
   *
   * <p>Perform update on all roots. 0. Blocks reloading project on external change, saving/syncing
   * on frame deactivation. 1. Checks if update is possible (rebase/merge in progress, no tracked
   * branches...) and provides merge dialog to solve problems. 2. Finds updaters to use (merge or
   * rebase). 3. Preserves local changes if needed (not needed for merge sometimes). 4. Updates via
   * 'git pull' or equivalent. 5. Restores local changes if update completed or failed with error.
   * If update is incomplete, i.e. some unmerged files remain, local changes are not restored.
   */
  @NotNull
  public GitUpdateResult update(final UpdateMethod updateMethod) {
    LOG.info("update started|" + updateMethod);
    String oldText = myProgressIndicator.getText();
    myProgressIndicator.setText("Updating...");

    for (GitRepository repository : myRepositories) {
      repository.update();
    }

    // check if update is possible
    if (checkRebaseInProgress()
        || isMergeInProgress()
        || areUnmergedFiles()
        || !checkTrackedBranchesConfigured()) {
      return GitUpdateResult.NOT_READY;
    }

    if (!fetchAndNotify()) {
      return GitUpdateResult.NOT_READY;
    }

    AccessToken token = DvcsUtil.workingTreeChangeStarted(myProject);
    GitUpdateResult result;
    try {
      result = updateImpl(updateMethod);
    } finally {
      DvcsUtil.workingTreeChangeFinished(myProject, token);
    }
    myProgressIndicator.setText(oldText);
    return result;
  }
  private boolean doDeleteRemote(
      @NotNull String branchName, @NotNull Collection<GitRepository> repositories) {
    Couple<String> pair = splitNameOfRemoteBranch(branchName);
    String remoteName = pair.getFirst();
    String branch = pair.getSecond();

    GitCompoundResult result = new GitCompoundResult(myProject);
    for (GitRepository repository : repositories) {
      GitCommandResult res;
      GitRemote remote = getRemoteByName(repository, remoteName);
      if (remote == null) {
        String error = "Couldn't find remote by name: " + remoteName;
        LOG.error(error);
        res = GitCommandResult.error(error);
      } else {
        res = pushDeletion(repository, remote, branch);
        if (!res.success() && isAlreadyDeletedError(res.getErrorOutputAsJoinedString())) {
          res = myGit.remotePrune(repository, remote);
        }
      }
      result.append(repository, res);
      repository.update();
    }
    if (!result.totalSuccess()) {
      VcsNotifier.getInstance(myProject)
          .notifyError(
              "Failed to delete remote branch " + branchName,
              result.getErrorOutputWithReposIndication());
    }
    return result.totalSuccess();
  }
示例#9
0
 /**
  * {@code git checkout &lt;reference&gt;} <br>
  * {@code git checkout -b &lt;newBranch&gt; &lt;reference&gt;}
  */
 @NotNull
 @Override
 public GitCommandResult checkout(
     @NotNull GitRepository repository,
     @NotNull String reference,
     @Nullable String newBranch,
     boolean force,
     @NotNull GitLineHandlerListener... listeners) {
   final GitLineHandler h =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CHECKOUT);
   h.setSilent(false);
   h.setStdoutSuppressed(false);
   if (force) {
     h.addParameters("--force");
   }
   if (newBranch == null) { // simply checkout
     h.addParameters(reference);
   } else { // checkout reference as new branch
     h.addParameters("-b", newBranch, reference);
   }
   for (GitLineHandlerListener listener : listeners) {
     h.addLineListener(listener);
   }
   return run(h);
 }
 /**
  * TODO this is non-optimal and even incorrect, since such diff shows the difference between
  * committed changes For each of the given repositories looks to the diff between current branch
  * and the given branch and converts it to the list of local changes.
  */
 @NotNull
 Map<GitRepository, List<Change>> collectLocalChangesConflictingWithBranch(
     @NotNull Collection<GitRepository> repositories,
     @NotNull String currentBranch,
     @NotNull String otherBranch) {
   Map<GitRepository, List<Change>> changes = new HashMap<GitRepository, List<Change>>();
   for (GitRepository repository : repositories) {
     try {
       Collection<String> diff =
           GitUtil.getPathsDiffBetweenRefs(myGit, repository, currentBranch, otherBranch);
       List<Change> changesInRepo = convertPathsToChanges(repository, diff, false);
       if (!changesInRepo.isEmpty()) {
         changes.put(repository, changesInRepo);
       }
     } catch (VcsException e) {
       // ignoring the exception: this is not fatal if we won't collect such a diff from other
       // repositories.
       // At worst, use will get double dialog proposing the smart checkout.
       LOG.warn(
           String.format(
               "Couldn't collect diff between %s and %s in %s",
               currentBranch, otherBranch, repository.getRoot()),
           e);
     }
   }
   return changes;
 }
示例#11
0
 @NotNull
 @Override
 public GitCommandResult config(@NotNull GitRepository repository, String... params) {
   final GitLineHandler h =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CONFIG);
   h.addParameters(params);
   return run(h);
 }
 private static boolean noBranchesToCompare(@NotNull GitRepository repository) {
   int locals = repository.getBranches().getLocalBranches().size();
   boolean haveRemotes = !repository.getBranches().getRemoteBranches().isEmpty();
   if (repository.isOnBranch()) { // there are other branches to compare
     return locals < 2 && !haveRemotes;
   }
   return locals == 0 && !haveRemotes; // there are at least 1 branch to compare
 }
 @Override
 @NotNull
 public GitCommandResult resetHard(@NotNull GitRepository repository, @NotNull String revision) {
   final GitLineHandler handler =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.RESET);
   handler.addParameters("--hard", revision);
   return run(handler);
 }
示例#14
0
 @NotNull
 @Override
 public GitCommandResult show(@NotNull GitRepository repository, @NotNull String... params) {
   final GitLineHandler handler =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.SHOW);
   handler.addParameters(params);
   return run(handler);
 }
 @NotNull
 private static Collection<String> getRemoteNames(@NotNull GitRepository repository) {
   Collection<String> names = new ArrayList<String>(repository.getRemotes().size());
   for (GitRemote remote : repository.getRemotes()) {
     names.add(remote.getName());
   }
   return names;
 }
 /**
  * Updates the recently visited branch in the settings. This is to be performed after successful
  * checkout operation.
  */
 protected void updateRecentBranch() {
   if (getRepositories().size() == 1) {
     GitRepository repository = myRepositories.iterator().next();
     mySettings.setRecentBranchOfRepository(repository.getRoot().getPath(), myCurrentBranchOrRev);
   } else {
     mySettings.setRecentCommonBranch(myCurrentBranchOrRev);
   }
 }
 /**
  * Returns the last (tip) commit on the given branch.<br>
  * {@code git rev-list -1 <branchName>}
  */
 @NotNull
 @Override
 public GitCommandResult tip(@NotNull GitRepository repository, @NotNull String branchName) {
   final GitLineHandler h =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.REV_LIST);
   h.addParameters("-1");
   h.addParameters(branchName);
   return run(h);
 }
 /** Create branch without checking it out. {@code git branch <branchName>} */
 @Override
 @NotNull
 public GitCommandResult branchCreate(
     @NotNull GitRepository repository, @NotNull String branchName) {
   final GitLineHandler h =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.BRANCH);
   h.addParameters(branchName);
   return run(h);
 }
 /** Get branches containing the commit. {@code git branch --contains <commit>} */
 @Override
 @NotNull
 public GitCommandResult branchContains(
     @NotNull GitRepository repository, @NotNull String commit) {
   final GitLineHandler h =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.BRANCH);
   h.addParameters("--contains", commit);
   return run(h);
 }
示例#20
0
 @NotNull
 @Override
 public GitCommandResult stashSave(@NotNull GitRepository repository, @NotNull String message) {
   final GitLineHandler h =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.STASH);
   h.addParameters("save");
   h.addParameters(message);
   return run(h);
 }
 @NotNull
 @Override
 public GitCommandResult getUnmergedFiles(@NotNull GitRepository repository) {
   GitLineHandler h =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.LS_FILES);
   h.addParameters("--unmerged");
   h.setSilent(true);
   return run(h);
 }
  @Nullable
  public static GithubCreatePullRequestWorker createPullRequestWorker(
      @NotNull final Project project, @Nullable final VirtualFile file) {
    Git git = ServiceManager.getService(Git.class);

    GitRepository gitRepository = GithubUtil.getGitRepository(project, file);
    if (gitRepository == null) {
      GithubNotifications.showError(
          project, CANNOT_CREATE_PULL_REQUEST, "Can't find git repository");
      return null;
    }
    gitRepository.update();

    Pair<GitRemote, String> remote = GithubUtil.findGithubRemote(gitRepository);
    if (remote == null) {
      GithubNotifications.showError(
          project, CANNOT_CREATE_PULL_REQUEST, "Can't find GitHub remote");
      return null;
    }
    String remoteName = remote.getFirst().getName();
    String remoteUrl = remote.getSecond();
    GithubFullPath path = GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(remoteUrl);
    if (path == null) {
      GithubNotifications.showError(
          project, CANNOT_CREATE_PULL_REQUEST, "Can't process remote: " + remoteUrl);
      return null;
    }

    GitLocalBranch currentBranch = gitRepository.getCurrentBranch();
    if (currentBranch == null) {
      GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "No current branch");
      return null;
    }

    GithubAuthData auth;
    try {
      auth =
          GithubUtil.computeValueInModal(
              project,
              "Access to GitHub",
              new ThrowableConvertor<ProgressIndicator, GithubAuthData, IOException>() {
                @Override
                public GithubAuthData convert(ProgressIndicator indicator) throws IOException {
                  return GithubUtil.getValidAuthDataFromConfig(project, indicator);
                }
              });
    } catch (GithubAuthenticationCanceledException e) {
      return null;
    } catch (IOException e) {
      GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, e);
      return null;
    }

    return new GithubCreatePullRequestWorker(
        project, git, gitRepository, path, remoteName, remoteUrl, currentBranch.getName(), auth);
  }
示例#23
0
 @NotNull
 @Override
 public GitCommandResult stashPop(
     @NotNull GitRepository repository, @NotNull GitLineHandlerListener... listeners) {
   final GitLineHandler handler =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.STASH);
   handler.addParameters("pop");
   addListeners(handler, listeners);
   return run(handler);
 }
 @Override
 @NotNull
 public GitCommandResult resetMerge(@NotNull GitRepository repository, @Nullable String revision) {
   final GitLineHandler handler =
       new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.RESET);
   handler.addParameters("--merge");
   if (revision != null) {
     handler.addParameters(revision);
   }
   return run(handler);
 }
 private boolean isRepositoryReady(@NotNull VirtualFile root) {
   GitRepository repository = getRepository(root);
   if (repository == null) {
     LOG.error("Repository not found for root " + root);
     return false;
   } else if (repository.isFresh()) {
     LOG.info("Fresh repository: " + root);
     return false;
   }
   return true;
 }
 @Override
 public CloudGitApplication deploy() throws ServerRuntimeException {
   CloudGitApplication application = findOrCreateApplication();
   GitRepository repository = findOrCreateRepository();
   addOrResetGitRemote(application, repository);
   add();
   commit();
   repository.update();
   pushApplication(application);
   return application;
 }
 @Nullable
 @Override
 public String getCurrentBranch(@NotNull VirtualFile root) {
   GitRepository repository = myRepositoryManager.getRepositoryForRoot(root);
   if (repository == null) return null;
   String currentBranchName = repository.getCurrentBranchName();
   if (currentBranchName == null && repository.getCurrentRevision() != null) {
     return "HEAD";
   }
   return currentBranchName;
 }
  protected void fetch() throws ServerRuntimeException {
    final VirtualFile contentRoot = getContentRoot();
    GitRepository repository = getRepository();
    final GitLineHandler fetchHandler =
        new GitLineHandler(getProject(), contentRoot, GitCommand.FETCH);
    fetchHandler.setSilent(false);
    fetchHandler.addParameters(getRemoteName());
    fetchHandler.addLineListener(createGitLineHandlerListener());
    performRemoteGitTask(fetchHandler, CloudBundle.getText("fetching.application", getCloudName()));

    repository.update();
  }
  /**
   * Setup remotes combobox. The default remote for the current branch is selected by default. This
   * method gets current branch for the project.
   *
   * @param project the project
   * @param root the git root
   * @param remoteCombobox the combobox to update
   * @param fetchUrl if true, the fetch url is shown instead of push url
   */
  public static void setupRemotes(
      final Project project,
      final VirtualFile root,
      final JComboBox remoteCombobox,
      final boolean fetchUrl) {
    final GitRepository repo = GitRepositoryManager.getInstance(project).getRepositoryForRoot(root);
    assert repo != null : "GitRepository can't be null for root " + root;
    GitBranch gitBranch = repo.getCurrentBranch();

    final String branch = gitBranch != null ? gitBranch.getName() : null;
    setupRemotes(project, root, branch, remoteCombobox, fetchUrl);
  }
示例#30
0
 @NotNull
 @Override
 public List<GitCommit> history(@NotNull GitRepository repository, @NotNull String range) {
   try {
     return GitHistoryUtils.history(repository.getProject(), repository.getRoot(), range);
   } catch (VcsException e) {
     // this is critical, because we need to show the list of unmerged commits, and it shouldn't
     // happen => inform user and developer
     throw new GitExecutionException(
         "Couldn't get [git log " + range + "] on repository [" + repository.getRoot() + "]", e);
   }
 }