public static boolean justOneGitRepository(Project project) { if (project.isDisposed()) { return true; } GitRepositoryManager manager = getRepositoryManager(project); return !manager.moreThanOneRoot(); }
@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); }
/** * Prepare delete files handler. * * @param project the project * @param root a vcs root * @param files a files to commit * @param message a message file to use * @param nextCommitAuthor a author for the next commit * @param nextCommitAmend true, if the commit should be amended * @param nextCommitAuthorDate Author date timestamp to override the date of the commit or null if * this overriding is not needed. * @return a simple handler that does the task * @throws VcsException in case of git problem */ private static void commit( Project project, VirtualFile root, Collection<FilePath> files, File message, final String nextCommitAuthor, boolean nextCommitAmend, Date nextCommitAuthorDate) throws VcsException { boolean amend = nextCommitAmend; for (List<String> paths : VcsFileUtil.chunkPaths(root, files)) { GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.COMMIT); handler.setStdoutSuppressed(false); if (amend) { handler.addParameters("--amend"); } else { amend = true; } handler.addParameters("--only", "-F", message.getAbsolutePath()); if (nextCommitAuthor != null) { handler.addParameters("--author=" + nextCommitAuthor); } if (nextCommitAuthorDate != null) { handler.addParameters("--date", COMMIT_DATE_FORMAT.format(nextCommitAuthorDate)); } handler.endOptions(); handler.addParameters(paths); handler.run(); } if (!project.isDisposed()) { GitRepositoryManager manager = GitUtil.getRepositoryManager(project); manager.updateRepository(root); } }
@Nullable public static GitRepository getRepositoryForRootOrLogError( @NotNull Project project, @NotNull VirtualFile root) { GitRepositoryManager manager = getRepositoryManager(project); GitRepository repository = manager.getRepositoryForRoot(root); if (repository == null) { LOG.error("Repository is null for root " + root); } return repository; }
private static void updateUntrackedFilesHolderOnFileAdd( @NotNull Project project, @NotNull VirtualFile root, @NotNull Collection<VirtualFile> addedFiles) { GitRepositoryManager manager = GitUtil.getRepositoryManager(project); if (manager == null) { return; } final GitRepository repository = manager.getRepositoryForRoot(root); if (repository != null) { repository.getUntrackedFilesHolder().remove(addedFiles); } }
/** * Scans the Git roots, selected for commit, for the root which is on a detached HEAD. Returns * null, if all repositories are on the branch. There might be several detached repositories, - * in that case only one is returned. This is because the situation is very rare, while it * requires a lot of additional effort of making a well-formed message. */ @Nullable private DetachedRoot getDetachedRoot() { GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(myPanel.getProject()); for (VirtualFile root : getSelectedRoots()) { GitRepository repository = repositoryManager.getRepositoryForRoot(root); if (repository == null) { continue; } if (!repository.isOnBranch()) { return new DetachedRoot(root, repository.isRebaseInProgress()); } } return null; }
/** * Returns true if the root was loaded with conflict. False is returned in all other cases: in the * case of success and in case of some other error. */ private boolean loadRoot(final VirtualFile root) { LOG.info("loadRoot " + root); myProgressIndicator.setText(GitHandlerUtil.formatOperationName("Unstashing changes to", root)); GitRepository repository = myRepositoryManager.getRepositoryForRoot(root); if (repository == null) { LOG.error("Repository is null for root " + root); return false; } GitSimpleEventDetector conflictDetector = new GitSimpleEventDetector(GitSimpleEventDetector.Event.MERGE_CONFLICT_ON_UNSTASH); GitCommandResult result = myGit.stashPop(repository, conflictDetector); VfsUtil.markDirtyAndRefresh(false, true, false, root); if (result.success()) { return false; } else if (conflictDetector.hasHappened()) { return true; } else { LOG.info("unstash failed " + result.getErrorOutputAsJoinedString()); GitUIUtil.notifyImportantError( myProject, "Couldn't unstash", "<br/>" + result.getErrorOutputAsHtmlString()); return false; } }
@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 public GitFetchResult fetch(@NotNull VirtualFile root, @NotNull String remoteName) { GitRepository repository = myRepositoryManager.getRepositoryForRoot(root); if (repository == null) { return logError("Repository can't be null for " + root, myRepositoryManager.toString()); } GitRemote remote = GitUtil.findRemoteByName(repository, remoteName); if (remote == null) { return logError("Couldn't find remote with the name " + remoteName, null); } String url = remote.getFirstUrl(); if (url == null) { return logError("URL is null for remote " + remote.getName(), null); } return fetchRemote(repository, remote, url); }
@Nullable protected GitRepository findRepository() { if (myRepository != null) { return myRepository; } myRepository = myGitRepositoryManager.getRepositoryForRoot(myContentRoot); return myRepository; }
@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; }
/** * 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); }
@NotNull public static Collection<GitRepository> getRepositoriesFromRoots( @NotNull GitRepositoryManager repositoryManager, @NotNull Collection<VirtualFile> roots) { Collection<GitRepository> repositories = new ArrayList<GitRepository>(roots.size()); for (VirtualFile root : roots) { GitRepository repo = repositoryManager.getRepositoryForRoot(root); if (repo == null) { LOG.error("Repository not found for root " + root); } else { repositories.add(repo); } } return repositories; }
@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()); }
@Override public void actionPerformed(AnActionEvent e) { Data data = Data.collect(e); if (!data.isValid()) { return; } List<VcsFullCommitDetails> details = data.log.getSelectedDetails(); if (details.size() != 1) { return; } VcsFullCommitDetails commit = details.get(0); GitRepositoryManager repositoryManager = ServiceManager.getService(data.project, GitRepositoryManager.class); final GitRepository repository = repositoryManager.getRepositoryForRoot(commit.getRoot()); if (repository == null) { DvcsUtil.noVcsRepositoryForRoot( LOG, commit.getRoot(), data.project, repositoryManager, GitVcs.getInstance(data.project)); return; } actionPerformed(repository, commit); }
@Override public void actionPerformed(@NotNull AnActionEvent event) { final Project project = event.getProject(); if (project == null) { return; } final Set<VirtualFile> conflictedFiles = new TreeSet<VirtualFile>( new Comparator<VirtualFile>() { @Override public int compare(@NotNull VirtualFile f1, @NotNull VirtualFile f2) { return f1.getPresentableUrl().compareTo(f2.getPresentableUrl()); } }); for (Change change : ChangeListManager.getInstance(project).getAllChanges()) { if (change.getFileStatus() != FileStatus.MERGED_WITH_CONFLICTS) { continue; } final ContentRevision before = change.getBeforeRevision(); final ContentRevision after = change.getAfterRevision(); if (before != null) { final VirtualFile file = before.getFile().getVirtualFile(); if (file != null) { conflictedFiles.add(file); } } if (after != null) { final VirtualFile file = after.getFile().getVirtualFile(); if (file != null) { conflictedFiles.add(file); } } } AbstractVcsHelper.getInstance(project) .showMergeDialog( new ArrayList<VirtualFile>(conflictedFiles), GitVcs.getInstance(project).getMergeProvider()); for (VirtualFile conflictedFile : conflictedFiles) { final GitRepository repo = GitRepositoryManager.getInstance(project).getRepositoryForFile(conflictedFile); if (repo != null) { repo.update(GitRepository.TrackedTopic.ALL_CURRENT); } } }
@NotNull private String makeAdditionalInfoByRoot(@NotNull Map<VirtualFile, String> additionalInfo) { if (additionalInfo.isEmpty()) { return ""; } StringBuilder info = new StringBuilder(); if (myRepositoryManager.moreThanOneRoot()) { for (Map.Entry<VirtualFile, String> entry : additionalInfo.entrySet()) { info.append(entry.getValue()) .append(" in ") .append(GitUIUtil.getShortRepositoryName(myProject, entry.getKey())) .append("<br/>"); } } else { info.append(additionalInfo.values().iterator().next()); } return info.toString(); }
/** * Parse changes from lines * * @param root the git root * @return a set of unmerged files * @throws com.intellij.openapi.vcs.VcsException if the input format does not matches expected * format */ private List<VirtualFile> unmergedFiles(final VirtualFile root) throws VcsException { GitRepository repository = myRepositoryManager.getRepositoryForRoot(root); if (repository == null) { LOG.error("Repository not found for root " + root); return Collections.emptyList(); } GitCommandResult result = myGit.getUnmergedFiles(repository); if (!result.success()) { throw new VcsException(result.getErrorOutputAsJoinedString()); } String output = StringUtil.join(result.getOutput(), "\n"); HashSet<String> unmergedPaths = ContainerUtil.newHashSet(); for (StringScanner s = new StringScanner(output); s.hasMoreData(); ) { if (s.isEol()) { s.nextLine(); continue; } s.boundedToken('\t'); String relative = s.line(); unmergedPaths.add(GitUtil.unescapePath(relative)); } if (unmergedPaths.size() == 0) { return Collections.emptyList(); } else { List<File> files = ContainerUtil.map( unmergedPaths, new Function<String, File>() { @Override public File fun(String path) { return new File(root.getPath(), path); } }); return sortVirtualFilesByPresentation(findVirtualFilesWithRefresh(files)); } }
@Nullable private GitRepository getRepository(@NotNull VirtualFile root) { myRepositoryManager.waitUntilInitialized(); return myRepositoryManager.getRepositoryForRoot(root); }
@NotNull @Override public DetailedLogData readFirstBlock( @NotNull VirtualFile root, @NotNull Requirements requirements) throws VcsException { if (!isRepositoryReady(root)) { return LogDataImpl.empty(); } GitRepository repository = ObjectUtils.assertNotNull(myRepositoryManager.getRepositoryForRoot(root)); // need to query more to sort them manually; this doesn't affect performance: it is equal for // -1000 and -2000 int commitCount = requirements.getCommitCount() * 2; String[] params = new String[] {"HEAD", "--branches", "--remotes", "--max-count=" + commitCount}; // NB: not specifying --tags, because it introduces great slowdown if there are many tags, // but makes sense only if there are heads without branch or HEAD labels (rare case). Such cases // are partially handled below. boolean refresh = requirements instanceof VcsLogProviderRequirementsEx && ((VcsLogProviderRequirementsEx) requirements).isRefresh(); DetailedLogData data = GitHistoryUtils.loadMetadata(myProject, root, true, params); Set<VcsRef> safeRefs = data.getRefs(); Set<VcsRef> allRefs = new OpenTHashSet<VcsRef>(safeRefs, DONT_CONSIDER_SHA); Set<VcsRef> branches = readBranches(repository); addNewElements(allRefs, branches); Collection<VcsCommitMetadata> allDetails; Set<String> currentTagNames = null; DetailedLogData commitsFromTags = null; if (!refresh) { allDetails = data.getCommits(); } else { // on refresh: get new tags, which point to commits not from the first block; then get // history, walking down just from these tags // on init: just ignore such tagged-only branches. The price for speed-up. VcsLogProviderRequirementsEx rex = (VcsLogProviderRequirementsEx) requirements; currentTagNames = readCurrentTagNames(root); addOldStillExistingTags(allRefs, currentTagNames, rex.getPreviousRefs()); allDetails = newHashSet(data.getCommits()); Set<String> previousTags = newHashSet(ContainerUtil.mapNotNull(rex.getPreviousRefs(), GET_TAG_NAME)); Set<String> safeTags = newHashSet(ContainerUtil.mapNotNull(safeRefs, GET_TAG_NAME)); Set<String> newUnmatchedTags = remove(currentTagNames, previousTags, safeTags); if (!newUnmatchedTags.isEmpty()) { commitsFromTags = loadSomeCommitsOnTaggedBranches(root, commitCount, newUnmatchedTags); addNewElements(allDetails, commitsFromTags.getCommits()); addNewElements(allRefs, commitsFromTags.getRefs()); } } StopWatch sw = StopWatch.start("sorting commits in " + root.getName()); List<VcsCommitMetadata> sortedCommits = VcsLogSorter.sortByDateTopoOrder(allDetails); sortedCommits = sortedCommits.subList(0, Math.min(sortedCommits.size(), requirements.getCommitCount())); sw.report(); if (LOG.isDebugEnabled()) { validateDataAndReportError( root, allRefs, sortedCommits, data, branches, currentTagNames, commitsFromTags); } return new LogDataImpl(allRefs, sortedCommits); }
@Nullable private GitRepository getRepository(@NotNull VirtualFile root) { return myRepositoryManager.getRepositoryForRoot(root); }
@NotNull protected String makeProgressTitle(@NotNull String operation) { return myRepositoryManager.moreThanOneRoot() ? String.format("%s %s...", operation, myRoot.getName()) : operation + "..."; }
public void checkGitUrl(final T settings) throws ConfigurationException { if (!(myDeploymentSource instanceof ModuleDeploymentSource)) { return; } ModuleDeploymentSource moduleSource = (ModuleDeploymentSource) myDeploymentSource; Module module = moduleSource.getModule(); if (module == null) { return; } File contentRootFile = myDeploymentSource.getFile(); if (contentRootFile == null) { return; } final Project project = module.getProject(); if (myGitRepositoryManager == null) { myGitRepositoryManager = GitUtil.getRepositoryManager(project); } VirtualFile contentRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(contentRootFile); if (contentRoot == null) { return; } GitRepository repository = myGitRepositoryManager.getRepositoryForRoot(contentRoot); if (repository == null) { return; } String expectedName = settings.getDeploymentSourceName(myDeploymentSource); List<String> appNames = myDetector.collectApplicationNames(repository); if (appNames.isEmpty() || appNames.contains(expectedName)) { return; } RuntimeConfigurationWarning warning = new RuntimeConfigurationWarning( "Cloud Git URL found in repository, but it doesn't match the run configuration"); warning.setQuickFix( new Runnable() { @Override public void run() { CloudGitApplication application = new CloudConnectionTask<CloudGitApplication, SC, T, SR>( project, "Searching for application", myServer) { @Override protected CloudGitApplication run(SR serverRuntime) throws ServerRuntimeException { CloudGitDeploymentRuntime deploymentRuntime = (CloudGitDeploymentRuntime) serverRuntime.createDeploymentRuntime( myDeploymentSource, settings, project); return deploymentRuntime.findApplication4Repository(); } }.performSync(); if (application == null) { Messages.showErrorDialog( mySettingsEditor.getComponent(), "No application matching repository URL(s) found in account"); } else { T fixedSettings = mySettingsEditor.getFactory().create(); fixedSettings.setDefaultDeploymentName(false); fixedSettings.setDeploymentName(application.getName()); mySettingsEditor.resetFrom(fixedSettings); } } }); throw warning; }
/** * Preform a merge commit * * @param project a project * @param root a vcs root * @param added added files * @param removed removed files * @param messageFile a message file for commit * @param author an author * @param exceptions the list of exceptions to report * @param partialOperation * @return true if merge commit was successful */ private static boolean mergeCommit( final Project project, final VirtualFile root, final Set<FilePath> added, final Set<FilePath> removed, final File messageFile, final String author, List<VcsException> exceptions, @NotNull final PartialOperation partialOperation) { HashSet<FilePath> realAdded = new HashSet<FilePath>(); HashSet<FilePath> realRemoved = new HashSet<FilePath>(); // perform diff GitSimpleHandler diff = new GitSimpleHandler(project, root, GitCommand.DIFF); diff.setSilent(true); diff.setStdoutSuppressed(true); diff.addParameters("--diff-filter=ADMRUX", "--name-status", "HEAD"); diff.endOptions(); String output; try { output = diff.run(); } catch (VcsException ex) { exceptions.add(ex); return false; } String rootPath = root.getPath(); for (StringTokenizer lines = new StringTokenizer(output, "\n", false); lines.hasMoreTokens(); ) { String line = lines.nextToken().trim(); if (line.length() == 0) { continue; } String[] tk = line.split("\t"); switch (tk[0].charAt(0)) { case 'M': case 'A': realAdded.add(VcsUtil.getFilePath(rootPath + "/" + tk[1])); break; case 'D': realRemoved.add(VcsUtil.getFilePathForDeletedFile(rootPath + "/" + tk[1], false)); break; default: throw new IllegalStateException("Unexpected status: " + line); } } realAdded.removeAll(added); realRemoved.removeAll(removed); if (realAdded.size() != 0 || realRemoved.size() != 0) { final List<FilePath> files = new ArrayList<FilePath>(); files.addAll(realAdded); files.addAll(realRemoved); final Ref<Boolean> mergeAll = new Ref<Boolean>(); try { GuiUtils.runOrInvokeAndWait( new Runnable() { public void run() { String message = GitBundle.message("commit.partial.merge.message", partialOperation.getName()); SelectFilePathsDialog dialog = new SelectFilePathsDialog( project, files, message, null, "Commit All Files", CommonBundle.getCancelButtonText(), false); dialog.setTitle(GitBundle.getString("commit.partial.merge.title")); dialog.show(); mergeAll.set(dialog.isOK()); } }); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { throw new RuntimeException("Unable to invoke a message box on AWT thread", ex); } if (!mergeAll.get()) { return false; } // update non-indexed files if (!updateIndex(project, root, realAdded, realRemoved, exceptions)) { return false; } for (FilePath f : realAdded) { VcsDirtyScopeManager.getInstance(project).fileDirty(f); } for (FilePath f : realRemoved) { VcsDirtyScopeManager.getInstance(project).fileDirty(f); } } // perform merge commit try { GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.COMMIT); handler.setStdoutSuppressed(false); handler.addParameters("-F", messageFile.getAbsolutePath()); if (author != null) { handler.addParameters("--author=" + author); } handler.endOptions(); handler.run(); GitRepositoryManager manager = GitUtil.getRepositoryManager(project); manager.updateRepository(root); } catch (VcsException ex) { exceptions.add(ex); return false; } return true; }
@NotNull private GitRepository getRepository(@NotNull FilePath path) { GitRepository repository = myRepositoryManager.getRepositoryForFile(path); LOG.assertTrue(repository != null, "Repository is null for " + path); return repository; }