public List<ShelvedChangeList> importChangeLists( final Collection<VirtualFile> files, final Consumer<VcsException> exceptionConsumer) { final List<ShelvedChangeList> result = new ArrayList<ShelvedChangeList>(files.size()); try { final FilesProgress filesProgress = new FilesProgress(files.size(), "Processing "); for (VirtualFile file : files) { filesProgress.updateIndicator(file); final String description = file.getNameWithoutExtension().replace('_', ' '); final File patchPath = getPatchPath(description); final ShelvedChangeList list = new ShelvedChangeList( patchPath.getPath(), description, new SmartList<ShelvedBinaryFile>(), file.getTimeStamp()); try { final List<TextFilePatch> patchesList = loadPatches(myProject, file.getPath(), new CommitContext()); if (!patchesList.isEmpty()) { FileUtil.copy(new File(file.getPath()), patchPath); // add only if ok to read patch myShelvedChangeLists.add(list); result.add(list); } } catch (IOException e) { exceptionConsumer.consume(new VcsException(e)); } catch (PatchSyntaxException e) { exceptionConsumer.consume(new VcsException(e)); } } } finally { notifyStateChanged(); } return result; }
void saveRemainingPatches( final ShelvedChangeList changeList, final List<FilePatch> remainingPatches, final List<ShelvedBinaryFile> remainingBinaries, CommitContext commitContext) { final File newPath = getPatchPath(changeList.DESCRIPTION); try { FileUtil.copy(new File(changeList.PATH), newPath); } catch (IOException e) { // do not delete if cannot recycle return; } final ShelvedChangeList listCopy = new ShelvedChangeList( newPath.getAbsolutePath(), changeList.DESCRIPTION, new ArrayList<ShelvedBinaryFile>(changeList.getBinaryFiles())); listCopy.DATE = (changeList.DATE == null) ? null : new Date(changeList.DATE.getTime()); writePatchesToFile(myProject, changeList.PATH, remainingPatches, commitContext); changeList.getBinaryFiles().retainAll(remainingBinaries); changeList.clearLoadedChanges(); recycleChangeList(listCopy, changeList); notifyStateChanged(); }
public ShelvedChangeList importFilePatches( final String fileName, final List<FilePatch> patches, final PatchEP[] patchTransitExtensions) throws IOException { try { final File patchPath = getPatchPath(fileName); myFileProcessor.savePathFile( new CompoundShelfFileProcessor.ContentProvider() { public void writeContentTo(final Writer writer, CommitContext commitContext) throws IOException { UnifiedDiffWriter.write( myProject, patches, writer, "\n", patchTransitExtensions, commitContext); } }, patchPath, new CommitContext()); final ShelvedChangeList changeList = new ShelvedChangeList( patchPath.toString(), fileName.replace('\n', ' '), new SmartList<ShelvedBinaryFile>()); myShelvedChangeLists.add(changeList); return changeList; } finally { notifyStateChanged(); } }
public ShelvedChangeList shelveChanges( final Collection<Change> changes, final String commitMessage, final boolean rollback) throws IOException, VcsException { final List<Change> textChanges = new ArrayList<Change>(); final List<ShelvedBinaryFile> binaryFiles = new ArrayList<ShelvedBinaryFile>(); for (Change change : changes) { if (ChangesUtil.getFilePath(change).isDirectory()) { continue; } if (change.getBeforeRevision() instanceof BinaryContentRevision || change.getAfterRevision() instanceof BinaryContentRevision) { binaryFiles.add(shelveBinaryFile(change)); } else { textChanges.add(change); } } final ShelvedChangeList changeList; try { File patchPath = getPatchPath(commitMessage); ProgressManager.checkCanceled(); final List<FilePatch> patches = IdeaTextPatchBuilder.buildPatch( myProject, textChanges, myProject.getBaseDir().getPresentableUrl(), false); ProgressManager.checkCanceled(); CommitContext commitContext = new CommitContext(); baseRevisionsOfDvcsIntoContext(textChanges, commitContext); myFileProcessor.savePathFile( new CompoundShelfFileProcessor.ContentProvider() { public void writeContentTo(final Writer writer, CommitContext commitContext) throws IOException { UnifiedDiffWriter.write(myProject, patches, writer, "\n", commitContext); } }, patchPath, commitContext); changeList = new ShelvedChangeList( patchPath.toString(), commitMessage.replace('\n', ' '), binaryFiles); myShelvedChangeLists.add(changeList); ProgressManager.checkCanceled(); if (rollback) { new RollbackWorker(myProject, false) .doRollback(changes, true, null, VcsBundle.message("shelve.changes.action")); } } finally { notifyStateChanged(); } return changeList; }
private void deleteListImpl(final ShelvedChangeList changeList) { File file = new File(changeList.PATH); myFileProcessor.delete(file.getName()); for (ShelvedBinaryFile binaryFile : changeList.getBinaryFiles()) { final String path = binaryFile.SHELVED_PATH; if (path != null) { File binFile = new File(path); myFileProcessor.delete(binFile.getName()); } } }
/** * Create a file that contains the specified message * * @param root a git repository root * @param message a message to write * @return a file reference * @throws IOException if file cannot be created */ private File createMessageFile(VirtualFile root, final String message) throws IOException { // filter comment lines File file = FileUtil.createTempFile(GIT_COMMIT_MSG_FILE_PREFIX, GIT_COMMIT_MSG_FILE_EXT); file.deleteOnExit(); @NonNls String encoding = GitConfigUtil.getCommitEncoding(myProject, root); Writer out = new OutputStreamWriter(new FileOutputStream(file), encoding); try { out.write(message); } finally { out.close(); } return file; }
private File getPatchPath(@NonNls final String commitMessage) { File file = myFileProcessor.getBaseIODir(); if (!file.exists()) { file.mkdirs(); } return suggestPatchName( myProject, commitMessage.length() > PatchNameChecker.MAX ? (commitMessage.substring(0, PatchNameChecker.MAX)) : commitMessage, file, VcsConfiguration.PATCH); }
/** * 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); } }
private ShelvedBinaryFile shelveBinaryFile(final Change change) throws IOException { final ContentRevision beforeRevision = change.getBeforeRevision(); final ContentRevision afterRevision = change.getAfterRevision(); File beforeFile = beforeRevision == null ? null : beforeRevision.getFile().getIOFile(); File afterFile = afterRevision == null ? null : afterRevision.getFile().getIOFile(); String shelvedPath = null; if (afterFile != null) { String shelvedName = FileUtil.getNameWithoutExtension(afterFile.getName()); String shelvedExt = FileUtil.getExtension(afterFile.getName()); File shelvedFile = FileUtil.findSequentNonexistentFile( myFileProcessor.getBaseIODir(), shelvedName, shelvedExt); myFileProcessor.saveFile(afterRevision.getFile().getIOFile(), shelvedFile); shelvedPath = shelvedFile.getPath(); } String beforePath = ChangesUtil.getProjectRelativePath(myProject, beforeFile); String afterPath = ChangesUtil.getProjectRelativePath(myProject, afterFile); return new ShelvedBinaryFile(beforePath, afterPath, shelvedPath); }
public static File suggestPatchName( Project project, final String commitMessage, final File file, String extension) { @NonNls String defaultPath = PathUtil.suggestFileName(commitMessage); if (defaultPath.length() == 0) { defaultPath = "unnamed"; } if (defaultPath.length() > (PatchNameChecker.MAX - 10)) { defaultPath = defaultPath.substring(0, PatchNameChecker.MAX - 10); } while (true) { final File nonexistentFile = FileUtil.findSequentNonexistentFile( file, defaultPath, extension == null ? VcsConfiguration.getInstance(project).getPatchFileExtension() : extension); if (nonexistentFile.getName().length() >= PatchNameChecker.MAX) { defaultPath = defaultPath.substring(0, defaultPath.length() - 1); continue; } return nonexistentFile; } }
/** * 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; }
public List<VcsException> commit( @NotNull List<Change> changes, @NotNull String message, @NotNull NullableFunction<Object, Object> parametersHolder, Set<String> feedback) { List<VcsException> exceptions = new ArrayList<VcsException>(); Map<VirtualFile, Collection<Change>> sortedChanges = sortChangesByGitRoot(changes, exceptions); log.assertTrue( !sortedChanges.isEmpty(), "Trying to commit an empty list of changes: " + changes); for (Map.Entry<VirtualFile, Collection<Change>> entry : sortedChanges.entrySet()) { final VirtualFile root = entry.getKey(); try { File messageFile = createMessageFile(root, message); try { final Set<FilePath> added = new HashSet<FilePath>(); final Set<FilePath> removed = new HashSet<FilePath>(); for (Change change : entry.getValue()) { switch (change.getType()) { case NEW: case MODIFICATION: added.add(change.getAfterRevision().getFile()); break; case DELETED: removed.add(change.getBeforeRevision().getFile()); break; case MOVED: FilePath afterPath = change.getAfterRevision().getFile(); FilePath beforePath = change.getBeforeRevision().getFile(); added.add(afterPath); if (!GitFileUtils.shouldIgnoreCaseChange( afterPath.getPath(), beforePath.getPath())) { removed.add(beforePath); } break; default: throw new IllegalStateException("Unknown change type: " + change.getType()); } } try { try { Set<FilePath> files = new HashSet<FilePath>(); files.addAll(added); files.addAll(removed); commit( myProject, root, files, messageFile, myNextCommitAuthor, myNextCommitAmend, myNextCommitAuthorDate); } catch (VcsException ex) { PartialOperation partialOperation = isMergeCommit(ex); if (partialOperation == PartialOperation.NONE) { throw ex; } if (!mergeCommit( myProject, root, added, removed, messageFile, myNextCommitAuthor, exceptions, partialOperation)) { throw ex; } } } finally { if (!messageFile.delete()) { log.warn("Failed to remove temporary file: " + messageFile); } } } catch (VcsException e) { exceptions.add(e); } } catch (IOException ex) { //noinspection ThrowableInstanceNeverThrown exceptions.add(new VcsException("Creation of commit message file failed", ex)); } } if (myNextCommitIsPushed != null && myNextCommitIsPushed.booleanValue() && exceptions.isEmpty()) { // push UIUtil.invokeLaterIfNeeded( new Runnable() { public void run() { GitPusher.showPushDialogAndPerformPush( myProject, ServiceManager.getService(myProject, GitPlatformFacade.class)); } }); } return exceptions; }