public boolean checkAction(@NotNull String targetBranch) {
    DiffInfo info = getDiffInfoWithModal(targetBranch);
    if (info == null) {
      return true;
    }
    if (info.getInfo().getBranchToHeadCommits(myGitRepository).isEmpty()) {
      GithubNotifications.showWarningDialog(
          myProject,
          CANNOT_CREATE_PULL_REQUEST,
          "Can't create empty pull request: the branch"
              + getCurrentBranch()
              + " in fully merged to the branch "
              + targetBranch
              + ".");
      return false;
    }
    if (info.getInfo().getHeadToBranchCommits(myGitRepository).isEmpty()) {
      return GithubNotifications.showYesNoDialog(
              myProject,
              "The branch"
                  + targetBranch
                  + " in not fully merged to the branch "
                  + getCurrentBranch(),
              "Do you want to proceed anyway?")
          == Messages.YES;
    }

    return true;
  }
  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;
  }
  public boolean checkAction(@Nullable final BranchInfo branch) {
    if (branch == null) {
      GithubNotifications.showWarningDialog(
          myProject, CANNOT_CREATE_PULL_REQUEST, "Target branch is not selected");
      return false;
    }

    DiffInfo info;
    try {
      info =
          GithubUtil.computeValueInModal(
              myProject,
              "Collecting diff data...",
              new ThrowableConvertor<ProgressIndicator, DiffInfo, IOException>() {
                @Override
                public DiffInfo convert(ProgressIndicator indicator) throws IOException {
                  return GithubUtil.runInterruptable(
                      indicator,
                      new ThrowableComputable<DiffInfo, IOException>() {
                        @Override
                        public DiffInfo compute() throws IOException {
                          return getDiffInfo(branch);
                        }
                      });
                }
              });
    } catch (IOException e) {
      GithubNotifications.showError(myProject, "Can't collect diff data", e);
      return true;
    }
    if (info == null) {
      return true;
    }

    ForkInfo fork = branch.getForkInfo();

    String localBranchName = "'" + myCurrentBranch + "'";
    String targetBranchName = "'" + fork.getRemoteName() + "/" + branch.getRemoteName() + "'";
    if (info.getInfo().getBranchToHeadCommits(myGitRepository).isEmpty()) {
      return GithubNotifications.showYesNoDialog(
          myProject,
          "Do you want to proceed anyway?",
          "Empty pull request: the branch "
              + localBranchName
              + " is fully merged to the branch "
              + targetBranchName);
    }
    if (!info.getInfo().getHeadToBranchCommits(myGitRepository).isEmpty()) {
      return GithubNotifications.showYesNoDialog(
          myProject,
          "Do you want to proceed anyway?",
          "The branch "
              + targetBranchName
              + " is not fully merged to the branch "
              + localBranchName);
    }

    return true;
  }
  @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);
  }
 @Nullable
 private List<GithubFullPath> getAvailableForks(@NotNull ProgressIndicator indicator) {
   try {
     List<GithubFullPath> forks =
         ContainerUtil.map(
             GithubUtil.runTask(
                 myProject,
                 myAuthHolder,
                 indicator,
                 new ThrowableConvertor<GithubConnection, List<GithubRepo>, IOException>() {
                   @NotNull
                   @Override
                   public List<GithubRepo> convert(@NotNull GithubConnection connection)
                       throws IOException {
                     return GithubApiUtil.getForks(
                         connection, mySource.getUser(), mySource.getRepository());
                   }
                 }),
             new Function<GithubRepo, GithubFullPath>() {
               @Override
               public GithubFullPath fun(GithubRepo repo) {
                 return repo.getFullPath();
               }
             });
     if (!forks.contains(mySource)) return ContainerUtil.append(forks, mySource);
     return forks;
   } catch (IOException e) {
     GithubNotifications.showWarning(myProject, "Can't load available forks", e);
     return null;
   }
 }
 private void generateToken() {
   try {
     myToken.setText(
         GithubUtil.computeValueInModal(
             myProject,
             "Access to GitHub",
             new ThrowableConvertor<ProgressIndicator, String, IOException>() {
               @NotNull
               @Override
               public String convert(ProgressIndicator indicator) throws IOException {
                 return GithubUtil.runTaskWithBasicAuthForHost(
                     myProject,
                     GithubAuthDataHolder.createFromSettings(),
                     indicator,
                     getHost(),
                     new ThrowableConvertor<GithubAuthData, String, IOException>() {
                       @NotNull
                       @Override
                       public String convert(@NotNull GithubAuthData auth) throws IOException {
                         return GithubApiUtil.getReadOnlyToken(
                             auth, getRepoAuthor(), getRepoName(), "Intellij tasks plugin");
                       }
                     });
               }
             }));
   } catch (GithubOperationCanceledException ignore) {
   } catch (IOException e) {
     GithubNotifications.showErrorDialog(myProject, "Can't get access token", e);
   }
 }
  @Nullable
  private ForkInfo doAddFork(
      @NotNull GithubFullPath path,
      @Nullable String remoteName,
      @NotNull ProgressIndicator indicator) {
    for (ForkInfo fork : myForks) {
      if (fork.getPath().equals(path)) {
        if (fork.getRemoteName() == null && remoteName != null) {
          fork.setRemoteName(remoteName);
        }
        return fork;
      }
    }

    try {
      List<String> branches = loadBranches(path, indicator);
      String defaultBranch = doLoadDefaultBranch(path, indicator);

      ForkInfo fork = new ForkInfo(path, branches, defaultBranch);
      myForks.add(fork);
      if (remoteName != null) {
        fork.setRemoteName(remoteName);
      }
      return fork;
    } catch (IOException e) {
      GithubNotifications.showWarning(
          myProject, "Can't load branches for " + path.getFullName(), e);
      return null;
    }
  }
  @Nullable
  static String configureUpstreamRemote(
      @NotNull Project project,
      @NotNull GitRepository gitRepository,
      @NotNull ProgressIndicator indicator) {
    GithubRepoDetailed repositoryInfo = loadRepositoryInfo(project, gitRepository, indicator);
    if (repositoryInfo == null) {
      return null;
    }

    if (!repositoryInfo.isFork() || repositoryInfo.getParent() == null) {
      GithubNotifications.showWarningURL(
          project,
          CANNOT_PERFORM_GITHUB_REBASE,
          "GitHub repository ",
          "'" + repositoryInfo.getName() + "'",
          " is not a forked one",
          repositoryInfo.getHtmlUrl());
      return null;
    }

    final String parentRepoUrl =
        GithubUrlUtil.getCloneUrl(repositoryInfo.getParent().getFullPath());

    LOG.info("Adding GitHub parent as a remote host");
    indicator.setText("Adding GitHub parent as a remote host...");

    if (GithubUtil.addGithubRemote(project, gitRepository, "upstream", parentRepoUrl)) {
      return parentRepoUrl;
    } else {
      return null;
    }
  }
  @Nullable
  private static GithubFullPath findRepositoryByUser(
      @NotNull Project project,
      @NotNull String user,
      @NotNull Set<GithubFullPath> forks,
      @NotNull GithubAuthData auth,
      @NotNull GithubRepo source) {
    for (GithubFullPath path : forks) {
      if (StringUtil.equalsIgnoreCase(user, path.getUser())) {
        return path;
      }
    }

    try {
      GithubRepoDetailed target = GithubApiUtil.getDetailedRepoInfo(auth, user, source.getName());
      if (target.getSource() != null
          && StringUtil.equals(target.getSource().getUserName(), source.getUserName())) {
        return target.getFullPath();
      }
    } catch (IOException ignore) {
      // such repo may not exist
    }

    try {
      GithubRepo fork =
          GithubApiUtil.findForkByUser(auth, source.getUserName(), source.getName(), user);
      if (fork != null) {
        return fork.getFullPath();
      }
    } catch (IOException e) {
      GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, e);
    }

    return null;
  }
  @Nullable
  private static String createGithubRepository(
      @NotNull Project project,
      @NotNull GithubAuthDataHolder authHolder,
      @NotNull ProgressIndicator indicator,
      @NotNull final String name,
      @NotNull final String description,
      final boolean isPrivate) {

    try {
      return GithubUtil.runTask(
              project,
              authHolder,
              indicator,
              new ThrowableConvertor<GithubAuthData, GithubRepo, IOException>() {
                @NotNull
                @Override
                public GithubRepo convert(@NotNull GithubAuthData auth) throws IOException {
                  return GithubApiUtil.createRepo(auth, name, description, isPrivate);
                }
              })
          .getHtmlUrl();
    } catch (IOException e) {
      GithubNotifications.showError(project, "Failed to create GitHub Repository", e);
      return null;
    }
  }
  private static void rebaseMyGithubFork(
      @NotNull final Project project, @Nullable final VirtualFile file) {
    final GitRepository gitRepository = GithubUtil.getGitRepository(project, file);
    if (gitRepository == null) {
      GithubNotifications.showError(
          project, CANNOT_PERFORM_GITHUB_REBASE, "Can't find git repository");
      return;
    }

    BasicAction.saveAll();

    new Task.Backgroundable(project, "Rebasing GitHub fork...") {
      @Override
      public void run(@NotNull ProgressIndicator indicator) {
        gitRepository.update();
        String upstreamRemoteUrl = GithubUtil.findUpstreamRemote(gitRepository);

        if (upstreamRemoteUrl == null) {
          LOG.info("Configuring upstream remote");
          indicator.setText("Configuring upstream remote...");
          upstreamRemoteUrl = configureUpstreamRemote(project, gitRepository, indicator);
          if (upstreamRemoteUrl == null) {
            return;
          }
        }

        if (!GithubUrlUtil.isGithubUrl(upstreamRemoteUrl)) {
          GithubNotifications.showError(
              project,
              CANNOT_PERFORM_GITHUB_REBASE,
              "Configured upstream is not a GitHub repository: " + upstreamRemoteUrl);
          return;
        } else {
          final GithubFullPath userAndRepo =
              GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(upstreamRemoteUrl);
          final String login = GithubSettings.getInstance().getLogin();
          if (userAndRepo != null) {
            if (userAndRepo.getUser().equals(login)) {
              GithubNotifications.showError(
                  project,
                  CANNOT_PERFORM_GITHUB_REBASE,
                  "Configured upstream seems to be your own repository: " + upstreamRemoteUrl);
              return;
            }
          }
        }

        LOG.info("Fetching upstream");
        indicator.setText("Fetching upstream...");
        if (!fetchParent(project, gitRepository, indicator)) {
          return;
        }

        LOG.info("Rebasing current branch");
        indicator.setText("Rebasing current branch...");
        rebaseCurrentBranch(project, gitRepository, indicator);
      }
    }.queue();
  }
  public void showDiffDialog(@Nullable final BranchInfo branch) {
    if (branch == null) {
      GithubNotifications.showWarningDialog(
          myProject, "Can't Show Diff", "Target branch is not selected");
      return;
    }

    DiffInfo info;
    try {
      info =
          GithubUtil.computeValueInModal(
              myProject,
              "Collecting diff data...",
              new ThrowableConvertor<ProgressIndicator, DiffInfo, IOException>() {
                @Override
                public DiffInfo convert(ProgressIndicator indicator) throws IOException {
                  return GithubUtil.runInterruptable(
                      indicator,
                      new ThrowableComputable<DiffInfo, IOException>() {
                        @Override
                        public DiffInfo compute() throws IOException {
                          return getDiffInfo(branch);
                        }
                      });
                }
              });
    } catch (IOException e) {
      GithubNotifications.showError(myProject, "Can't collect diff data", e);
      return;
    }
    if (info == null) {
      GithubNotifications.showErrorDialog(myProject, "Can't Show Diff", "Can't collect diff data");
      return;
    }

    GitCompareBranchesDialog dialog =
        new GitCompareBranchesDialog(
            myProject, info.getTo(), info.getFrom(), info.getInfo(), myGitRepository, true);
    dialog.show();
  }
  public void showDiffDialog(@NotNull String branch) {
    if (canShowDiff()) {
      DiffInfo info = getDiffInfoWithModal(branch);
      if (info == null) {
        GithubNotifications.showErrorDialog(myProject, "Can't Show Diff", "Can't get diff info");
        return;
      }

      GitCompareBranchesDialog dialog =
          new GitCompareBranchesDialog(
              myProject, info.getTo(), info.getFrom(), info.getInfo(), myGitRepository);
      dialog.show();
    }
  }
  @Nullable
  private static GithubRepoDetailed loadRepositoryInfo(
      @NotNull Project project,
      @NotNull GitRepository gitRepository,
      @NotNull ProgressIndicator indicator) {
    final String remoteUrl = GithubUtil.findGithubRemoteUrl(gitRepository);
    if (remoteUrl == null) {
      GithubNotifications.showError(
          project, CANNOT_PERFORM_GITHUB_REBASE, "Can't find github remote");
      return null;
    }
    final GithubFullPath userAndRepo = GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(remoteUrl);
    if (userAndRepo == null) {
      GithubNotifications.showError(
          project, CANNOT_PERFORM_GITHUB_REBASE, "Can't process remote: " + remoteUrl);
      return null;
    }

    try {
      return GithubUtil.runWithValidAuth(
          project,
          indicator,
          new ThrowableConvertor<GithubAuthData, GithubRepoDetailed, IOException>() {
            @Override
            @NotNull
            public GithubRepoDetailed convert(GithubAuthData authData) throws IOException {
              return GithubApiUtil.getDetailedRepoInfo(
                  authData, userAndRepo.getUser(), userAndRepo.getRepository());
            }
          });
    } catch (GithubOperationCanceledException e) {
      return null;
    } catch (IOException e) {
      GithubNotifications.showError(project, "Can't load repository info", e);
      return null;
    }
  }
 @Nullable
 private static GithubPullRequest createPullRequest(
     @NotNull Project project,
     @NotNull GithubAuthData auth,
     @NotNull GithubFullPath targetRepo,
     @NotNull String title,
     @NotNull String description,
     @NotNull String head,
     @NotNull String base) {
   try {
     return GithubApiUtil.createPullRequest(
         auth, targetRepo.getUser(), targetRepo.getRepository(), title, description, head, base);
   } catch (IOException e) {
     GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, e);
     return null;
   }
 }
  @Nullable
  private ForkInfo findRepositoryByUser(
      @NotNull final ProgressIndicator indicator, @NotNull final String user) {
    for (ForkInfo fork : myForks) {
      if (StringUtil.equalsIgnoreCase(user, fork.getPath().getUser())) {
        return fork;
      }
    }

    try {
      GithubRepo repo =
          GithubUtil.runTask(
              myProject,
              myAuthHolder,
              indicator,
              new ThrowableConvertor<GithubConnection, GithubRepo, IOException>() {
                @Nullable
                @Override
                public GithubRepo convert(@NotNull GithubConnection connection) throws IOException {
                  try {
                    GithubRepoDetailed target =
                        GithubApiUtil.getDetailedRepoInfo(
                            connection, user, mySource.getRepository());
                    if (target.getSource() != null
                        && StringUtil.equals(
                            target.getSource().getUserName(), mySource.getUser())) {
                      return target;
                    }
                  } catch (IOException ignore) {
                    // such repo may not exist
                  }

                  return GithubApiUtil.findForkByUser(
                      connection, mySource.getUser(), mySource.getRepository(), user);
                }
              });

      if (repo == null) return null;
      return doAddFork(repo, indicator);
    } catch (IOException e) {
      GithubNotifications.showError(myProject, "Can't find repository", e);
      return null;
    }
  }
  @Nullable
  private static GithubInfo2 getAvailableForksInModal(
      @NotNull final Project project,
      @NotNull final GitRepository gitRepository,
      @NotNull final GithubAuthData auth,
      @NotNull final GithubFullPath path) {
    try {
      return GithubUtil.computeValueInModal(
          project,
          "Access to GitHub",
          new ThrowableConvertor<ProgressIndicator, GithubInfo2, IOException>() {
            @Override
            public GithubInfo2 convert(ProgressIndicator indicator) throws IOException {
              final Set<GithubFullPath> forks = new HashSet<GithubFullPath>();

              // GitHub
              GithubRepoDetailed repo =
                  GithubApiUtil.getDetailedRepoInfo(auth, path.getUser(), path.getRepository());
              forks.add(path);
              if (repo.getParent() != null) {
                forks.add(repo.getParent().getFullPath());
              }
              if (repo.getSource() != null) {
                forks.add(repo.getSource().getFullPath());
              }

              // Git
              forks.addAll(getAvailableForksFromGit(gitRepository));

              GithubRepo forkTreeRoot = repo.getSource() == null ? repo : repo.getSource();
              return new GithubInfo2(forks, forkTreeRoot);
            }
          });
    } catch (GithubAuthenticationCanceledException e) {
      return null;
    } catch (IOException e) {
      GithubNotifications.showErrorDialog(project, CANNOT_CREATE_PULL_REQUEST, e);
      return null;
    }
  }
  @Nullable
  private static GithubInfo loadGithubInfoWithModal(
      @NotNull final GithubAuthDataHolder authHolder, @NotNull final Project project) {
    try {
      return GithubUtil.computeValueInModal(
          project,
          "Access to GitHub",
          new ThrowableConvertor<ProgressIndicator, GithubInfo, IOException>() {
            @NotNull
            @Override
            public GithubInfo convert(ProgressIndicator indicator) throws IOException {
              // get existing github repos (network) and validate auth data
              return GithubUtil.runTask(
                  project,
                  authHolder,
                  indicator,
                  new ThrowableConvertor<GithubAuthData, GithubInfo, IOException>() {
                    @NotNull
                    @Override
                    public GithubInfo convert(@NotNull GithubAuthData auth) throws IOException {
                      // check access to private repos (network)
                      GithubUserDetailed userInfo = GithubApiUtil.getCurrentUserDetailed(auth);

                      HashSet<String> names = new HashSet<String>();
                      for (GithubRepo info : GithubApiUtil.getUserRepos(auth)) {
                        names.add(info.getName());
                      }
                      return new GithubInfo(userInfo, names);
                    }
                  });
            }
          });
    } catch (GithubOperationCanceledException e) {
      return null;
    } catch (IOException e) {
      GithubNotifications.showErrorDialog(project, "Failed to connect to GitHub", e);
      return null;
    }
  }
  @Nullable
  private GithubPullRequest doCreatePullRequest(
      @NotNull ProgressIndicator indicator,
      @NotNull final BranchInfo branch,
      @NotNull final String title,
      @NotNull final String description) {
    final ForkInfo fork = branch.getForkInfo();

    final String head = myPath.getUser() + ":" + myCurrentBranch;
    final String base = branch.getRemoteName();

    try {
      return GithubUtil.runTask(
          myProject,
          myAuthHolder,
          indicator,
          new ThrowableConvertor<GithubConnection, GithubPullRequest, IOException>() {
            @NotNull
            @Override
            public GithubPullRequest convert(@NotNull GithubConnection connection)
                throws IOException {
              return GithubApiUtil.createPullRequest(
                  connection,
                  fork.getPath().getUser(),
                  fork.getPath().getRepository(),
                  title,
                  description,
                  head,
                  base);
            }
          });
    } catch (IOException e) {
      GithubNotifications.showError(myProject, CANNOT_CREATE_PULL_REQUEST, e);
      return null;
    }
  }
  @Nullable
  public GithubTargetInfo setTarget(@NotNull final GithubFullPath forkPath) {
    try {
      GithubInfo info =
          GithubUtil.computeValueInModal(
              myProject,
              "Access to GitHub",
              new ThrowableConvertor<ProgressIndicator, GithubInfo, IOException>() {
                @Override
                public GithubInfo convert(ProgressIndicator indicator) throws IOException {
                  // configure remote
                  GitRemote targetRemote = GithubUtil.findGithubRemote(myGitRepository, forkPath);
                  String targetRemoteName = targetRemote == null ? null : targetRemote.getName();
                  if (targetRemoteName == null) {
                    final AtomicReference<Integer> responseRef = new AtomicReference<Integer>();
                    ApplicationManager.getApplication()
                        .invokeAndWait(
                            new Runnable() {
                              @Override
                              public void run() {
                                responseRef.set(
                                    GithubNotifications.showYesNoDialog(
                                        myProject,
                                        "Can't Find Remote",
                                        "Configure remote for '" + forkPath.getUser() + "'?"));
                              }
                            },
                            indicator.getModalityState());
                    if (responseRef.get() == Messages.YES) {
                      targetRemoteName = configureRemote(myProject, myGitRepository, forkPath);
                    }
                  }

                  // load available branches
                  List<String> branches =
                      ContainerUtil.map(
                          GithubApiUtil.getRepoBranches(
                              myAuth, forkPath.getUser(), forkPath.getRepository()),
                          new Function<GithubBranch, String>() {
                            @Override
                            public String fun(GithubBranch githubBranch) {
                              return githubBranch.getName();
                            }
                          });

                  // fetch
                  if (targetRemoteName != null) {
                    GitFetchResult result =
                        new GitFetcher(myProject, indicator, false)
                            .fetch(myGitRepository.getRoot(), targetRemoteName, null);
                    if (!result.isSuccess()) {
                      GitFetcher.displayFetchResult(myProject, result, null, result.getErrors());
                      targetRemoteName = null;
                    }
                  }

                  return new GithubInfo(branches, targetRemoteName);
                }
              });

      myForkPath = forkPath;
      myTargetRemote = info.getTargetRemote();

      myDiffInfos.clear();
      if (canShowDiff()) {
        for (final String branch : info.getBranches()) {
          myDiffInfos.put(
              branch,
              new FutureTask<DiffInfo>(
                  new Callable<DiffInfo>() {
                    @Override
                    public DiffInfo call() throws Exception {
                      return loadDiffInfo(
                          myProject,
                          myGitRepository,
                          myCurrentBranch,
                          myTargetRemote + "/" + branch);
                    }
                  }));
        }
      }

      return new GithubTargetInfo(info.getBranches());
    } catch (GithubAuthenticationCanceledException e) {
      return null;
    } catch (IOException e) {
      GithubNotifications.showErrorDialog(myProject, CANNOT_CREATE_PULL_REQUEST, e);
      return null;
    }
  }
  @Override
  public void doCheckout(@NotNull final Project project, @Nullable final Listener listener) {
    if (!GithubUtil.testGitExecutable(project)) {
      return;
    }
    BasicAction.saveAll();

    List<GithubRepo> availableRepos;
    try {
      availableRepos =
          GithubUtil.computeValueInModal(
              project,
              "Access to GitHub",
              new ThrowableConvertor<ProgressIndicator, List<GithubRepo>, IOException>() {
                @NotNull
                @Override
                public List<GithubRepo> convert(ProgressIndicator indicator) throws IOException {
                  return GithubUtil.runTask(
                      project,
                      GithubAuthDataHolder.createFromSettings(),
                      indicator,
                      new ThrowableConvertor<GithubAuthData, List<GithubRepo>, IOException>() {
                        @NotNull
                        @Override
                        public List<GithubRepo> convert(@NotNull GithubAuthData auth)
                            throws IOException {
                          return GithubApiUtil.getAvailableRepos(auth);
                        }
                      });
                }
              });
    } catch (IOException e) {
      GithubNotifications.showError(project, "Couldn't get the list of GitHub repositories", e);
      return;
    }
    Collections.sort(
        availableRepos,
        new Comparator<GithubRepo>() {
          @Override
          public int compare(final GithubRepo r1, final GithubRepo r2) {
            final int comparedOwners = r1.getUserName().compareTo(r2.getUserName());
            return comparedOwners != 0 ? comparedOwners : r1.getName().compareTo(r2.getName());
          }
        });

    final GitCloneDialog dialog = new GitCloneDialog(project);
    // Add predefined repositories to history
    dialog.prependToHistory("-----------------------------------------------");
    for (int i = availableRepos.size() - 1; i >= 0; i--) {
      dialog.prependToHistory(availableRepos.get(i).getCloneUrl());
    }
    dialog.show();
    if (!dialog.isOK()) {
      return;
    }
    dialog.rememberSettings();
    final VirtualFile destinationParent =
        LocalFileSystem.getInstance().findFileByIoFile(new File(dialog.getParentDirectory()));
    if (destinationParent == null) {
      return;
    }
    final String sourceRepositoryURL = dialog.getSourceRepositoryURL();
    final String directoryName = dialog.getDirectoryName();
    final String parentDirectory = dialog.getParentDirectory();

    Git git = ServiceManager.getService(Git.class);
    GitCheckoutProvider.clone(
        project,
        git,
        listener,
        destinationParent,
        sourceRepositoryURL,
        directoryName,
        parentDirectory);
  }
  public static void shareProjectOnGithub(
      @NotNull final Project project, @Nullable final VirtualFile file) {
    BasicAction.saveAll();

    // get gitRepository
    final GitRepository gitRepository = GithubUtil.getGitRepository(project, file);
    final boolean gitDetected = gitRepository != null;
    final VirtualFile root = gitDetected ? gitRepository.getRoot() : project.getBaseDir();

    // check for existing git repo
    boolean externalRemoteDetected = false;
    if (gitDetected) {
      final String githubRemote = GithubUtil.findGithubRemoteUrl(gitRepository);
      if (githubRemote != null) {
        GithubNotifications.showInfoURL(
            project, "Project is already on GitHub", "GitHub", githubRemote);
        return;
      }
      externalRemoteDetected = !gitRepository.getRemotes().isEmpty();
    }

    final GithubAuthDataHolder authHolder = GithubAuthDataHolder.createFromSettings();

    // get available GitHub repos with modal progress
    final GithubInfo githubInfo = loadGithubInfoWithModal(authHolder, project);
    if (githubInfo == null) {
      return;
    }

    // Show dialog (window)
    final GithubShareDialog shareDialog =
        new GithubShareDialog(
            project, githubInfo.getRepositoryNames(), githubInfo.getUser().canCreatePrivateRepo());
    DialogManager.show(shareDialog);
    if (!shareDialog.isOK()) {
      return;
    }
    final boolean isPrivate = shareDialog.isPrivate();
    final String name = shareDialog.getRepositoryName();
    final String description = shareDialog.getDescription();

    // finish the job in background
    final boolean finalExternalRemoteDetected = externalRemoteDetected;
    new Task.Backgroundable(project, "Sharing project on GitHub...") {
      @Override
      public void run(@NotNull ProgressIndicator indicator) {
        // create GitHub repo (network)
        LOG.info("Creating GitHub repository");
        indicator.setText("Creating GitHub repository...");
        final String url =
            createGithubRepository(project, authHolder, indicator, name, description, isPrivate);
        if (url == null) {
          return;
        }
        LOG.info("Successfully created GitHub repository");

        // creating empty git repo if git is not initialized
        LOG.info("Binding local project with GitHub");
        if (!gitDetected) {
          LOG.info("No git detected, creating empty git repo");
          indicator.setText("Creating empty git repo...");
          if (!createEmptyGitRepository(project, root, indicator)) {
            return;
          }
        }

        GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(project);
        final GitRepository repository = repositoryManager.getRepositoryForRoot(root);
        LOG.assertTrue(repository != null, "GitRepository is null for root " + root);
        if (repository == null) {
          GithubNotifications.showError(
              project, "Failed to create GitHub Repository", "Can't find Git repository");
          return;
        }

        final String remoteUrl = GithubUrlUtil.getCloneUrl(githubInfo.getUser().getLogin(), name);
        final String remoteName = finalExternalRemoteDetected ? "github" : "origin";

        // git remote add origin [email protected]:login/name.git
        LOG.info("Adding GitHub as a remote host");
        indicator.setText("Adding GitHub as a remote host...");
        if (!GithubUtil.addGithubRemote(project, repository, remoteName, remoteUrl)) {
          return;
        }

        // create sample commit for binding project
        if (!performFirstCommitIfRequired(project, root, repository, indicator, name, url)) {
          return;
        }

        // git push origin master
        LOG.info("Pushing to github master");
        indicator.setText("Pushing to github master...");
        if (!pushCurrentBranch(project, repository, remoteName, remoteUrl, name, url)) {
          return;
        }

        GithubNotifications.showInfoURL(
            project, "Successfully shared project on GitHub", name, url);
      }
    }.queue();
  }
  private static boolean performFirstCommitIfRequired(
      @NotNull final Project project,
      @NotNull VirtualFile root,
      @NotNull GitRepository repository,
      @NotNull ProgressIndicator indicator,
      @NotNull String name,
      @NotNull String url) {
    // check if there is no commits
    if (!repository.isFresh()) {
      return true;
    }

    LOG.info("Trying to commit");
    try {
      LOG.info("Adding files for commit");
      indicator.setText("Adding files to git...");

      // ask for files to add
      final List<VirtualFile> trackedFiles =
          ChangeListManager.getInstance(project).getAffectedFiles();
      final Collection<VirtualFile> untrackedFiles =
          filterOutIgnored(project, repository.getUntrackedFilesHolder().retrieveUntrackedFiles());
      trackedFiles.removeAll(untrackedFiles); // fix IDEA-119855

      final List<VirtualFile> allFiles = new ArrayList<VirtualFile>();
      allFiles.addAll(trackedFiles);
      allFiles.addAll(untrackedFiles);

      final Ref<GithubUntrackedFilesDialog> dialogRef = new Ref<GithubUntrackedFilesDialog>();
      ApplicationManager.getApplication()
          .invokeAndWait(
              new Runnable() {
                @Override
                public void run() {
                  GithubUntrackedFilesDialog dialog =
                      new GithubUntrackedFilesDialog(project, allFiles);
                  if (!trackedFiles.isEmpty()) {
                    dialog.setSelectedFiles(trackedFiles);
                  }
                  DialogManager.show(dialog);
                  dialogRef.set(dialog);
                }
              },
              indicator.getModalityState());
      final GithubUntrackedFilesDialog dialog = dialogRef.get();

      final Collection<VirtualFile> files2commit = dialog.getSelectedFiles();
      if (!dialog.isOK() || files2commit.isEmpty()) {
        GithubNotifications.showInfoURL(
            project, "Successfully created empty repository on GitHub", name, url);
        return false;
      }

      Collection<VirtualFile> files2add = ContainerUtil.intersection(untrackedFiles, files2commit);
      Collection<VirtualFile> files2rm = ContainerUtil.subtract(trackedFiles, files2commit);
      Collection<VirtualFile> modified = new HashSet<VirtualFile>(trackedFiles);
      modified.addAll(files2commit);

      GitFileUtils.addFiles(project, root, files2add);
      GitFileUtils.deleteFilesFromCache(project, root, files2rm);

      // commit
      LOG.info("Performing commit");
      indicator.setText("Performing commit...");
      GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.COMMIT);
      handler.addParameters("-m", dialog.getCommitMessage());
      handler.endOptions();
      handler.run();

      VcsFileUtil.refreshFiles(project, modified);
    } catch (VcsException e) {
      LOG.warn(e);
      GithubNotifications.showErrorURL(
          project,
          "Can't finish GitHub sharing process",
          "Successfully created project ",
          "'" + name + "'",
          " on GitHub, but initial commit failed:<br/>" + GithubUtil.getErrorTextFromException(e),
          url);
      return false;
    }
    LOG.info("Successfully created initial commit");
    return true;
  }