private static boolean pushCurrentBranch(
      @NotNull Project project,
      @NotNull GitRepository repository,
      @NotNull String remoteName,
      @NotNull String remoteUrl,
      @NotNull String name,
      @NotNull String url) {
    Git git = ServiceManager.getService(Git.class);

    GitLocalBranch currentBranch = repository.getCurrentBranch();
    if (currentBranch == null) {
      GithubNotifications.showErrorURL(
          project,
          "Can't finish GitHub sharing process",
          "Successfully created project ",
          "'" + name + "'",
          " on GitHub, but initial push failed: no current branch",
          url);
      return false;
    }
    GitCommandResult result =
        git.push(repository, remoteName, remoteUrl, currentBranch.getName(), true);
    if (!result.success()) {
      GithubNotifications.showErrorURL(
          project,
          "Can't finish GitHub sharing process",
          "Successfully created project ",
          "'" + name + "'",
          " on GitHub, but initial push failed:<br/>" + result.getErrorOutputAsHtmlString(),
          url);
      return false;
    }
    return true;
  }
  @NotNull
  private static FetchParams getFetchParams(@NotNull GitRepository repository) {
    GitLocalBranch currentBranch = repository.getCurrentBranch();
    if (currentBranch == null) {
      // fetching current branch is called from Update Project and Push, where branch tracking is
      // pre-checked
      String message = "Current branch can't be null here. \nRepository: " + repository;
      LOG.error(message);
      return new FetchParams(GitFetchResult.error(new Exception(message)));
    }
    GitBranchTrackInfo trackInfo = GitBranchUtil.getTrackInfoForBranch(repository, currentBranch);
    if (trackInfo == null) {
      String message =
          "Tracked info is null for branch " + currentBranch + "\n Repository: " + repository;
      LOG.error(message);
      return new FetchParams(GitFetchResult.error(new Exception(message)));
    }

    GitRemote remote = trackInfo.getRemote();
    String url = remote.getFirstUrl();
    if (url == null) {
      String message = "URL is null for remote " + remote.getName();
      LOG.error(message);
      return new FetchParams(GitFetchResult.error(new Exception(message)));
    }

    return new FetchParams(remote, trackInfo.getRemoteBranch(), url);
  }
 /**
  * Returns the tracking information (remote and the name of the remote branch), or null if we are
  * not on a branch.
  */
 @Nullable
 public static GitBranchTrackInfo getTrackInfoForCurrentBranch(@NotNull GitRepository repository) {
   GitLocalBranch currentBranch = repository.getCurrentBranch();
   if (currentBranch == null) {
     return null;
   }
   return GitBranchUtil.getTrackInfoForBranch(repository, currentBranch);
 }
  /** Performs the actual sync/stash needed before attaching. */
  private void syncOrStash() {
    // When the user edits a document in intelliJ, there are spurious updates to the timestamp of
    // the document for an unspecified amount of time (even though there are no real edits).
    // So, we save-all right before we stash to (help) ensure we don't get a conflict dialog.
    // The conflict dialog happens when the timestamps of the document and file are mismatched.
    // So when we do the git operations, we want the document and file timestamps to match exactly.
    BasicAction.saveAll();

    mySourceRepository = mySyncResult.getLocalRepository();

    if (mySyncResult.needsStash() || mySyncResult.needsSync()) {
      if (mySourceRepository.getCurrentBranch() != null) {
        myOriginalBranchName = mySourceRepository.getCurrentBranch().getName();
      } else {
        myOriginalBranchName = mySourceRepository.getCurrentRevision();
      }
    }

    if (mySyncResult.needsStash()) {
      if (!stash()) {
        return;
      }
    }

    if (!Strings.isNullOrEmpty(mySyncResult.getTargetSyncSHA())) {
      // try to check out that revision.
      final GitBrancher brancher = ServiceManager.getService(myProject, GitBrancher.class);
      if (mySourceRepository == null) {
        LOG.error("unexpected null source repo with a target SHA.");
        return;
      }
      assert mySyncResult.getTargetSyncSHA() != null;
      brancher.checkout(
          mySyncResult.getTargetSyncSHA(),
          Collections.singletonList(mySourceRepository),
          new Runnable() {
            @Override
            public void run() {
              refreshAndClose();
            }
          });
    } else {
      refreshAndClose();
    }
  }
  @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);
  }
  /**
   * 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);
  }
  private String makeDescription() {
    String currentBranchOrRev;
    boolean onBranch;
    if (myRepositories.size() > 1) {
      LOG.assertTrue(myBaseBranch != null, "Branches have unexpectedly diverged");
      currentBranchOrRev = myBaseBranch;
      onBranch = true;
    } else {
      GitRepository repository = myInitialRepository;
      if (repository.isOnBranch()) {
        GitBranch currentBranch = repository.getCurrentBranch();
        assert currentBranch != null;
        currentBranchOrRev = currentBranch.getName();
        onBranch = true;
      } else {
        currentBranchOrRev = repository.getCurrentRevision();
        onBranch = false;
      }
    }

    StringBuilder description = new StringBuilder();
    if (onBranch) {
      description.append(
          GitBundle.message(
              "branch.delete.not_fully_merged.description", myBranchToDelete, myBaseBranch));
    } else {
      description.append(
          GitBundle.message(
              "branch.delete.not_fully_merged.description.not_on_branch",
              myBranchToDelete,
              currentBranchOrRev,
              myBaseBranch));
    }
    if (!myMergedToBranches.isEmpty()) {
      String listOfMergedBranches =
          StringUtil.join(
              StringUtil.surround(ArrayUtil.toStringArray(myMergedToBranches), "<b>", "</b>"),
              ", ");
      description.append("<br>");
      if (myMergedToBranches.size() == 1) {
        description.append(
            GitBundle.message(
                "branch.delete.merged_to.one", myBranchToDelete, listOfMergedBranches));
      } else {
        description.append(
            GitBundle.message(
                "branch.delete.merged_to.many", myBranchToDelete, listOfMergedBranches));
      }
    }
    description.append("<br>").append(GitBundle.message("branch.delete.warning", myBranchToDelete));
    return description.toString();
  }
 /**
  * For each root check that the repository is on branch, and this branch is tracking a remote
  * branch, and the remote branch exists. If it is not true for at least one of roots, notify and
  * return false. If branch configuration is OK for all roots, return true.
  */
 private boolean checkTrackedBranchesConfigured() {
   LOG.info("checking tracked branch configuration...");
   for (GitRepository repository : myRepositories) {
     VirtualFile root = repository.getRoot();
     final GitLocalBranch branch = repository.getCurrentBranch();
     if (branch == null) {
       LOG.info("checkTrackedBranchesConfigured: current branch is null in " + repository);
       notifyImportantError(
           myProject,
           "Can't update: no current branch",
           "You are in 'detached HEAD' state, which means that you're not on any branch"
               + rootStringIfNeeded(root)
               + "Checkout a branch to make update possible.");
       return false;
     }
     GitBranchTrackInfo trackInfo = GitBranchUtil.getTrackInfoForBranch(repository, branch);
     if (trackInfo == null) {
       final String branchName = branch.getName();
       LOG.info(
           String.format(
               "checkTrackedBranchesConfigured: no track info for current branch %s in %s",
               branch, repository));
       String recommendedCommand =
           String.format(
               GitVersionSpecialty.KNOWS_SET_UPSTREAM_TO.existsIn(repository.getVcs().getVersion())
                   ? "git branch --set-upstream-to origin/%1$s %1$s"
                   : "git branch --set-upstream %1$s origin/%1$s",
               branchName);
       notifyImportantError(
           myProject,
           "Can't update: no tracked branch",
           "No tracked branch configured for branch "
               + code(branchName)
               + rootStringIfNeeded(root)
               + "To make your branch track a remote branch call, for example,<br/>"
               + "<code>"
               + recommendedCommand
               + "</code>");
       return false;
     }
     myTrackedBranches.put(root, new GitBranchPair(branch, trackInfo.getRemoteBranch()));
   }
   return true;
 }
  private static List<String> getBranchNamesExceptCurrent(GitRepository repository) {
    List<GitBranch> localBranches =
        new ArrayList<GitBranch>(repository.getBranches().getLocalBranches());
    Collections.sort(localBranches);
    List<GitBranch> remoteBranches =
        new ArrayList<GitBranch>(repository.getBranches().getRemoteBranches());
    Collections.sort(remoteBranches);

    if (repository.isOnBranch()) {
      localBranches.remove(repository.getCurrentBranch());
    }

    final List<String> branchNames = new ArrayList<String>();
    for (GitBranch branch : localBranches) {
      branchNames.add(branch.getName());
    }
    for (GitBranch branch : remoteBranches) {
      branchNames.add(branch.getName());
    }
    return branchNames;
  }
  @Override
  public void actionPerformed(final AnActionEvent event) {
    final Project project = event.getProject();
    assert project != null;

    final VirtualFile file = getAffectedFile(event);

    GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
    GitRepository repository = manager.getRepositoryForFile(file);
    assert repository != null;

    GitBranch currentBranch = repository.getCurrentBranch();
    final String head;
    if (currentBranch == null) {
      String currentRevision = repository.getCurrentRevision();
      LOG.assertTrue(
          currentRevision != null,
          "Current revision is null for "
              + repository
              + ". Compare with branch shouldn't be available for fresh repository");
      head = DvcsUtil.getShortHash(currentRevision);
    } else {
      head = currentBranch.getName();
    }
    final List<String> branchNames = getBranchNamesExceptCurrent(repository);

    // prepare and invoke popup
    final JBList list = new JBList(branchNames);

    JBPopupFactory.getInstance()
        .createListPopupBuilder(list)
        .setTitle("Select branch to compare")
        .setItemChoosenCallback(new OnBranchChooseRunnable(project, file, head, list))
        .setAutoselectOnMouseMove(true)
        .createPopup()
        .showInBestPositionFor(event.getDataContext());
  }