@NotNull @Override public MergeData loadRevisions(final VirtualFile file) throws VcsException { final MergeData mergeData = new MergeData(); final VcsRunnable runnable = new VcsRunnable() { public void run() throws VcsException { final HgWorkingCopyRevisionsCommand command = new HgWorkingCopyRevisionsCommand(myProject); final VirtualFile repo = HgUtil.getHgRootOrThrow(myProject, file); final HgFile hgFile = new HgFile(myProject, file); HgRevisionNumber serverRevisionNumber; HgRevisionNumber localRevisionNumber; HgRevisionNumber baseRevisionNumber = null; // there are two possibilities: we have checked in local changes in the selected file or // we didn't. if (wasFileCheckedIn(repo, file)) { // 1. We checked in. // We have a merge in progress, which means we have 2 heads (parents). // the second one is "their" revision pulled from the parent repo, // first parent is the local change. // to retrieve the base version we get the parent of the local change, i.e. the [only] // parent of the first parent. // Whick one is local revision depends on which one is merged with, // i.e if you update to 17 revision and then merge it woth 23, so 17 is your local and // 17->parent is your base revision. // This may produce misunderstanding when you update your project with merging (your // update firstly to next revisions and then // merge with previous). see // http://hgbook.red-bean.com/read/managing-releases-and-branchy-development.html final Couple<HgRevisionNumber> parents = command.parents(repo, file); serverRevisionNumber = parents.second; localRevisionNumber = parents.first; final HgContentRevision local = new HgContentRevision(myProject, hgFile, localRevisionNumber); mergeData.CURRENT = local.getContentAsBytes(); // we are sure that we have a common ancestor, because otherwise we'll get "repository // is unrelated" error while pulling, // due to different root changesets which is prohibited. // Find common ancestor of two revisions : hg debugancestor rev1 rev2 // Using quotes may produce wrong escaping errors on Unix-type systems List<String> arguments = new ArrayList<String>(); String localChangeset = localRevisionNumber.getChangeset(); String serverChangeset = serverRevisionNumber.getChangeset(); arguments.add( StringUtil.isEmptyOrSpaces(localChangeset) ? localRevisionNumber.getRevision() : localChangeset); arguments.add( StringUtil.isEmptyOrSpaces(serverChangeset) ? serverRevisionNumber.getRevision() : serverChangeset); HgCommandResult result = new HgPromptCommandExecutor(myProject) .executeInCurrentThread(repo, "debugancestor", arguments); if (result != null) { String output = result.getRawOutput(); final List<String> parts = StringUtil.split(output, ":"); if (parts.size() < 2) { LOG.info("Couldn't parse result of debugancestor command execution " + arguments); new HgCommandResultNotifier(myProject) .notifyError( null, HgVcsMessages.message("hg4idea.error.debugancestor.command.execution"), HgVcsMessages.message("hg4idea.error.debugancestor.command.description")); } else { baseRevisionNumber = HgRevisionNumber.getInstance(parts.get(0), parts.get(1)); } } else { LOG.info( HgVcsMessages.message("hg4idea.error.debugancestor.command.execution") + arguments); new HgCommandResultNotifier(myProject) .notifyError( null, HgVcsMessages.message("hg4idea.error.debugancestor.command.execution"), HgVcsMessages.message("hg4idea.error.debugancestor.command.description")); } } else { // 2. local changes are not checked in. // then there is only one parent, which is server changes. // local changes are retrieved from the file system, they are not in the Mercurial // yet. // base is the only parent of server changes. serverRevisionNumber = command.parents(repo, file).first; baseRevisionNumber = command.parents(repo, file, serverRevisionNumber).first; final File origFile = new File(file.getPath() + ".orig"); try { mergeData.CURRENT = VcsUtil.getFileByteContent(origFile); } catch (IOException e) { LOG.info("Couldn't retrieve byte content of the file: " + origFile.getPath(), e); } } if (baseRevisionNumber != null) { final HgContentRevision base = new HgContentRevision(myProject, hgFile, baseRevisionNumber); // if file doesn't exist in ancestor revision the base revision should be empty mergeData.ORIGINAL = base.getContent() != null ? base.getContentAsBytes() : new byte[0]; } else { // no base revision means that the file was added simultaneously with different // content in both repositories mergeData.ORIGINAL = new byte[0]; } final HgContentRevision server = new HgContentRevision(myProject, hgFile, serverRevisionNumber); mergeData.LAST = server.getContentAsBytes(); file.refresh(false, false); } }; VcsUtil.runVcsProcessWithProgress( runnable, VcsBundle.message("multiple.file.merge.loading.progress.title"), false, myProject); return mergeData; }
@NotNull public MergeData loadRevisions(@NotNull final VirtualFile file) throws VcsException { final MergeData data = new MergeData(); VcsRunnable runnable = new VcsRunnable() { public void run() throws VcsException { File oldFile = null; File newFile = null; File workingFile = null; boolean mergeCase = false; SvnVcs vcs = SvnVcs.getInstance(myProject); Info info = vcs.getInfo(file); if (info != null) { oldFile = info.getConflictOldFile(); newFile = info.getConflictNewFile(); workingFile = info.getConflictWrkFile(); mergeCase = workingFile == null || workingFile.getName().contains("working"); // for debug if (workingFile == null) { LOG.info( "Null working file when merging text conflict for " + file.getPath() + " old file: " + oldFile + " new file: " + newFile); } if (mergeCase) { // this is merge case oldFile = info.getConflictNewFile(); newFile = info.getConflictOldFile(); workingFile = info.getConflictWrkFile(); } data.LAST_REVISION_NUMBER = new SvnRevisionNumber(info.getRevision()); } else { throw new VcsException("Could not get info for " + file.getPath()); } if (oldFile == null || newFile == null || workingFile == null) { ByteArrayOutputStream bos = getBaseRevisionContents(vcs, file); data.ORIGINAL = bos.toByteArray(); data.LAST = bos.toByteArray(); data.CURRENT = readFile(new File(file.getPath())); } else { data.ORIGINAL = readFile(oldFile); data.LAST = readFile(newFile); data.CURRENT = readFile(workingFile); } if (mergeCase) { final ByteArrayOutputStream contents = getBaseRevisionContents(vcs, file); if (!Arrays.equals(contents.toByteArray(), data.ORIGINAL)) { // swap base and server: another order of merge arguments byte[] original = data.ORIGINAL; data.ORIGINAL = data.LAST; data.LAST = original; } } } }; VcsUtil.runVcsProcessWithProgress( runnable, VcsBundle.message("multiple.file.merge.loading.progress.title"), false, myProject); return data; }