int CRebaseDlg::CheckNextCommitIsSquash() { int index; if(m_CommitList.m_IsOldFirst) index=m_CurrentRebaseIndex+1; else index=m_CurrentRebaseIndex-1; GitRev *curRev; do { if(index<0) return -1; if(index>= m_CommitList.GetItemCount()) return -1; curRev=(GitRev*)m_CommitList.m_arShownList[index]; if( curRev->GetAction(&m_CommitList)&CTGitPath::LOGACTIONS_REBASE_SQUASH ) return 0; if( curRev->GetAction(&m_CommitList)&CTGitPath::LOGACTIONS_REBASE_SKIP) { if(m_CommitList.m_IsOldFirst) index++; else index--; } else return -1; }while(curRev->GetAction(&m_CommitList)&CTGitPath::LOGACTIONS_REBASE_SKIP); return -1; }
void CFileDiffDlg::OnTimer(UINT_PTR nIDEvent) { if (m_bThreadRunning) return; if( nIDEvent == IDT_FILTER) { CString sFilterText; KillTimer(IDT_FILTER); m_cFilter.GetWindowText(sFilterText); m_cFileList.SetRedraw(FALSE); m_cFileList.DeleteAllItems(); Filter(sFilterText); m_cFileList.SetRedraw(TRUE); __super::OnTimer(nIDEvent); } if( nIDEvent == IDT_INPUT) { KillTimer(IDT_INPUT); TRACE(_T("Input Timer\r\n")); GitRev gitrev; CString str; int mask = 0; this->m_ctrRev1Edit.GetWindowText(str); if( !gitrev.GetCommit(str) ) { this->m_rev1=gitrev; mask |= 0x1; } this->m_ctrRev2Edit.GetWindowText(str); if( !gitrev.GetCommit(str) ) { this->m_rev2=gitrev; mask |= 0x2; } this->SetURLLabels(mask); if(mask == 0x3) { InterlockedExchange(&m_bThreadRunning, TRUE); if (AfxBeginThread(DiffThreadEntry, this)==NULL) { InterlockedExchange(&m_bThreadRunning, FALSE); CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR); } } } }
bool LogCommand::Execute() { //the log command line looks like this: //command:log path:<path_to_file_or_directory_to_show_the_log_messages> [startrev:<startrevision>] [endrev:<endrevision>] CString val = parser.GetVal(_T("startrev")); if ( val.IsEmpty() ) { // support deprecated parameter prior 1.5.0 val = parser.GetVal(_T("revstart")); } GitRev revstart ; val = parser.GetVal(_T("endrev")); if ( val.IsEmpty() ) { // support deprecated parameter prior 1.5.0 val = parser.GetVal(_T("revend")); } GitRev revend ; val = parser.GetVal(_T("limit")); int limit = _tstoi(val); val = parser.GetVal(_T("pegrev")); if ( val.IsEmpty() ) { // support deprecated parameter prior 1.5.0 val = parser.GetVal(_T("revpeg")); } GitRev pegrev; #if 0 SVNRev pegrev = val.IsEmpty() ? SVNRev() : SVNRev(val); if (!revstart.IsValid()) revstart = SVNRev::REV_HEAD; if (!revend.IsValid()) revend = 0; #endif if (limit == 0) { CRegDWORD reg = CRegDWORD(_T("Software\\TortoiseGit\\NumberOfLogs"), 100); limit = (int)(LONG)reg; } CLogDlg dlg; theApp.m_pMainWnd = &dlg; //dlg.SetParams(cmdLinePath); dlg.SetParams(cmdLinePath, pegrev, revstart, revend, limit); // dlg.SetIncludeMerge(!!parser.HasKey(_T("merge"))); // val = parser.GetVal(_T("propspath")); // if (!val.IsEmpty()) // dlg.SetProjectPropertiesPath(CTSVNPath(val)); dlg.DoModal(); return true; }
void CLogDataVector::updateLanes(GitRev& c, Lanes& lns, CGitHash &sha) { // we could get third argument from c.sha(), but we are in fast path here // and c.sha() involves a deep copy, so we accept a little redundancy if (lns.isEmpty()) lns.init(sha); bool isDiscontinuity; bool isFork = lns.isFork(sha, isDiscontinuity); bool isMerge = (c.ParentsCount() > 1); bool isInitial = (c.ParentsCount() == 0); if (isDiscontinuity) lns.changeActiveLane(sha); // uses previous isBoundary state lns.setBoundary(c.IsBoundary() == TRUE); // update must be here TRACE(_T("%s %d"),c.m_CommitHash.ToString(),c.IsBoundary()); if (isFork) lns.setFork(sha); if (isMerge) lns.setMerge(c.m_ParentHash); //if (c.isApplied) // lns.setApplied(); if (isInitial) lns.setInitial(); lns.getLanes(c.m_Lanes); // here lanes are snapshotted CGitHash nextSha; if( !isInitial) nextSha = c.m_ParentHash[0]; lns.nextParent(nextSha); //if (c.isApplied) // lns.afterApplied(); if (isMerge) lns.afterMerge(); if (isFork) lns.afterFork(); if (lns.isBranch()) lns.afterBranch(); // QString tmp = "", tmp2; // for (uint i = 0; i < c.lanes.count(); i++) { // tmp2.setNum(c.lanes[i]); // tmp.append(tmp2 + "-"); // } // qDebug("%s %s",tmp.latin1(), c.sha.latin1()); }
int CLogDataVector::ParserFromRefLog(CString ref) { if(g_Git.m_IsUseGitDLL) { git_for_each_reflog_ent(CUnicodeUtils::GetUTF8(ref),AddTolist,this); for(int i=0;i<size();i++) { m_pLogCache->m_HashMap[at(i)].m_Ref.Format(_T("%s{%d}"), ref,i); } } else { CString cmd, out; GitRev rev; cmd.Format(_T("git.exe reflog show %s"),ref); if (g_Git.Run(cmd, &out, NULL, CP_UTF8)) return -1; int pos=0; while(pos>=0) { CString one=out.Tokenize(_T("\n"),pos); int ref=one.Find(_T(' '),0); if(ref<0) continue; rev.Clear(); rev.m_CommitHash=g_Git.GetHash(one.Left(ref)); int action=one.Find(_T(' '),ref+1); int message; if(action>0) { rev.m_Ref=one.Mid(ref+1,action-ref-2); message=one.Find(_T(":"),action); if(message>0) { rev.m_RefAction=one.Mid(action+1,message-action-1); rev.GetSubject()=one.Right(one.GetLength()-message-1); } } this->m_pLogCache->m_HashMap[rev.m_CommitHash]=rev; this->push_back(rev.m_CommitHash); } } return 0; }
void CRebaseDlg::FillLogMessageCtrl() { int selCount = m_CommitList.GetSelectedCount(); if (selCount == 1 && (m_RebaseStage == CHOOSE_BRANCH || m_RebaseStage == CHOOSE_COMMIT_PICK_MODE)) { POSITION pos = m_CommitList.GetFirstSelectedItemPosition(); int selIndex = m_CommitList.GetNextSelectedItem(pos); GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_CommitList.m_arShownList.SafeGetAt(selIndex)); m_FileListCtrl.UpdateWithGitPathList(pLogEntry->GetFiles(&m_CommitList)); m_FileListCtrl.m_CurrentVersion = pLogEntry->m_CommitHash; m_FileListCtrl.Show(GITSLC_SHOWVERSIONED); m_LogMessageCtrl.Call(SCI_SETREADONLY, FALSE); m_LogMessageCtrl.SetText(pLogEntry->GetSubject() + _T("\n") + pLogEntry->GetBody()); m_LogMessageCtrl.Call(SCI_SETREADONLY, TRUE); } }
int AddTolist(unsigned char * /*osha1*/, unsigned char *nsha1, const char * /*name*/, unsigned long /*time*/, int /*sz*/, const char *msg, void *data) { CLogDataVector *vector = (CLogDataVector*)data; GitRev rev; rev.m_CommitHash=(char*)nsha1; CString one; g_Git.StringAppend(&one, (BYTE *)msg); int message=one.Find(_T(":"),0); if(message>0) { rev.m_RefAction=one.Left(message); rev.GetSubject()=one.Mid(message+1); } vector->m_pLogCache->m_HashMap[rev.m_CommitHash]=rev; vector->insert(vector->begin(),rev.m_CommitHash); return 0; }
int AddToRefLoglist(unsigned char * /*osha1*/, unsigned char *nsha1, const char * /*name*/, unsigned long time, int /*sz*/, const char *msg, void *data) { std::vector<GitRev> *vector = (std::vector<GitRev> *)data; GitRev rev; rev.m_CommitHash = (char *)nsha1; rev.GetCommitterDate() = CTime(time); CString one; g_Git.StringAppend(&one, (BYTE *)msg); int message = one.Find(_T(":"), 0); if (message > 0) { rev.m_RefAction = one.Left(message); rev.GetSubject() = one.Mid(message + 1); } vector->insert(vector->begin(), rev); return 0; }
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); } }
//CLogDataVector Class int CLogDataVector::ParserFromLog(CTGitPath *path ,int count ,int infomask,CString *from,CString *to) { // only enable --follow on files if ((path == NULL || path->IsDirectory()) && (infomask & CGit::LOG_INFO_FOLLOW)) infomask = infomask ^ CGit::LOG_INFO_FOLLOW; CString hash; CString cmd=g_Git.GetLogCmd(hash,path,count,infomask,from,to,true); if(g_Git.IsInitRepos()) return 0; git_init(); GIT_LOG handle; if(git_open_log(&handle,CUnicodeUtils::GetMulti(cmd,CP_ACP).GetBuffer())) { return -1; } git_get_log_firstcommit(handle); GIT_COMMIT commit; GitRev rev; while (git_get_log_nextcommit(handle, &commit, infomask & CGit::LOG_INFO_FOLLOW) == 0) { if (commit.m_ignore == 1) { git_free_commit(&commit); continue; } CGitHash hash = (char*)commit.m_hash ; rev.Clear(); GitRev *pRev = this->m_pLogCache->GetCacheData(hash); char *note=NULL; git_get_notes(commit.m_hash,¬e); if(note) { pRev->m_Notes.Empty(); g_Git.StringAppend(&pRev->m_Notes,(BYTE*)note); } if(pRev == NULL || !pRev->m_IsFull) { pRev->ParserFromCommit(&commit); pRev->ParserParentFromCommit(&commit); git_free_commit(&commit); //Must call free commit before SafeFetchFullInfo, commit parent is rewrite by log. //file list will wrong if parent rewrite. pRev->SafeFetchFullInfo(&g_Git); } else { ASSERT(pRev->m_CommitHash == hash); pRev->ParserParentFromCommit(&commit); git_free_commit(&commit); } this->push_back(pRev->m_CommitHash); m_HashMap[rev.m_CommitHash]=size()-1; } git_close_log(handle); return 0; }
int CRebaseDlg::DoRebase() { CString cmd,out; if(m_CurrentRebaseIndex <0) return 0; if(m_CurrentRebaseIndex >= m_CommitList.GetItemCount() ) return 0; GitRev *pRev = (GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex]; int mode=pRev->GetAction(&m_CommitList) & CTGitPath::LOGACTIONS_REBASE_MODE_MASK; CString nocommit; if( mode== CTGitPath::LOGACTIONS_REBASE_SKIP) { pRev->GetAction(&m_CommitList)|= CTGitPath::LOGACTIONS_REBASE_DONE; return 0; } if( mode != CTGitPath::LOGACTIONS_REBASE_PICK ) { this->m_SquashMessage+= pRev->GetSubject(); this->m_SquashMessage+= _T("\n"); this->m_SquashMessage+= pRev->GetBody(); } else this->m_SquashMessage.Empty(); if(mode == CTGitPath::LOGACTIONS_REBASE_SQUASH) nocommit=_T(" --no-commit "); CString log; log.Format(_T("%s %d: %s"),CTGitPath::GetActionName(mode),this->GetCurrentCommitID(),pRev->m_CommitHash.ToString()); AddLogString(log); AddLogString(pRev->GetSubject()); if (pRev->GetSubject().IsEmpty()) { CMessageBox::Show(m_hWnd, _T("Found an empty commit message. You have to enter one or rebase cannot proceed."), _T("TortoiseGit"), MB_OK | MB_ICONEXCLAMATION); mode = CTGitPath::LOGACTIONS_REBASE_EDIT; } cmd.Format(_T("git.exe cherry-pick %s %s"),nocommit,pRev->m_CommitHash.ToString()); if(g_Git.Run(cmd,&out,CP_UTF8)) { AddLogString(out); CTGitPathList list; if(g_Git.ListConflictFile(list)) { AddLogString(_T("Get conflict files fail")); return -1; } if(list.GetCount() == 0 ) { if(mode == CTGitPath::LOGACTIONS_REBASE_PICK) { pRev->GetAction(&m_CommitList)|= CTGitPath::LOGACTIONS_REBASE_DONE; return 0; } if(mode == CTGitPath::LOGACTIONS_REBASE_EDIT) { this->m_RebaseStage = REBASE_EDIT ; return -1; // Edit return -1 to stop rebase. } // Squash Case if(CheckNextCommitIsSquash()) { // no squash // let user edit last commmit message this->m_RebaseStage = REBASE_SQUASH_EDIT; return -1; } } if(mode == CTGitPath::LOGACTIONS_REBASE_SQUASH) m_RebaseStage = REBASE_SQUASH_CONFLICT; else m_RebaseStage = REBASE_CONFLICT; return -1; } else { AddLogString(out); if(mode == CTGitPath::LOGACTIONS_REBASE_PICK) { pRev->GetAction(&m_CommitList)|= CTGitPath::LOGACTIONS_REBASE_DONE; return 0; } if(mode == CTGitPath::LOGACTIONS_REBASE_EDIT) { this->m_RebaseStage = REBASE_EDIT ; return -1; // Edit return -1 to stop rebase. } // Squash Case if(CheckNextCommitIsSquash()) { // no squash // let user edit last commmit message this->m_RebaseStage = REBASE_SQUASH_EDIT; return -1; } else if(mode == CTGitPath::LOGACTIONS_REBASE_SQUASH) pRev->GetAction(&m_CommitList)|= CTGitPath::LOGACTIONS_REBASE_DONE; } return 0; }
int ParserFromRefLog(CString ref, std::vector<GitRev> &refloglist) { refloglist.clear(); if (g_Git.m_IsUseLibGit2) { CAutoRepository repo(g_Git.GetGitRepository()); if (!repo) { MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not open repository.")), _T("TortoiseGit"), MB_ICONERROR); return -1; } CAutoReflog reflog; if (git_reflog_read(reflog.GetPointer(), repo, CUnicodeUtils::GetUTF8(ref)) < 0) { MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not read reflog.")), _T("TortoiseGit"), MB_ICONERROR); return -1; } for (size_t i = 0; i < git_reflog_entrycount(reflog); ++i) { const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i); if (!entry) continue; GitRev rev; rev.m_CommitHash = (char *)git_reflog_entry_id_new(entry)->id; rev.m_Ref.Format(_T("%s@{%d}"), ref, i); rev.GetCommitterDate() = CTime(git_reflog_entry_committer(entry)->when.time); if (git_reflog_entry_message(entry) != nullptr) { CString one; g_Git.StringAppend(&one, (BYTE *)git_reflog_entry_message(entry)); int message = one.Find(_T(":"), 0); if (message > 0) { rev.m_RefAction = one.Left(message); rev.GetSubject() = one.Mid(message + 1); } } refloglist.push_back(rev); } } else if (g_Git.m_IsUseGitDLL) { git_for_each_reflog_ent(CUnicodeUtils::GetUTF8(ref), AddToRefLoglist, &refloglist); for (size_t i = 0; i < refloglist.size(); ++i) refloglist[i].m_Ref.Format(_T("%s@{%d}"), ref, i); } else { CString cmd, out; GitRev rev; cmd.Format(_T("git.exe reflog show --pretty=\"%%H %%gD: %%gs\" --date=raw %s"), ref); if (g_Git.Run(cmd, &out, NULL, CP_UTF8)) return -1; int i = 0; CString prefix = ref + _T("@{"); int pos = 0; while (pos >= 0) { CString one = out.Tokenize(_T("\n"), pos); int refPos = one.Find(_T(' '), 0); if (refPos < 0) continue; rev.Clear(); CString hashStr = one.Left(refPos); rev.m_CommitHash = hashStr; rev.m_Ref.Format(_T("%s@{%d}"), ref, i++); int prefixPos = one.Find(prefix, refPos + 1); if (prefixPos != refPos + 1) continue; int spacePos = one.Find(_T(' '), prefixPos + prefix.GetLength() + 1); if (spacePos < 0) continue; CString timeStr = one.Mid(prefixPos + prefix.GetLength(), spacePos - prefixPos - prefix.GetLength()); rev.GetCommitterDate() = CTime(_ttoi(timeStr)); int action = one.Find(_T("}: "), spacePos + 1); if (action > 0) { action += 2; int message = one.Find(_T(":"), action); if (message > 0) { rev.m_RefAction = one.Mid(action + 1, message - action - 1); rev.GetSubject() = one.Right(one.GetLength() - message - 1); } } refloglist.push_back(rev); } } return 0; }
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(); }
//CLogDataVector Class int CLogDataVector::ParserFromLog(CTGitPath *path, int count, int infomask, CString *range) { // only enable --follow on files if ((path == NULL || path->IsDirectory()) && (infomask & CGit::LOG_INFO_FOLLOW)) infomask = infomask ^ CGit::LOG_INFO_FOLLOW; CString gitrange = _T("HEAD"); if (range != nullptr) gitrange = *range; CString cmd = g_Git.GetLogCmd(gitrange, path, count, infomask, true); if (!g_Git.CanParseRev(gitrange)) return 0; try { [] { git_init(); } (); } catch (const char* msg) { MessageBox(NULL, _T("Could not initialize libgit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR); return -1; } GIT_LOG handle; try { CAutoLocker lock(g_Git.m_critGitDllSec); if (git_open_log(&handle,CUnicodeUtils::GetMulti(cmd, CP_UTF8).GetBuffer())) { return -1; } } catch (char* msg) { MessageBox(NULL, _T("Could not open log.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR); return -1; } try { CAutoLocker lock(g_Git.m_critGitDllSec); [&]{ git_get_log_firstcommit(handle); }(); } catch (char* msg) { MessageBox(NULL, _T("Could not get first commit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR); return -1; } int ret = 0; while (ret == 0) { GIT_COMMIT commit; try { CAutoLocker lock(g_Git.m_critGitDllSec); [&]{ ret = git_get_log_nextcommit(handle, &commit, infomask & CGit::LOG_INFO_FOLLOW); }(); } catch (char* msg) { MessageBox(NULL, _T("Could not get next commit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR); break; } if (ret) break; if (commit.m_ignore == 1) { git_free_commit(&commit); continue; } CGitHash hash = (char*)commit.m_hash ; GitRev *pRev = this->m_pLogCache->GetCacheData(hash); char *pNote = nullptr; { CAutoLocker lock(g_Git.m_critGitDllSec); git_get_notes(commit.m_hash, &pNote); } if (pNote) { pRev->m_Notes.Empty(); g_Git.StringAppend(&pRev->m_Notes,(BYTE*)pNote); } ASSERT(pRev->m_CommitHash == hash); pRev->ParserFromCommit(&commit); pRev->ParserParentFromCommit(&commit); git_free_commit(&commit); // Must call free commit before SafeFetchFullInfo, commit parent is rewrite by log. // file list will wrong if parent rewrite. if (!pRev->m_IsFull && (infomask & CGit::LOG_INFO_FULL_DIFF)) { try { pRev->SafeFetchFullInfo(&g_Git); } catch (char * g_last_error) { MessageBox(NULL, _T("Could not fetch full info of a commit.\nlibgit reports:\n") + CString(g_last_error), _T("TortoiseGit"), MB_ICONERROR); return -1; } } this->push_back(pRev->m_CommitHash); m_HashMap[pRev->m_CommitHash] = (int)size() - 1; } { CAutoLocker lock(g_Git.m_critGitDllSec); git_close_log(handle); } return 0; }
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 CPropertiesWnd::UpdateProperties(GitRev *rev) { if(rev) { m_CommitHash->SetValue(rev->m_CommitHash.ToString()); m_AuthorName->SetValue(rev->GetAuthorName()); m_AuthorDate->SetValue(rev->GetAuthorDate().Format(_T("%Y-%m-%d %H:%M"))); m_AuthorEmail->SetValue(rev->GetAuthorEmail()); m_CommitterName->SetValue(rev->GetAuthorName()); m_CommitterEmail->SetValue(rev->GetCommitterEmail()); m_CommitterDate->SetValue(rev->GetCommitterDate().Format(_T("%Y-%m-%d %H:%M"))); m_Subject->SetValue(rev->GetSubject()); m_Body->SetValue(rev->GetBody().Trim()); RemoveParent(); m_ParentGroup; CLogDataVector *pLogEntry = &((CMainFrame*)AfxGetApp()->GetMainWnd())->m_wndOutput.m_LogList.m_logEntries; for (unsigned int i = 0; i < rev->m_ParentHash.size(); ++i) { CString str; CString parentsubject; GitRev *p =NULL; if( pLogEntry->m_pLogCache->m_HashMap.find(rev->m_ParentHash[i]) == pLogEntry->m_pLogCache->m_HashMap.end()) { p=NULL; } else { p= &pLogEntry->m_pLogCache->m_HashMap[rev->m_ParentHash[i]] ; } if(p) parentsubject=p->GetSubject(); str.Format(_T("%d - %s \n %s"),i,rev->m_ParentHash[i].ToString(),parentsubject); CMFCPropertyGridProperty*pProtery=new CMFCPropertyGridProperty( rev->m_ParentHash[i].ToString().Left(8), parentsubject, str ); pProtery->AllowEdit(FALSE); m_ParentGroup->AddSubItem(pProtery); } m_ParentGroup->Expand(); for (int i = 0; i < m_BaseInfoGroup->GetSubItemsCount(); ++i) m_BaseInfoGroup->GetSubItem(i)->SetDescription(m_BaseInfoGroup->GetSubItem(i)->GetValue()); } else { m_CommitHash->SetValue(_T("")); m_AuthorName->SetValue(_T("")); m_AuthorDate->SetValue(_T("")); m_AuthorEmail->SetValue(_T("")); m_CommitterName->SetValue(_T("")); m_CommitterEmail->SetValue(_T("")); m_CommitterDate->SetValue(_T("")); m_Subject->SetValue(_T("")); m_Body->SetValue(_T("")); RemoveParent(); for (int i = 0; i < m_BaseInfoGroup->GetSubItemsCount(); ++i) m_BaseInfoGroup->GetSubItem(i)->SetDescription(_T("")); } this->Invalidate(); m_wndPropList.Invalidate(); }
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()); }
LRESULT CRefLogDlg::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/) { ASSERT(m_pFindDialog != NULL); if (m_RefList.m_arShownList.IsEmpty()) return 0; // If the FR_DIALOGTERM flag is set, // invalidate the handle identifying the dialog box. if (m_pFindDialog->IsTerminating()) { m_pFindDialog = NULL; return 0; } // If the FR_FINDNEXT flag is set, // call the application-defined search routine // to search for the requested string. if (m_pFindDialog->FindNext()) { //read data from dialog CString findString = m_pFindDialog->GetFindString(); bool bFound = false; bool bCaseSensitive = !!(m_pFindDialog->m_nFlags & FR_MATCHCASE); if (!bCaseSensitive) findString.MakeLower(); int i = m_nSearchLine; if (i < 0 || i >= m_RefList.m_arShownList.GetCount()) i = 0; do { GitRev * data = (GitRev*)m_RefList.m_arShownList.SafeGetAt(i); CString str; str += data->m_Ref; str += _T("\n"); str += data->m_RefAction; str += _T("\n"); str += data->m_CommitHash.ToString(); str += _T("\n"); str += data->GetSubject(); str += _T("\n"); str += data->GetBody(); str += _T("\n"); if (!bCaseSensitive) str.MakeLower(); if (str.Find(findString) >= 0) bFound = true; ++i; if(!bFound && i >= m_RefList.m_arShownList.GetCount()) i=0; } while (i != m_nSearchLine && (!bFound)); if (bFound) { m_RefList.SetHotItem(i - 1); m_RefList.EnsureVisible(i - 1, FALSE); m_nSearchLine = i; } else MessageBox(_T("\"") + findString + _T("\" ") + CString(MAKEINTRESOURCE(IDS_NOTFOUND)), _T("TortoiseGit"), MB_ICONINFORMATION); } return 0; }