List<VcsException> (@NotNull Project project, @NotNull GitRepository repository, @NotNull Collection<? extends ChangedPath> rootChanges, @NotNull Set<? extends ChangedPath> changedWithIndex, @NotNull File messageFile, @NotNull GitCommitOptions commitOptions) { List<VcsException> exceptions = new ArrayList<>(); try { Set<FilePath> added = map2SetNotNull(rootChanges, it -> it.afterPath); Set<FilePath> removed = map2SetNotNull(rootChanges, it -> it.beforePath); VirtualFile root = repository.getRoot(); String rootPath = root.getPath(); List<FilePath> unmergedFiles = GitChangeUtils.getUnmergedFiles(repository); if (!unmergedFiles.isEmpty()) { throw new VcsException(GitBundle.message("error.commit.cant.commit.with.unmerged.paths")); } // Check what is staged besides our changes Collection<GitDiffChange> stagedChanges = GitChangeUtils.getStagedChanges(project, root); LOG.debug("Found staged changes: " + getLogStringGitDiffChanges(rootPath, stagedChanges)); Collection<ChangedPath> excludedStagedChanges = new ArrayList<>(); Collection<FilePath> excludedStagedAdditions = new ArrayList<>(); processExcludedPaths(stagedChanges, added, removed, (before, after) -> { if (before != null || after != null) excludedStagedChanges.add(new ChangedPath(before, after)); if (before == null && after != null) excludedStagedAdditions.add(after); }); // Find files with 'AD' status, we will not be able to restore them after using 'git add' command, // getting "pathspec 'file.txt' did not match any files" error (and preventing us from adding other files). Collection<GitDiffChange> unstagedChanges = GitChangeUtils.getUnstagedChanges(project, root, excludedStagedAdditions, false); LOG.debug("Found unstaged changes: " + getLogStringGitDiffChanges(rootPath, unstagedChanges)); Set<FilePath> excludedUnstagedDeletions = new HashSet<>(); processExcludedPaths(unstagedChanges, added, removed, (before, after) -> { if (before != null && after == null) excludedUnstagedDeletions.add(before); }); if (!excludedStagedChanges.isEmpty()) { // Reset staged changes which are not selected for commit LOG.info("Staged changes excluded for commit: " + getLogString(rootPath, excludedStagedChanges)); resetExcluded(project, root, excludedStagedChanges); } try { List<FilePath> alreadyHandledPaths = getPaths(changedWithIndex); // Stage what else is needed to commit Set<FilePath> toAdd = new HashSet<>(added); toAdd.removeAll(alreadyHandledPaths); Set<FilePath> toRemove = new HashSet<>(removed); toRemove.removeAll(toAdd); toRemove.removeAll(alreadyHandledPaths); LOG.debug(String.format("Updating index: added: %s, removed: %s", toAdd, toRemove)); updateIndex(project, root, toAdd, toRemove, exceptions); if (!exceptions.isEmpty()) return exceptions; // Commit the staging area LOG.debug("Performing commit..."); GitRepositoryCommitter committer = new GitRepositoryCommitter(repository, commitOptions); committer.commitStaged(messageFile); } finally { // Stage back the changes unstaged before commit if (!excludedStagedChanges.isEmpty()) { restoreExcluded(project, root, excludedStagedChanges, excludedUnstagedDeletions); } } } catch (VcsException e) { exceptions.add(e); } return exceptions; }