void CRebaseDlg::AddBranchToolTips(CHistoryCombo *pBranch) { if(pBranch) { CString text=pBranch->GetString(); CString tooltip; GitRev rev; rev.GetCommit(text); tooltip.Format(_T("CommitHash:%s\nCommit by: %s %s\n <b>%s</b> \n %s"), rev.m_CommitHash.ToString(), rev.GetAuthorName(), CLoglistUtils::FormatDateAndTime(rev.GetAuthorDate(), DATE_LONGDATE), rev.GetSubject(), rev.GetBody()); pBranch->DisableTooltip(); this->m_tooltips.AddTool(pBranch->GetComboBoxCtrl(),tooltip); } }
void CTortoiseGitBlameData::ParseBlameOutput(BYTE_VECTOR &data, CGitHashMap & HashToRev, DWORD dateFormat, bool bRelativeTimes) { std::unordered_map<CGitHash, CString> hashToFilename; std::vector<CGitHash> hashes; std::vector<int> originalLineNumbers; std::vector<CString> filenames; std::vector<BYTE_VECTOR> rawLines; std::vector<CString> authors; std::vector<CString> dates; CGitHash hash; int originalLineNumber = 0; int numberOfSubsequentLines = 0; CString filename; size_t pos = 0; bool expectHash = true; while (pos < data.size()) { if (data[pos] == 0) { ++pos; continue; } size_t lineBegin = pos; size_t lineEnd = data.find('\n', lineBegin); if (lineEnd == BYTE_VECTOR::npos) lineEnd = data.size(); if (lineEnd > lineBegin) { if (data[lineBegin] != '\t') { if (expectHash) { expectHash = false; if (lineEnd - lineBegin > 2 * GIT_HASH_SIZE) { hash = CGitHash::FromHexStr(reinterpret_cast<char*>(&data[lineBegin])); size_t hashEnd = lineBegin + 2 * GIT_HASH_SIZE; size_t originalLineNumberBegin = hashEnd + 1; size_t originalLineNumberEnd = data.find(' ', originalLineNumberBegin); if (originalLineNumberEnd != BYTE_VECTOR::npos) { originalLineNumber = atoi(CStringA(reinterpret_cast<LPCSTR>(&data[originalLineNumberBegin]), static_cast<int>(originalLineNumberEnd - originalLineNumberBegin))); size_t finalLineNumberBegin = originalLineNumberEnd + 1; size_t finalLineNumberEnd = (numberOfSubsequentLines == 0) ? data.find(' ', finalLineNumberBegin) : lineEnd; if (finalLineNumberEnd != BYTE_VECTOR::npos) { if (numberOfSubsequentLines == 0) { size_t numberOfSubsequentLinesBegin = finalLineNumberEnd + 1; size_t numberOfSubsequentLinesEnd = lineEnd; numberOfSubsequentLines = atoi(CStringA(reinterpret_cast<LPCSTR>(&data[numberOfSubsequentLinesBegin]), static_cast<int>(numberOfSubsequentLinesEnd - numberOfSubsequentLinesBegin))); } } else { // parse error numberOfSubsequentLines = 0; } } else { // parse error numberOfSubsequentLines = 0; } auto it = hashToFilename.find(hash); if (it != hashToFilename.end()) filename = it->second; else filename.Empty(); } else { // parse error numberOfSubsequentLines = 0; } } else { size_t tokenBegin = lineBegin; size_t tokenEnd = data.find(' ', tokenBegin); if (tokenEnd != BYTE_VECTOR::npos) { if (!strncmp("filename", reinterpret_cast<const char*>(&data[tokenBegin]), tokenEnd - tokenBegin)) { size_t filenameBegin = tokenEnd + 1; size_t filenameEnd = lineEnd; CStringA filenameA = CStringA(reinterpret_cast<LPCSTR>(&data[filenameBegin]), static_cast<int>(filenameEnd - filenameBegin)); filename = UnquoteFilename(filenameA); auto r = hashToFilename.emplace(hash, filename); if (!r.second) { r.first->second = filename; } } } } } else { expectHash = true; // remove <TAB> at start BYTE_VECTOR line; if (lineEnd - 1 > lineBegin) line.append(&data[lineBegin + 1], lineEnd-lineBegin - 1); while (!line.empty() && line[line.size() - 1] == 13) line.pop_back(); hashes.push_back(hash); filenames.push_back(filename); originalLineNumbers.push_back(originalLineNumber); rawLines.push_back(line); --numberOfSubsequentLines; } } pos = lineEnd + 1; } for (const auto& hash2 : hashes) { CString err; GitRev* pRev = GetRevForHash(HashToRev, hash2, &err); if (pRev) { authors.push_back(pRev->GetAuthorName()); dates.push_back(CLoglistUtils::FormatDateAndTime(pRev->GetAuthorDate(), dateFormat, true, bRelativeTimes)); } else { MessageBox(nullptr, err, L"TortoiseGit", MB_ICONERROR); authors.emplace_back(); dates.emplace_back(); } } m_Hash.swap(hashes); m_OriginalLineNumbers.swap(originalLineNumbers); m_Filenames.swap(filenames); m_RawLines.swap(rawLines); m_Authors.swap(authors); m_Dates.swap(dates); // reset detected and applied encoding m_encode = -1; m_Utf8Lines.clear(); }
void CTortoiseGitBlameData::ParseBlameOutput(BYTE_VECTOR &data, CGitHashMap & HashToRev, DWORD dateFormat, bool bRelativeTimes) { std::map<CGitHash, CString> hashToFilename; std::vector<CGitHash> hashes; std::vector<int> originalLineNumbers; std::vector<CString> filenames; std::vector<BYTE_VECTOR> rawLines; std::vector<CString> authors; std::vector<CString> dates; CGitHash hash; int originalLineNumber = 0; int finalLineNumber = 0; int numberOfSubsequentLines = 0; CString filename; int pos = 0; bool expectHash = true; while (pos >= 0 && (size_t)pos < data.size()) { if (data[pos] == 0) { ++pos; continue; } int lineBegin = pos; int lineEnd = data.find('\n', lineBegin); if (lineEnd < 0) lineEnd = (int)data.size(); if (lineEnd > lineBegin) { if (data[lineBegin] != '\t') { if (expectHash) { expectHash = false; if (lineEnd - lineBegin > 40) { hash.ConvertFromStrA((char*)&data[lineBegin]); int hashEnd = lineBegin + 40; int originalLineNumberBegin = hashEnd + 1; int originalLineNumberEnd = data.find(' ', originalLineNumberBegin); if (originalLineNumberEnd >= 0) { originalLineNumber = atoi(CStringA((LPCSTR)&data[originalLineNumberBegin], originalLineNumberEnd - originalLineNumberBegin)); int finalLineNumberBegin = originalLineNumberEnd + 1; int finalLineNumberEnd = (numberOfSubsequentLines == 0) ? data.find(' ', finalLineNumberBegin) : lineEnd; if (finalLineNumberEnd >= 0) { finalLineNumber = atoi(CStringA((LPCSTR)&data[finalLineNumberBegin], finalLineNumberEnd - finalLineNumberBegin)); if (numberOfSubsequentLines == 0) { int numberOfSubsequentLinesBegin = finalLineNumberEnd + 1; int numberOfSubsequentLinesEnd = lineEnd; numberOfSubsequentLines = atoi(CStringA((LPCSTR)&data[numberOfSubsequentLinesBegin], numberOfSubsequentLinesEnd - numberOfSubsequentLinesBegin)); } } else { // parse error finalLineNumber = 0; numberOfSubsequentLines = 0; } } else { // parse error finalLineNumber = 0; numberOfSubsequentLines = 0; } auto it = hashToFilename.find(hash); if (it != hashToFilename.end()) filename = it->second; else filename.Empty(); } else { // parse error finalLineNumber = 0; numberOfSubsequentLines = 0; } } else { int tokenBegin = lineBegin; int tokenEnd = data.find(' ', tokenBegin); if (tokenEnd >= 0) { if (!strncmp("filename", (const char*)&data[tokenBegin], tokenEnd - tokenBegin)) { int filenameBegin = tokenEnd + 1; int filenameEnd = lineEnd; CStringA filenameA = CStringA((LPCSTR)&data[filenameBegin], filenameEnd - filenameBegin); filename = UnquoteFilename(filenameA); auto r = hashToFilename.insert(std::make_pair(hash, filename)); if (!r.second) { r.first->second = filename; } } } } } else { expectHash = true; // remove <TAB> at start BYTE_VECTOR line; if (lineEnd - 1 > lineBegin) line.append(&data[lineBegin + 1], lineEnd-lineBegin - 1); hashes.push_back(hash); filenames.push_back(filename); originalLineNumbers.push_back(originalLineNumber); rawLines.push_back(line); --numberOfSubsequentLines; } } pos = lineEnd + 1; } for (auto it = hashes.begin(), it_end = hashes.end(); it != it_end; ++it) { CGitHash hash = *it; CString err; GitRev* pRev = GetRevForHash(HashToRev, hash, &err); if (pRev) { authors.push_back(pRev->GetAuthorName()); dates.push_back(CLoglistUtils::FormatDateAndTime(pRev->GetAuthorDate(), dateFormat, true, bRelativeTimes)); } else { MessageBox(nullptr, err, _T("TortoiseGit"), MB_ICONERROR); authors.push_back(CString()); dates.push_back(CString()); } } m_Hash.swap(hashes); m_OriginalLineNumbers.swap(originalLineNumbers); m_Filenames.swap(filenames); m_RawLines.swap(rawLines); m_Authors.swap(authors); m_Dates.swap(dates); // reset detected and applied encoding m_encode = -1; m_Utf8Lines.clear(); }
void GetRevParsingTests() { GitRev rev; EXPECT_TRUE(rev.m_CommitHash.IsEmpty()); EXPECT_EQ(0, rev.GetCommit(_T("HEAD"))); EXPECT_STREQ(_T("7c3cbfe13a929d2291a574dca45e4fd2d2ac1aa6"), rev.m_CommitHash.ToString()); EXPECT_STREQ(_T("Sven Strickroth"), rev.GetAuthorName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail()); EXPECT_STREQ(_T("2015-03-07 18:03:58"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail()); EXPECT_STREQ(_T("2015-03-07 18:03:58"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Changed ASCII file"), rev.GetSubject()); EXPECT_STREQ(_T(""), rev.GetBody()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); EXPECT_EQ(0, rev.ParentsCount()); EXPECT_EQ(0, rev.GetParentFromHash(rev.m_CommitHash)); ASSERT_EQ(1, rev.ParentsCount()); EXPECT_STREQ(_T("1fc3c9688e27596d8717b54f2939dc951568f6cb"), rev.m_ParentHash[0].ToString()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); rev.Clear(); EXPECT_EQ(0, rev.GetCommit(GitRev::GetWorkingCopy())); EXPECT_TRUE(rev.m_CommitHash.IsEmpty()); EXPECT_STREQ(_T(""), rev.GetAuthorName()); EXPECT_STREQ(_T(""), rev.GetAuthorEmail()); EXPECT_STREQ(_T(""), rev.GetCommitterName()); EXPECT_STREQ(_T(""), rev.GetCommitterEmail()); EXPECT_STREQ(_T("Working Copy"), rev.GetSubject()); EXPECT_STREQ(_T(""), rev.GetBody()); EXPECT_EQ(0, rev.ParentsCount()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); rev.Clear(); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); EXPECT_EQ(0, rev.GetCommit(_T("aa5b97f89cea6863222823c8289ce392d06d1691"))); EXPECT_STREQ(_T("aa5b97f89cea6863222823c8289ce392d06d1691"), rev.m_CommitHash.ToString()); EXPECT_STREQ(_T("Another dummy with ümlaut"), rev.GetAuthorName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail()); EXPECT_STREQ(_T("2015-03-14 22:30:06"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Another dummy with ümlaut"), rev.GetCommitterName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail()); EXPECT_STREQ(_T("2015-03-14 22:30:06"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Subject line"), rev.GetSubject()); EXPECT_STREQ(_T("\nalso some more lines\n\nhere in body\n\nSigned-off-by: Another dummy with ümlaut <*****@*****.**>\n"), rev.GetBody()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); rev.Clear(); EXPECT_TRUE(rev.m_CommitHash.IsEmpty()); EXPECT_EQ(0, rev.GetCommit(_T("1fc3c9688e27596d8717b54f2939dc951568f6cb"))); EXPECT_STREQ(_T("1fc3c9688e27596d8717b54f2939dc951568f6cb"), rev.m_CommitHash.ToString()); EXPECT_STREQ(_T("Some other User"), rev.GetAuthorName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail()); EXPECT_STREQ(_T("2015-03-07 18:03:39"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail()); EXPECT_STREQ(_T("2015-03-07 18:03:39"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Added an ascii file"), rev.GetSubject()); EXPECT_STREQ(_T(""), rev.GetBody()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); rev.Clear(); EXPECT_EQ(-1, rev.GetCommit(_T("does-not-exist"))); EXPECT_FALSE(rev.GetLastErr().IsEmpty()); EXPECT_TRUE(rev.m_CommitHash.IsEmpty()); rev.Clear(); CGitHash hash(_T("aa5b97f89cea6863222823c8289ce392d06d1691")); EXPECT_EQ(0, rev.GetCommitFromHash(hash)); EXPECT_EQ(hash, rev.m_CommitHash); EXPECT_STREQ(_T("Another dummy with ümlaut"), rev.GetAuthorName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail()); EXPECT_STREQ(_T("2015-03-14 22:30:06"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Another dummy with ümlaut"), rev.GetCommitterName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail()); EXPECT_STREQ(_T("2015-03-14 22:30:06"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Subject line"), rev.GetSubject()); EXPECT_STREQ(_T("\nalso some more lines\n\nhere in body\n\nSigned-off-by: Another dummy with ümlaut <*****@*****.**>\n"), rev.GetBody()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); rev.Clear(); EXPECT_EQ(0, rev.GetCommit(_T("8d1ebbcc7eeb63af10ff8bcf7712afb9fcc90b8a"))); EXPECT_STREQ(_T("8d1ebbcc7eeb63af10ff8bcf7712afb9fcc90b8a"), rev.m_CommitHash.ToString()); EXPECT_STREQ(_T("Sven Strickroth"), rev.GetAuthorName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail()); EXPECT_STREQ(_T("2015-03-04 17:50:24"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail()); EXPECT_STREQ(_T("2015-03-04 17:50:24"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Merge branch 'for-merge' into subdir/branch"), rev.GetSubject()); EXPECT_STREQ(_T(""), rev.GetBody()); EXPECT_EQ(0, rev.ParentsCount()); EXPECT_EQ(0, rev.GetParentFromHash(rev.m_CommitHash)); ASSERT_EQ(2, rev.ParentsCount()); EXPECT_STREQ(_T("3686b9cf74f1a4ef96d6bfe736595ef9abf0fb8d"), rev.m_ParentHash[0].ToString()); EXPECT_STREQ(_T("1ce788330fd3a306c8ad37654063ceee13a7f172"), rev.m_ParentHash[1].ToString()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); rev.Clear(); EXPECT_TRUE(rev.m_CommitHash.IsEmpty()); EXPECT_EQ(0, rev.GetCommit(_T("844309789a13614b52d5e7cbfe6350dd73d1dc72"))); // root-commit EXPECT_STREQ(_T("844309789a13614b52d5e7cbfe6350dd73d1dc72"), rev.m_CommitHash.ToString()); EXPECT_STREQ(_T("Sven Strickroth"), rev.GetAuthorName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail()); EXPECT_STREQ(_T("2015-03-04 17:35:13"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail()); EXPECT_STREQ(_T("2015-03-04 17:35:13"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("added ansi file"), rev.GetSubject()); EXPECT_STREQ(_T(""), rev.GetBody()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); EXPECT_EQ(0, rev.ParentsCount()); EXPECT_EQ(0, rev.GetParentFromHash(rev.m_CommitHash)); EXPECT_EQ(0, rev.ParentsCount()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); rev.Clear(); // GPG signed commit which was also amended with different dates EXPECT_EQ(0, rev.GetCommit(_T("subdir/branch"))); EXPECT_STREQ(_T("4c5c93d2a0b368bc4570d5ec02ab03b9c4334d44"), rev.m_CommitHash.ToString()); EXPECT_STREQ(_T("Sven Strickroth"), rev.GetAuthorName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail()); EXPECT_STREQ(_T("2015-03-16 12:52:29"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName()); EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail()); EXPECT_STREQ(_T("2015-03-16 13:06:08"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S")); EXPECT_STREQ(_T("Several actions"), rev.GetSubject()); EXPECT_STREQ(_T("\n* amended with different date\n* make utf16-be-nobom.txt a symlink ti ascii.txt\n* remove utf8-bom.txt\n* Copied ascii.txt\n\nSigned-off-by: Sven Strickroth <*****@*****.**>\n"), rev.GetBody()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); EXPECT_EQ(0, rev.ParentsCount()); EXPECT_EQ(0, rev.GetParentFromHash(rev.m_CommitHash)); ASSERT_EQ(1, rev.ParentsCount()); EXPECT_STREQ(_T("aa5b97f89cea6863222823c8289ce392d06d1691"), rev.m_ParentHash[0].ToString()); EXPECT_TRUE(rev.GetLastErr().IsEmpty()); }