void ValidPathAndUrlTest() { CTSVNPath testPath; testPath.SetFromWin(L"c:\\a\\b\\c.test.txt"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"D:\\.Net\\SpindleSearch\\"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\test folder\\file"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\folder\\"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\ext.ext.ext\\ext.ext.ext.ext"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\.svn"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com\\file"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\test\\conf"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\LPT"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\test\\LPT"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com1test"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\?\\c:\\test\\com1test"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\filename"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\filename.extension"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\.svn"); ATLASSERT(testPath.IsValidOnWindows()); // now the negative tests testPath.SetFromWin(L"c:\\test:folder"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\file<name"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\something*else"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\folder\\file?nofile"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\ext.>ension"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com1\\filename"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com1"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com1\\AuX"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\lpt9\\filename"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\prn"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\NUL"); ATLASSERT(!testPath.IsValidOnWindows()); // now come some URL tests testPath.SetFromSVN(L"http://myserver.com/repos/trunk"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"https://myserver.com/repos/trunk/file%20with%20spaces"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"svn://myserver.com/repos/trunk/file with spaces"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"svn+ssh://www.myserver.com/repos/trunk"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"http://localhost:90/repos/trunk"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"file:///C:/SVNRepos/Tester/Proj1/tags/t2"); ATLASSERT(testPath.IsValidOnWindows()); // and some negative URL tests testPath.SetFromSVN(L"https://myserver.com/rep:os/trunk/file%20with%20spaces"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L"svn://myserver.com/rep<os/trunk/file with spaces"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L"svn+ssh://www.myserver.com/repos/trunk/prn/"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L"http://localhost:90/repos/trunk/com1"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L"http://localhost:90/repos/trunk/Blame3-%3Eblame.cpp"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L""); ATLASSERT(!testPath.IsUrl()); }
bool SVNDiff::DiffProps(const CTSVNPath& filePath, const SVNRev& rev1, const SVNRev& rev2, svn_revnum_t &baseRev) const { bool retvalue = false; // diff the properties SVNProperties propswc(filePath, rev1, false, false); SVNProperties propsbase(filePath, rev2, false, false); #define MAX_PATH_LENGTH 80 WCHAR pathbuf1[MAX_PATH] = {0}; if (filePath.GetWinPathString().GetLength() >= MAX_PATH) { std::wstring str = filePath.GetWinPath(); std::wregex rx(L"^(\\w+:|(?:\\\\|/+))((?:\\\\|/+)[^\\\\/]+(?:\\\\|/)[^\\\\/]+(?:\\\\|/)).*((?:\\\\|/)[^\\\\/]+(?:\\\\|/)[^\\\\/]+)$"); std::wstring replacement = L"$1$2...$3"; std::wstring str2 = std::regex_replace(str, rx, replacement); if (str2.size() >= MAX_PATH) str2 = str2.substr(0, MAX_PATH-2); PathCompactPathEx(pathbuf1, str2.c_str(), MAX_PATH_LENGTH, 0); } else PathCompactPathEx(pathbuf1, filePath.GetWinPath(), MAX_PATH_LENGTH, 0); if ((baseRev == 0) && (!filePath.IsUrl()) && (rev1.IsBase() || rev2.IsBase())) { SVNStatus stat; CTSVNPath dummy; svn_client_status_t * s = stat.GetFirstFileStatus(filePath, dummy); if (s) baseRev = s->revision; } // check for properties that got removed for (int baseindex = 0; baseindex < propsbase.GetCount(); ++baseindex) { std::string basename = propsbase.GetItemName(baseindex); tstring basenameU = CUnicodeUtils::StdGetUnicode(basename); tstring basevalue = (LPCTSTR)CUnicodeUtils::GetUnicode(propsbase.GetItemValue(baseindex).c_str()); bool bFound = false; for (int wcindex = 0; wcindex < propswc.GetCount(); ++wcindex) { if (basename.compare (propswc.GetItemName(wcindex))==0) { bFound = true; break; } } if (!bFound) { // write the old property value to temporary file CTSVNPath wcpropfile = CTempFiles::Instance().GetTempFilePath(false); CTSVNPath basepropfile = CTempFiles::Instance().GetTempFilePath(false); FILE * pFile; _tfopen_s(&pFile, wcpropfile.GetWinPath(), L"wb"); if (pFile) { fclose(pFile); FILE * pFile2; _tfopen_s(&pFile2, basepropfile.GetWinPath(), L"wb"); if (pFile2) { fputs(CUnicodeUtils::StdGetUTF8(basevalue).c_str(), pFile2); fclose(pFile2); } else return false; } else return false; SetFileAttributes(wcpropfile.GetWinPath(), FILE_ATTRIBUTE_READONLY); SetFileAttributes(basepropfile.GetWinPath(), FILE_ATTRIBUTE_READONLY); CString n1, n2; bool bSwitch = false; if (rev1.IsWorking()) n1.Format(IDS_DIFF_PROP_WCNAME, basenameU.c_str()); if (rev1.IsBase()) { if (baseRev) n1.FormatMessage(IDS_DIFF_PROP_BASENAMEREV, basenameU.c_str(), baseRev); else n1.Format(IDS_DIFF_PROP_BASENAME, basenameU.c_str()); } if (rev1.IsHead()) n1.Format(IDS_DIFF_PROP_REMOTENAME, basenameU.c_str()); if (n1.IsEmpty()) { CString temp; temp.Format(IDS_DIFF_REVISIONPATCHED, (LONG)rev1); n1 = basenameU.c_str(); n1 += L" " + temp; bSwitch = true; } else { n1 = CString(pathbuf1) + L" - " + n1; } if (rev2.IsWorking()) n2.Format(IDS_DIFF_PROP_WCNAME, basenameU.c_str()); if (rev2.IsBase()) { if (baseRev) n2.FormatMessage(IDS_DIFF_PROP_BASENAMEREV, basenameU.c_str(), baseRev); else n2.Format(IDS_DIFF_PROP_BASENAME, basenameU.c_str()); } if (rev2.IsHead()) n2.Format(IDS_DIFF_PROP_REMOTENAME, basenameU.c_str()); if (n2.IsEmpty()) { CString temp; temp.Format(IDS_DIFF_REVISIONPATCHED, (LONG)rev2); n2 = basenameU.c_str(); n2 += L" " + temp; bSwitch = true; } else { n2 = CString(pathbuf1) + L" - " + n2; } if (bSwitch) { retvalue = !!CAppUtils::StartExtDiffProps(wcpropfile, basepropfile, n1, n2, TRUE, TRUE); } else { retvalue = !!CAppUtils::StartExtDiffProps(basepropfile, wcpropfile, n2, n1, TRUE, TRUE); } } } for (int wcindex = 0; wcindex < propswc.GetCount(); ++wcindex) { std::string wcname = propswc.GetItemName(wcindex); tstring wcnameU = CUnicodeUtils::StdGetUnicode(wcname); tstring wcvalue = (LPCTSTR)CUnicodeUtils::GetUnicode(propswc.GetItemValue(wcindex).c_str()); tstring basevalue; bool bDiffRequired = true; for (int baseindex = 0; baseindex < propsbase.GetCount(); ++baseindex) { if (propsbase.GetItemName(baseindex).compare(wcname)==0) { basevalue = CUnicodeUtils::GetUnicode(propsbase.GetItemValue(baseindex).c_str()); if (basevalue.compare(wcvalue)==0) { // name and value are identical bDiffRequired = false; break; } } } if (bDiffRequired) { // write both property values to temporary files CTSVNPath wcpropfile = CTempFiles::Instance().GetTempFilePath(false); CTSVNPath basepropfile = CTempFiles::Instance().GetTempFilePath(false); FILE * pFile; _tfopen_s(&pFile, wcpropfile.GetWinPath(), L"wb"); if (pFile) { fputs(CUnicodeUtils::StdGetUTF8(wcvalue).c_str(), pFile); fclose(pFile); FILE * pFile2; _tfopen_s(&pFile2, basepropfile.GetWinPath(), L"wb"); if (pFile2) { fputs(CUnicodeUtils::StdGetUTF8(basevalue).c_str(), pFile2); fclose(pFile2); } else return false; } else return false; SetFileAttributes(wcpropfile.GetWinPath(), FILE_ATTRIBUTE_READONLY); SetFileAttributes(basepropfile.GetWinPath(), FILE_ATTRIBUTE_READONLY); CString n1, n2; if (rev1.IsWorking()) n1.Format(IDS_DIFF_WCNAME, wcnameU.c_str()); if (rev1.IsBase()) n1.Format(IDS_DIFF_BASENAME, wcnameU.c_str()); if (rev1.IsHead()) n1.Format(IDS_DIFF_REMOTENAME, wcnameU.c_str()); if (n1.IsEmpty()) n1.FormatMessage(IDS_DIFF_PROP_REVISIONNAME, wcnameU.c_str(), (LPCTSTR)rev1.ToString()); else n1 = CString(pathbuf1) + L" - " + n1; if (rev2.IsWorking()) n2.Format(IDS_DIFF_WCNAME, wcnameU.c_str()); if (rev2.IsBase()) n2.Format(IDS_DIFF_BASENAME, wcnameU.c_str()); if (rev2.IsHead()) n2.Format(IDS_DIFF_REMOTENAME, wcnameU.c_str()); if (n2.IsEmpty()) n2.FormatMessage(IDS_DIFF_PROP_REVISIONNAME, wcnameU.c_str(), (LPCTSTR)rev2.ToString()); else n2 = CString(pathbuf1) + L" - " + n2; retvalue = !!CAppUtils::StartExtDiffProps(basepropfile, wcpropfile, n2, n1, TRUE, TRUE); } } return retvalue; }
bool SVNDiff::ShowCompare( const CTSVNPath& url1, const SVNRev& rev1, const CTSVNPath& url2, const SVNRev& rev2, SVNRev peg, bool ignoreprops, const CString& options, bool ignoreancestry /*= false*/, bool blame /*= false*/, svn_node_kind_t nodekind /*= svn_node_unknown*/ ) { CTSVNPath tempfile; CString mimetype; CProgressDlg progDlg; progDlg.SetTitle(IDS_APPNAME); progDlg.SetTime(false); m_pSVN->SetAndClearProgressInfo(&progDlg); CAppUtils::DiffFlags diffFlags; diffFlags.ReadOnly().AlternativeTool(m_bAlternativeTool); if ((m_pSVN->PathIsURL(url1))||(!rev1.IsWorking())||(!url1.IsEquivalentTo(url2))) { // no working copy path! progDlg.ShowModeless(GetHWND()); tempfile = CTempFiles::Instance().GetTempFilePath(false, url1); // first find out if the url points to a file or dir CString sRepoRoot; if ((nodekind != svn_node_dir)&&(nodekind != svn_node_file)) { progDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROGRESS_INFO))); SVNInfo info; const SVNInfoData * data = info.GetFirstFileInfo(url1, (peg.IsValid() ? peg : m_headPeg), rev1, svn_depth_empty); if (data == NULL) { data = info.GetFirstFileInfo(url1, (peg.IsValid() ? peg : rev1), rev1, svn_depth_empty); if (data == NULL) { data = info.GetFirstFileInfo(url1, (peg.IsValid() ? peg : rev2), rev1, svn_depth_empty); if (data == NULL) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); info.ShowErrorDialog(GetHWND()); return false; } else { sRepoRoot = data->reposRoot; nodekind = data->kind; peg = peg.IsValid() ? peg : rev2; } } else { sRepoRoot = data->reposRoot; nodekind = data->kind; peg = peg.IsValid() ? peg : rev1; } } else { sRepoRoot = data->reposRoot; nodekind = data->kind; peg = peg.IsValid() ? peg : m_headPeg; } } else { sRepoRoot = m_pSVN->GetRepositoryRoot(url1); peg = peg.IsValid() ? peg : m_headPeg; } if (nodekind == svn_node_dir) { if (rev1.IsWorking()) { if (UnifiedDiff(tempfile, url1, rev1, url2, rev2, (peg.IsValid() ? peg : SVNRev::REV_WC), options)) { CString sWC; sWC.LoadString(IDS_DIFF_WORKINGCOPY); progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); return !!CAppUtils::StartExtPatch(tempfile, url1.GetDirectory(), sWC, url2.GetSVNPathString(), TRUE); } } else { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); CFileDiffDlg fdlg; fdlg.DoBlame(blame); if (url1.IsEquivalentTo(url2)) { fdlg.SetDiff(url1, (peg.IsValid() ? peg : m_headPeg), rev1, rev2, svn_depth_infinity, ignoreancestry); fdlg.DoModal(); } else { fdlg.SetDiff(url1, rev1, url2, rev2, svn_depth_infinity, ignoreancestry); fdlg.DoModal(); } } } else { if (url1.IsEquivalentTo(url2) && !ignoreprops) { svn_revnum_t baseRev = 0; DiffProps(url1, rev2, rev1, baseRev); } // diffing two revs of a file, so export two files CTSVNPath tempfile1 = CTempFiles::Instance().GetTempFilePath(m_bRemoveTempFiles, blame ? CTSVNPath() : url1, rev1); CTSVNPath tempfile2 = CTempFiles::Instance().GetTempFilePath(m_bRemoveTempFiles, blame ? CTSVNPath() : url2, rev2); m_pSVN->SetAndClearProgressInfo(&progDlg, true); // activate progress bar progDlg.FormatPathLine(1, IDS_PROGRESSGETFILEREVISION, (LPCTSTR)url1.GetUIFileOrDirectoryName(), (LPCTSTR)rev1.ToString()); CAppUtils::GetMimeType(url1, mimetype, rev1); CBlame blamer; blamer.SetAndClearProgressInfo(&progDlg, true); if (blame) { if (!blamer.BlameToFile(url1, 1, rev1, peg.IsValid() ? peg : rev1, tempfile1, options, TRUE, TRUE)) { if ((peg.IsValid())&&(blamer.GetSVNError()->apr_err != SVN_ERR_CLIENT_IS_BINARY_FILE)) { if (!blamer.BlameToFile(url1, 1, rev1, rev1, tempfile1, options, TRUE, TRUE)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); blamer.ShowErrorDialog(GetHWND()); return false; } } else { if (blamer.GetSVNError()->apr_err != SVN_ERR_CLIENT_IS_BINARY_FILE) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); } blamer.ShowErrorDialog(GetHWND()); if (blamer.GetSVNError()->apr_err == SVN_ERR_CLIENT_IS_BINARY_FILE) blame = false; else return false; } } } if (!blame) { bool tryWorking = (!m_pSVN->PathIsURL(url1) && rev1.IsWorking() && PathFileExists(url1.GetWinPath())); if (!m_pSVN->Export(url1, tempfile1, peg.IsValid() && !tryWorking ? peg : rev1, rev1)) { if (peg.IsValid()) { if (!m_pSVN->Export(url1, tempfile1, rev1, rev1)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } else { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } } SetFileAttributes(tempfile1.GetWinPath(), FILE_ATTRIBUTE_READONLY); progDlg.FormatPathLine(1, IDS_PROGRESSGETFILEREVISION, (LPCTSTR)url2.GetUIFileOrDirectoryName(), (LPCTSTR)rev2.ToString()); progDlg.SetProgress(50,100); if (blame) { if (!blamer.BlameToFile(url2, 1, rev2, peg.IsValid() ? peg : rev2, tempfile2, options, TRUE, TRUE)) { if (peg.IsValid()) { if (!blamer.BlameToFile(url2, 1, rev2, rev2, tempfile2, options, TRUE, TRUE)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } else { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } } else { if (!m_pSVN->Export(url2, tempfile2, peg.IsValid() ? peg : rev2, rev2)) { if (peg.IsValid()) { if (!m_pSVN->Export(url2, tempfile2, rev2, rev2)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } else { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } } SetFileAttributes(tempfile2.GetWinPath(), FILE_ATTRIBUTE_READONLY); progDlg.SetProgress(100,100); progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); CString revname1, revname2; if (url1.IsEquivalentTo(url2)) { revname1.Format(L"%s Revision %s", (LPCTSTR)url1.GetUIFileOrDirectoryName(), (LPCTSTR)rev1.ToString()); revname2.Format(L"%s Revision %s", (LPCTSTR)url2.GetUIFileOrDirectoryName(), (LPCTSTR)rev2.ToString()); } else { if (sRepoRoot.IsEmpty()) { revname1.Format(L"%s Revision %s", (LPCTSTR)url1.GetSVNPathString(), (LPCTSTR)rev1.ToString()); revname2.Format(L"%s Revision %s", (LPCTSTR)url2.GetSVNPathString(), (LPCTSTR)rev2.ToString()); } else { if (url1.IsUrl()) revname1.Format(L"%s Revision %s", (LPCTSTR)url1.GetSVNPathString().Mid(sRepoRoot.GetLength()), (LPCTSTR)rev1.ToString()); else revname1.Format(L"%s Revision %s", (LPCTSTR)url1.GetSVNPathString(), (LPCTSTR)rev1.ToString()); if (url2.IsUrl() && (url2.GetSVNPathString().Left(sRepoRoot.GetLength()).Compare(sRepoRoot) == 0)) revname2.Format(L"%s Revision %s", (LPCTSTR)url2.GetSVNPathString().Mid(sRepoRoot.GetLength()), (LPCTSTR)rev2.ToString()); else revname2.Format(L"%s Revision %s", (LPCTSTR)url2.GetSVNPathString(), (LPCTSTR)rev2.ToString()); } } return CAppUtils::StartExtDiff(tempfile1, tempfile2, revname1, revname2, url1, url2, rev1, rev2, peg, diffFlags.Blame(blame), m_JumpLine, L"", mimetype); } } else { // compare with working copy if (PathIsDirectory(url1.GetWinPath())) { if (UnifiedDiff(tempfile, url1, rev1, url1, rev2, (peg.IsValid() ? peg : SVNRev::REV_WC), options)) { CString sWC, sRev; sWC.LoadString(IDS_DIFF_WORKINGCOPY); sRev.Format(IDS_DIFF_REVISIONPATCHED, (LONG)rev2); progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); return !!CAppUtils::StartExtPatch(tempfile, url1.GetDirectory(), sWC, sRev, TRUE); } } else { ASSERT(rev1.IsWorking()); if (url1.IsEquivalentTo(url2) && !ignoreprops) { svn_revnum_t baseRev = 0; DiffProps(url1, rev1, rev2, baseRev); } m_pSVN->SetAndClearProgressInfo(&progDlg, true); // activate progress bar progDlg.ShowModeless(GetHWND()); progDlg.FormatPathLine(1, IDS_PROGRESSGETFILEREVISION, (LPCTSTR)url1.GetUIFileOrDirectoryName(), (LPCTSTR)rev2.ToString()); tempfile = CTempFiles::Instance().GetTempFilePath(m_bRemoveTempFiles, url1, rev2); if (blame) { CBlame blamer; if (!blamer.BlameToFile(url1, 1, rev2, (peg.IsValid() ? peg : SVNRev::REV_WC), tempfile, options, TRUE, TRUE)) { if (peg.IsValid()) { if (!blamer.BlameToFile(url1, 1, rev2, SVNRev::REV_WC, tempfile, options, TRUE, TRUE)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } else { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY); CTSVNPath tempfile2 = CTempFiles::Instance().GetTempFilePath(false, url1); if (!blamer.BlameToFile(url1, 1, SVNRev::REV_WC, SVNRev::REV_WC, tempfile2, options, TRUE, TRUE)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } CString revname, wcname; revname.Format(L"%s Revision %ld", (LPCTSTR)url1.GetFilename(), (LONG)rev2); wcname.Format(IDS_DIFF_WCNAME, (LPCTSTR)url1.GetFilename()); m_pSVN->SetAndClearProgressInfo((HWND)NULL); return CAppUtils::StartExtDiff(tempfile, tempfile2, revname, wcname, url1, url2, rev1, rev2, peg, diffFlags, m_JumpLine, url1.GetFileOrDirectoryName(), L""); } else { if (!m_pSVN->Export(url1, tempfile, (peg.IsValid() ? peg : SVNRev::REV_WC), rev2)) { if (peg.IsValid()) { if (!m_pSVN->Export(url1, tempfile, SVNRev::REV_WC, rev2)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } else { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY); CString revname, wcname; revname.Format(L"%s Revision %s", (LPCTSTR)url1.GetFilename(), (LPCTSTR)rev2.ToString()); wcname.Format(IDS_DIFF_WCNAME, (LPCTSTR)url1.GetFilename()); return CAppUtils::StartExtDiff(tempfile, url1, revname, wcname, url1, url1, rev2, rev1, peg, diffFlags, m_JumpLine, url1.GetFileOrDirectoryName(), L""); } } } m_pSVN->SetAndClearProgressInfo((HWND)NULL); return false; }
bool SVNDiff::UnifiedDiff(CTSVNPath& tempfile, const CTSVNPath& url1, const SVNRev& rev1, const CTSVNPath& url2, const SVNRev& rev2, const SVNRev& peg, const CString& options, bool bIgnoreAncestry /* = false */, bool bIgnoreProperties /* = true */) { tempfile = CTempFiles::Instance().GetTempFilePath(m_bRemoveTempFiles, CTSVNPath(L"Test.diff")); bool bIsUrl = !!SVN::PathIsURL(url1); CProgressDlg progDlg; progDlg.SetTitle(IDS_APPNAME); progDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROGRESS_UNIFIEDDIFF))); progDlg.SetTime(false); m_pSVN->SetAndClearProgressInfo(&progDlg); progDlg.ShowModeless(GetHWND()); // find the root of the files CTSVNPathList plist; plist.AddPath(url1); plist.AddPath(url2); CTSVNPath relativeTo = plist.GetCommonRoot(); if (!relativeTo.IsUrl()) { if (!relativeTo.IsDirectory()) relativeTo = relativeTo.GetContainingDirectory(); } if (relativeTo.IsEmpty() && url1.Exists() && url2.IsUrl()) { // the source path exists, i.e. it's a local path, so // use this as the relative url relativeTo = url1.GetDirectory(); } // the 'relativeTo' path must be a path: svn throws an error if it's used for urls. else if ((!url2.IsEquivalentTo(url1) && (relativeTo.IsEquivalentTo(url1) || relativeTo.IsEquivalentTo(url2))) || url1.IsUrl() || url2.IsUrl()) relativeTo.Reset(); if ((!url1.IsEquivalentTo(url2))||((rev1.IsWorking() || rev1.IsBase())&&(rev2.IsWorking() || rev2.IsBase()))) { if (!m_pSVN->Diff(url1, rev1, url2, rev2, relativeTo, svn_depth_infinity, true, false, false, false, false, false, bIgnoreProperties, false, options, bIgnoreAncestry, tempfile)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } else { if (!m_pSVN->PegDiff(url1, (peg.IsValid() ? peg : (bIsUrl ? m_headPeg : SVNRev::REV_WC)), rev1, rev2, relativeTo, svn_depth_infinity, true, false, false, false, false, false, bIgnoreProperties, false, options, false, tempfile)) { if (!m_pSVN->Diff(url1, rev1, url2, rev2, relativeTo, svn_depth_infinity, true, false, false, false, false, false, bIgnoreProperties, false, options, false, tempfile)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); m_pSVN->ShowErrorDialog(GetHWND()); return false; } } } if (CAppUtils::CheckForEmptyDiff(tempfile)) { progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); TaskDialog(GetHWND(), AfxGetResourceHandle(), MAKEINTRESOURCE(IDS_APPNAME), MAKEINTRESOURCE(IDS_ERR_ERROROCCURED), MAKEINTRESOURCE(IDS_ERR_EMPTYDIFF), TDCBF_OK_BUTTON, TD_ERROR_ICON, NULL); return false; } progDlg.Stop(); m_pSVN->SetAndClearProgressInfo((HWND)NULL); return true; }
void CFullHistory::QueryWCRevision (bool commitRevs, CString path) { // we need a thread-local SVN instance that checks our shared // progress dialog for cancelation. class SVNForwardedCancel : public SVN { private: CProgressDlg *progress; public: SVNForwardedCancel (CProgressDlg *progress) : progress (progress) { } virtual BOOL Cancel() { return progress && progress->HasUserCancelled() ? TRUE : FALSE; } }; // Find the revision the working copy is on, we mark that revision // later in the graph (handle option changes properly!). CTSVNPath tpath = CTSVNPath (path); if (!tpath.IsUrl()) { svn_revnum_t maxrev = pegRevision; svn_revnum_t minrev = 0; bool switched, modified, sparse; SVNForwardedCancel svn (progress); if (svn.GetWCRevisionStatus ( CTSVNPath (path) , commitRevs // get the "commit" revision , minrev , maxrev , switched , modified , sparse)) { // we want to report the oldest revision as WC revision: // If you commit at $WC/path/ and after that ask for the // rev graph at $WC/, we want to display the revision of // the base path ($WC/ is now "older") instead of the // newest one. if (commitRevs) { wcInfo.minCommit = minrev; wcInfo.maxCommit = maxrev; wcInfo.modified = modified; } else { wcInfo.minAtRev = minrev; wcInfo.maxAtRev = maxrev; } } } }
bool CFullHistory::FetchRevisionData ( CString path , SVNRev pegRev , bool showWCRev , bool showWCModification , CProgressDlg* progress , ITaskbarList3 * pTaskBarList , HWND hWnd) { // clear any previously existing SVN error info svn_error_clear(Err); Err = NULL; // remove internal data from previous runs CFuture<bool> clearJob (this, &CFullHistory::ClearCopyInfo, &cpuLoadScheduler); // set some text on the progress dialog, before we wait // for the log operation to start this->progress = progress; this->taskbarlist = pTaskBarList; this->hwnd = hWnd; CString temp; temp.LoadString (IDS_REVGRAPH_PROGGETREVS); progress->SetLine(1, temp); temp.LoadString (IDS_REVGRAPH_PROGPREPARING); progress->SetLine(2, temp); progress->SetProgress(0, 1); progress->ShowModeless (hWnd); if (taskbarlist) { taskbarlist->SetProgressState(hwnd, TBPF_INDETERMINATE); } // prepare the path for Subversion CTSVNPath svnPath (path); CStringA url = CPathUtils::PathEscape (CUnicodeUtils::GetUTF8 (svn.GetURLFromPath (svnPath))); // we have to get the log from the repository root CTSVNPath rootPath; svn_revnum_t head; if (FALSE == svn.GetRootAndHead (svnPath, rootPath, head)) { Err = svn_error_dup(const_cast<svn_error_t*>(svn.GetSVNError())); return false; } if (pegRev.IsHead()) pegRev = head; headRevision = head; CString escapedRepoRoot = rootPath.GetSVNPathString(); relPath = CPathUtils::PathUnescape (url.Mid (escapedRepoRoot.GetLength())); repoRoot = CPathUtils::PathUnescape (escapedRepoRoot); // fix issue #360: use WC revision as peg revision pegRevision = pegRev; if (pegRevision == NO_REVISION) { if (!svnPath.IsUrl()) { SVNInfo info; const SVNInfoData * baseInfo = info.GetFirstFileInfo (svnPath, SVNRev(), SVNRev()); if (baseInfo != NULL) pegRevision = baseInfo->rev; } } // fetch missing data from the repository try { // select / construct query object and optimize revision range to fetch svnQuery.reset (new CSVNLogQuery (ctx, pool)); bool cacheIsComplete = false; if (svn.GetLogCachePool()->IsEnabled()) { CLogCachePool* pool = svn.GetLogCachePool(); query.reset (new CCacheLogQuery (pool, svnQuery.get())); // get the cache and the lowest missing revision // (in off-line mode, the query may not find the cache as // it cannot contact the server to get the UUID) uuid = pool->GetRepositoryInfo().GetRepositoryUUID (rootPath); cache = pool->GetCache (uuid, escapedRepoRoot); firstRevision = cache != NULL ? cache->GetRevisions().GetFirstMissingRevision(1) : 0; // if the cache is already complete, the firstRevision here is // HEAD+1 - that revision does not exist and would throw an error later if (firstRevision > headRevision) { cacheIsComplete = true; firstRevision = headRevision; } } else { query.reset (new CCacheLogQuery (svn, svnQuery.get())); cache = NULL; firstRevision = 0; } // Find the revision the working copy is on, we mark that revision // later in the graph (handle option changes properly!). // For performance reasons, we only don't do it if we want to display it. wcInfo = SWCInfo (pegRev); if (showWCRev || showWCModification) { new CAsyncCall ( this , &CFullHistory::QueryWCRevision , true , path , &diskIOScheduler); new CAsyncCall ( this , &CFullHistory::QueryWCRevision , false , path , &diskIOScheduler); } // actually fetch the data if (!cacheIsComplete) query->Log ( CTSVNPathList (rootPath) , headRevision , headRevision , firstRevision , 0 , false // strictNodeHistory , this , false // includeChanges (log cache fetches them automatically) , false // includeMerges , true // includeStandardRevProps , false // includeUserRevProps , TRevPropNames()); // Store updated cache data if (cache == NULL) { cache = query->GetCache(); // This should never happen: if (cache == NULL) return false; } else { if (cache->IsModified()) new CAsyncCall ( cache , &LogCache::CCachedLogInfo::Save , &cpuLoadScheduler); } // store WC path const CPathDictionary* paths = &cache->GetLogInfo().GetPaths(); wcPath.reset (new CDictionaryBasedTempPath (paths, (const char*)relPath)); // wait for the cleanup jobs to finish before starting new ones // that depend of them clearJob.GetResult(); // analyse the data new CAsyncCall ( this , &CFullHistory::AnalyzeRevisionData , &cpuLoadScheduler); // pre-process log data (invert copy-relationship) new CAsyncCall ( this , &CFullHistory::BuildForwardCopies , &cpuLoadScheduler); // Wait for the jobs to finish if (showWCRev || showWCModification) { temp.LoadString (IDS_REVGRAPH_PROGREADINGWC); progress->SetLine(2, temp); } cpuLoadScheduler.WaitForEmptyQueue(); diskIOScheduler.WaitForEmptyQueue(); } catch (SVNError& e) { Err = svn_error_create (e.GetCode(), NULL, e.GetMessage()); return false; } return true; }