int CGit::Revert(CTGitPath &path,bool keep) { CString cmd, out; if(path.m_Action & CTGitPath::LOGACTIONS_ADDED) { //To init git repository, there are not HEAD, so we can use git reset command cmd.Format(_T("git.exe rm --cache -- \"%s\""),path.GetGitPathString()); if(g_Git.Run(cmd,&out,CP_OEMCP)) return -1; } else if(path.m_Action & CTGitPath::LOGACTIONS_REPLACED ) { cmd.Format(_T("git.exe mv \"%s\" \"%s\""),path.GetGitPathString(),path.GetGitOldPathString()); if(g_Git.Run(cmd,&out,CP_OEMCP)) return -1; cmd.Format(_T("git.exe checkout -f -- \"%s\""),path.GetGitOldPathString()); if(g_Git.Run(cmd,&out,CP_OEMCP)) return -1; } else { cmd.Format(_T("git.exe checkout -f -- \"%s\""),path.GetGitPathString()); if(g_Git.Run(cmd,&out,CP_OEMCP)) return -1; } return 0; }
int CGit::GetOneFile(CString Refname, CTGitPath &path, const CString &outputfile) { if(g_Git.m_IsUseGitDLL) { CAutoLocker lock(g_Git.m_critGitDllSec); try { g_Git.CheckAndInitDll(); CStringA ref, patha, outa; ref = CUnicodeUtils::GetMulti(Refname, CP_UTF8); patha = CUnicodeUtils::GetMulti(path.GetGitPathString(), CP_UTF8); outa = CUnicodeUtils::GetMulti(outputfile, CP_UTF8); ::DeleteFile(outputfile); return git_checkout_file((const char*)ref.GetBuffer(),(const char*)patha.GetBuffer(),(const char*)outa.GetBuffer()); }catch(...) { return -1; } } else { CString cmd; cmd.Format(_T("git.exe cat-file -p %s:\"%s\""), Refname, path.GetGitPathString()); return RunLogFile(cmd,outputfile); } }
int CGitPropertyPage::LogThread() { CTGitPath path(filenames.front().c_str()); CAutoLocker lock(g_Git.m_critGitDllSec); // HACK for libgit2 CString ProjectTopDir; if(!path.HasAdminDir(&ProjectTopDir)) return 0; CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir)); if (!repository) return 0; int stripLength = ProjectTopDir.GetLength(); if (ProjectTopDir[stripLength - 1] != _T('\\')) ++stripLength; CTGitPath relatepath; relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength)); CAutoCommit commit(FindFileRecentCommit(repository, relatepath.GetGitPathString())); if (commit) { SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)(git_commit*)commit); } else { SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL); } return 0; }
CGitProgressList::WC_File_NotificationData::WC_File_NotificationData(const CTGitPath& path, git_wc_notify_action_t action) : NotificationData() { this->action = action; this->path = path; sPathColumnText = path.GetGitPathString(); switch (action) { case git_wc_notify_add: sActionColumnText.LoadString(IDS_SVNACTION_ADD); break; case git_wc_notify_resolved: sActionColumnText.LoadString(IDS_SVNACTION_RESOLVE); break; case git_wc_notify_revert: sActionColumnText.LoadString(IDS_SVNACTION_REVERT); break; case git_wc_notify_checkout: sActionColumnText.LoadString(IDS_PROGRS_CMD_CHECKOUT); break; default: break; } }
int CFileDiffDlg::RevertSelectedItemToVersion(CString rev) { if (rev.IsEmpty() || rev == GIT_REV_ZERO) return 0; POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); int index; int count = 0; while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0) { CString cmd, out; CTGitPath *fentry = (CTGitPath *)m_arFilteredList[index]; cmd.Format(_T("git.exe checkout %s -- \"%s\""), (LPCTSTR)rev, (LPCTSTR)fentry->GetGitPathString()); if (g_Git.Run(cmd, &out, CP_UTF8)) { if (CMessageBox::Show(NULL, out, _T("TortoiseGit"), 2, IDI_WARNING, CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2) break; } else count++; } CString out; out.Format(IDS_STATUSLIST_FILESREVERTED, count, (LPCTSTR)rev); CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK); return 0; }
void CMassiveGitTaskBase::AddFile(const CTGitPath& filename) { assert(m_bUnused); if (m_bIsPath) m_pathList.AddPath(filename); else m_itemList.push_back(filename.GetGitPathString()); }
void CTGitPathList::RemoveItem(CTGitPath & path) { PathVector::iterator it; for(it = m_paths.begin(); it != m_paths.end(); ++it) { if (it->GetGitPathString()==path.GetGitPathString()) { m_paths.erase(it); return; } } }
void CGitPropertyPage::InitWorkfileView() { if (filenames.empty()) return; CTGitPath path(filenames.front().c_str()); CString ProjectTopDir; if(!path.HasAdminDir(&ProjectTopDir)) return; CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir)); if (!repository) return; CString username; CString useremail; CString autocrlf; CString safecrlf; CAutoConfig config(repository); if (config) { config.GetString(L"user.name", username); config.GetString(L"user.email", useremail); config.GetString(L"core.autocrlf", autocrlf); config.GetString(L"core.safecrlf", safecrlf); } CString branch; CString remotebranch; if (!git_repository_head_detached(repository)) { CAutoReference head; if (git_repository_head_unborn(repository)) { git_reference_lookup(head.GetPointer(), repository, "HEAD"); branch = CUnicodeUtils::GetUnicode(git_reference_symbolic_target(head)); if (branch.Find(_T("refs/heads/")) == 0) branch = branch.Mid(11); // 11 = len("refs/heads/") } else if (!git_repository_head(head.GetPointer(), repository)) { const char * branchChar = git_reference_shorthand(head); branch = CUnicodeUtils::GetUnicode(branchChar); const char * branchFullChar = git_reference_name(head); CAutoBuf upstreambranchname; if (!git_branch_upstream_name(upstreambranchname, repository, branchFullChar)) { remotebranch = CUnicodeUtils::GetUnicode(CStringA(upstreambranchname->ptr, (int)upstreambranchname->size)); remotebranch = remotebranch.Mid(13); // 13=len("refs/remotes/") } } } else branch = _T("detached HEAD"); if (autocrlf.Trim().IsEmpty()) autocrlf = _T("false"); if (safecrlf.Trim().IsEmpty()) safecrlf = _T("false"); SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim()); SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim()); SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim()); SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim()); SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim()); SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch); git_oid oid; CAutoCommit HEADcommit; if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(HEADcommit.GetPointer(), repository, &oid) && HEADcommit) DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE); { int stripLength = ProjectTopDir.GetLength(); if (ProjectTopDir[stripLength - 1] != _T('\\')) ++stripLength; bool allAreFiles = true; for (auto it = filenames.cbegin(); it < filenames.cend(); ++it) { if (PathIsDirectory(it->c_str())) { allAreFiles = false; break; } } if (allAreFiles) { size_t assumevalid = 0; size_t skipworktree = 0; size_t executable = 0; size_t symlink = 0; do { CAutoIndex index; if (git_repository_index(index.GetPointer(), repository)) break; for (auto it = filenames.cbegin(); it < filenames.cend(); ++it) { CTGitPath file; file.SetFromWin(CString(it->c_str()).Mid(stripLength)); CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8); size_t idx; if (!git_index_find(&idx, index, pathA)) { const git_index_entry *e = git_index_get_byindex(index, idx); if (e->flags & GIT_IDXENTRY_VALID) ++assumevalid; if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) ++skipworktree; if (e->mode & 0111) ++executable; if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK) ++symlink; } else { // do not show checkboxes for unversioned files ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE); ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE); ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE); ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE); break; } } } while (0); if (assumevalid != 0 && assumevalid != filenames.size()) { SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0); SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0); } else SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0); if (skipworktree != 0 && skipworktree != filenames.size()) { SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0); SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0); } else SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0); if (executable != 0 && executable != filenames.size()) { SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0); SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0); EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE); } else { SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0); EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), (executable == 0) ? TRUE : FALSE); } if (symlink != 0 && symlink != filenames.size()) { SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SYMLINK), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0); SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_INDETERMINATE, 0); EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE); } else { SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, (symlink == 0) ? BST_UNCHECKED : BST_CHECKED, 0); EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), (symlink == 0) ? TRUE : FALSE); } } else { ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE); ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE); ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE); ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE); } } if (filenames.size() == 1 && !PathIsDirectory(filenames[0].c_str())) { SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, CString(MAKEINTRESOURCE(IDS_LOADING))); _beginthread(LogThreadEntry, 0, this); } else ShowWindow(GetDlgItem(m_hwnd, IDC_STATIC_LASTMODIFIED), SW_HIDE); }
int CTGitPathList::ParserFromLog(BYTE_VECTOR &log, bool parseDeletes /*false*/) { this->Clear(); std::map<CString, size_t> duplicateMap; int pos=0; CTGitPath path; m_Action=0; int logend = (int)log.size(); while (pos >= 0 && pos < logend) { path.Reset(); if(log[pos]=='\n') ++pos; if (pos >= logend) return -1; if(log[pos]==':') { bool merged=false; if (pos + 1 >= logend) return -1; if(log[pos+1] ==':') { merged=true; } int end=log.find(0,pos); int actionstart=-1; int file1=-1,file2=-1; if( end>0 ) { actionstart=log.find(' ',end-6); pos=actionstart; } if( actionstart>0 ) { ++actionstart; if (actionstart >= logend) return -1; file1 = log.find(0,actionstart); if( file1>=0 ) { ++file1; pos=file1; } if( log[actionstart] == 'C' || log[actionstart] == 'R' ) { file2=file1; file1 = log.find(0,file1); if(file1>=0 ) { ++file1; pos=file1; } } } CString pathname1; CString pathname2; if( file1>=0 ) CGit::StringAppend(&pathname1, &log[file1], CP_UTF8); if( file2>=0 ) CGit::StringAppend(&pathname2, &log[file2], CP_UTF8); if (actionstart < 0) return -1; auto existing = duplicateMap.find(pathname1); if (existing != duplicateMap.end()) { CTGitPath& p = m_paths[existing->second]; p.ParserAction(log[actionstart]); if(merged) p.m_Action |= CTGitPath::LOGACTIONS_MERGED; m_Action |= p.m_Action; } else { int ac=path.ParserAction(log[actionstart] ); ac |= merged?CTGitPath::LOGACTIONS_MERGED:0; path.SetFromGit(pathname1,&pathname2); path.m_Action=ac; //action must be set after setfromgit. SetFromGit will clear all status. this->m_Action|=ac; AddPath(path); duplicateMap.insert(std::pair<CString, size_t>(path.GetGitPathString(), m_paths.size() - 1)); } } else { int tabstart=0; path.Reset(); CString StatAdd; CString StatDel; CString file1; CString file2; tabstart=log.find('\t',pos); if(tabstart >=0) { log[tabstart]=0; CGit::StringAppend(&StatAdd, &log[pos], CP_UTF8); pos=tabstart+1; } tabstart=log.find('\t',pos); if(tabstart >=0) { log[tabstart]=0; CGit::StringAppend(&StatDel, &log[pos], CP_UTF8); pos=tabstart+1; } if(log[pos] == 0) //rename { ++pos; CGit::StringAppend(&file2, &log[pos], CP_UTF8); int sec=log.find(0,pos); if(sec>=0) { ++sec; CGit::StringAppend(&file1, &log[sec], CP_UTF8); } pos=sec; } else { CGit::StringAppend(&file1, &log[pos], CP_UTF8); } path.SetFromGit(file1,&file2); auto existing = duplicateMap.find(path.GetGitPathString()); if (existing != duplicateMap.end()) { CTGitPath& p = m_paths[existing->second]; p.m_StatAdd = StatAdd; p.m_StatDel = StatDel; } else { //path.SetFromGit(pathname); if (parseDeletes) { path.m_StatAdd=_T("0"); path.m_StatDel=_T("0"); path.m_Action |= CTGitPath::LOGACTIONS_DELETED | CTGitPath::LOGACTIONS_MISSING; } else { path.m_StatAdd=StatAdd; path.m_StatDel=StatDel; } AddPath(path); duplicateMap.insert(std::pair<CString, size_t>(path.GetGitPathString(), m_paths.size() - 1)); } } pos=log.findNextString(pos); } return 0; }
BOOL CGitPropertyPage::PageProc (HWND /*hwnd*/, UINT uMessage, WPARAM wParam, LPARAM lParam) { switch (uMessage) { case WM_INITDIALOG: { InitWorkfileView(); return TRUE; } case WM_NOTIFY: { LPNMHDR point = (LPNMHDR)lParam; int code = point->code; // // Respond to notifications. // if (code == PSN_APPLY && m_bChanged) { do { CTGitPath path(filenames.front().c_str()); CString projectTopDir; if(!path.HasAdminDir(&projectTopDir) || path.IsDirectory()) break; int stripLength = projectTopDir.GetLength(); if (projectTopDir[stripLength - 1] != _T('\\')) ++stripLength; CAutoRepository repository(CUnicodeUtils::GetUTF8(projectTopDir)); if (!repository) break; CAutoIndex index; if (git_repository_index(index.GetPointer(), repository)) break; BOOL assumeValid = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_GETCHECK, 0, 0); BOOL skipWorktree = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_GETCHECK, 0, 0); BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0); BOOL symlink = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_GETCHECK, 0, 0); bool changed = false; for (auto it = filenames.cbegin(); it < filenames.cend(); ++it) { CTGitPath file; file.SetFromWin(CString(it->c_str()).Mid(stripLength)); CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8); size_t idx; if (!git_index_find(&idx, index, pathA)) { git_index_entry *e = const_cast<git_index_entry *>(git_index_get_byindex(index, idx)); // HACK if (assumeValid == BST_CHECKED) { if (!(e->flags & GIT_IDXENTRY_VALID)) { e->flags |= GIT_IDXENTRY_VALID; changed = true; } } else if (assumeValid != BST_INDETERMINATE) { if (e->flags & GIT_IDXENTRY_VALID) { e->flags &= ~GIT_IDXENTRY_VALID; changed = true; } } if (skipWorktree == BST_CHECKED) { if (!(e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)) { e->flags_extended |= GIT_IDXENTRY_SKIP_WORKTREE; changed = true; } } else if (skipWorktree != BST_INDETERMINATE) { if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) { e->flags_extended &= ~GIT_IDXENTRY_SKIP_WORKTREE; changed = true; } } if (executable == BST_CHECKED) { if (!(e->mode & 0111)) { e->mode |= 0111; changed = true; } } else if (executable != BST_INDETERMINATE) { if (e->mode & 0111) { e->mode &= ~0111; changed = true; } } if (symlink == BST_CHECKED) { if ((e->mode & GIT_FILEMODE_LINK) != GIT_FILEMODE_LINK) { e->mode |= GIT_FILEMODE_LINK; changed = true; } } else if (symlink != BST_INDETERMINATE) { if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK) { e->mode &= ~GIT_FILEMODE_LINK; changed = true; } } if (changed) git_index_add(index, e); } } if (changed) { if (!git_index_write(index)) m_bChanged = false; } } while (0); } SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE); return TRUE; } case WM_DESTROY: return TRUE; case WM_COMMAND: PageProcOnCommand(wParam); break; } // switch (uMessage) if (uMessage == m_UpdateLastCommit) { DisplayCommit((git_commit *)lParam, IDC_LAST_HASH, IDC_LAST_SUBJECT, IDC_LAST_AUTHOR, IDC_LAST_DATE); return TRUE; } return FALSE; }
CStatusCacheEntry CCachedDirectory::GetStatusFromGit(const CTGitPath &path, CString sProjectRoot) { CString subpaths = path.GetGitPathString(); if(subpaths.GetLength() >= sProjectRoot.GetLength()) { if(subpaths[sProjectRoot.GetLength()] == _T('/')) subpaths=subpaths.Right(subpaths.GetLength() - sProjectRoot.GetLength()-1); else subpaths=subpaths.Right(subpaths.GetLength() - sProjectRoot.GetLength()); } GitStatus *pGitStatus = &CGitStatusCache::Instance().m_GitStatus; UNREFERENCED_PARAMETER(pGitStatus); bool isVersion =true; pGitStatus->IsUnderVersionControl(sProjectRoot, subpaths, path.IsDirectory(), &isVersion); if(!isVersion) { //untracked file bool isDir = path.IsDirectory(); bool isIgnoreFileChanged = pGitStatus->HasIgnoreFilesChanged(sProjectRoot, subpaths, isDir); if( isIgnoreFileChanged) { pGitStatus->LoadIgnoreFile(sProjectRoot, subpaths, isDir); } if (isDir) { CCachedDirectory * dirEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(path, false); /* we needn't watch untracked directory*/ if(dirEntry) { AutoLocker lock(dirEntry->m_critSec); git_wc_status_kind dirstatus = dirEntry->GetCurrentFullStatus() ; if (CGitStatusCache::Instance().IsUnversionedAsModified() || dirstatus == git_wc_status_none || dirstatus >= git_wc_status_normal || isIgnoreFileChanged) {/* status have not initialized*/ bool isignore = false; pGitStatus->IsIgnore(sProjectRoot, subpaths, &isignore, isDir); if (!isignore && CGitStatusCache::Instance().IsUnversionedAsModified()) { dirEntry->EnumFiles(path, TRUE); dirEntry->UpdateCurrentStatus(); return CStatusCacheEntry(dirEntry->GetCurrentFullStatus()); } git_wc_status2_t status2; status2.text_status = status2.prop_status = (isignore? git_wc_status_ignored:git_wc_status_unversioned); // we do not know anything about files here, all we know is that there are not versioned files in this dir dirEntry->m_mostImportantFileStatus = git_wc_status_none; dirEntry->m_ownStatus.SetKind(git_node_dir); dirEntry->m_ownStatus.SetStatus(&status2); dirEntry->m_currentFullStatus = status2.text_status; } return dirEntry->m_ownStatus; } } else /* path is file */ { AutoLocker lock(m_critSec); CString strCacheKey = GetCacheKey(path); if (strCacheKey.IsEmpty()) return CStatusCacheEntry(); CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey); if(itMap == m_entryCache.end() || isIgnoreFileChanged) { git_wc_status2_t status2; bool isignore = false; pGitStatus->IsIgnore(sProjectRoot, subpaths, &isignore, isDir); status2.text_status = status2.prop_status = (isignore? git_wc_status_ignored:git_wc_status_unversioned); AddEntry(path, &status2); return m_entryCache[strCacheKey]; } else { return itMap->second; } } return CStatusCacheEntry(); } else { EnumFiles(path, TRUE); UpdateCurrentStatus(); if (!path.IsDirectory()) return GetCacheStatusForMember(path); return CStatusCacheEntry(m_ownStatus); } }
void CRepositoryBrowser::OpenFile(const CString path, eOpenType mode, bool isSubmodule, CGitHash itemHash) { CTGitPath gitPath(path); CString temppath; CString file; GetTempPath(temppath); CGitHash hash; if (g_Git.GetHash(hash, m_sRevision)) { MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of ") + m_sRevision + _T(".")), _T("TortoiseGit"), MB_ICONERROR); return; } file.Format(_T("%s%s_%s%s"), (LPCTSTR)temppath, (LPCTSTR)gitPath.GetBaseFilename(), (LPCTSTR)hash.ToString().Left(g_Git.GetShortHASHLength()), (LPCTSTR)gitPath.GetFileExtension()); if (isSubmodule) { if (mode == OPEN && !GitAdminDir::IsBareRepo(g_Git.m_CurrentDir)) { CTGitPath subPath = CTGitPath(g_Git.m_CurrentDir); subPath.AppendPathString(gitPath.GetWinPathString()); CAutoRepository repo(subPath.GetGitPathString()); CAutoCommit commit; if (!repo || git_commit_lookup(commit.GetPointer(), repo, (const git_oid *)itemHash.m_hash)) { CString out; out.Format(IDS_REPOBROWSEASKSUBMODULEUPDATE, (LPCTSTR)itemHash.ToString(), (LPCTSTR)gitPath.GetGitPathString()); if (MessageBox(out, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) != IDYES) return; CString sCmd; sCmd.Format(_T("/command:subupdate /bkpath:\"%s\" /selectedpath:\"%s\""), (LPCTSTR)g_Git.m_CurrentDir, (LPCTSTR)gitPath.GetGitPathString()); CAppUtils::RunTortoiseGitProc(sCmd); return; } CString cmd; cmd.Format(_T("/command:repobrowser /path:\"%s\" /rev:%s"), (LPCTSTR)g_Git.CombinePath(path), (LPCTSTR)itemHash.ToString()); CAppUtils::RunTortoiseGitProc(cmd); return; } file += _T(".txt"); CFile submoduleCommit(file, CFile::modeCreate | CFile::modeWrite); CStringA commitInfo = "Subproject commit " + CStringA(itemHash.ToString()); submoduleCommit.Write(commitInfo, commitInfo.GetLength()); } else if (g_Git.GetOneFile(m_sRevision, gitPath, file)) { CString out; out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, (LPCTSTR)gitPath.GetGitPathString(), (LPCTSTR)m_sRevision, (LPCTSTR)file); MessageBox(g_Git.GetGitLastErr(out, CGit::GIT_CMD_GETONEFILE), _T("TortoiseGit"), MB_ICONERROR); return; } if (mode == ALTERNATIVEEDITOR) { CAppUtils::LaunchAlternativeEditor(file); return; } else if (mode == OPEN) { CAppUtils::ShellOpen(file); return; } CAppUtils::ShowOpenWithDialog(file); }
int CGit::Revert(CString commit, CTGitPath &path) { CString cmd, out; if(path.m_Action & CTGitPath::LOGACTIONS_REPLACED && !path.GetGitOldPathString().IsEmpty()) { if (CTGitPath(path.GetGitOldPathString()).IsDirectory()) { CString err; err.Format(_T("Cannot revert renaming of \"%s\". A directory with the old name \"%s\" exists."), path.GetGitPathString(), path.GetGitOldPathString()); ::MessageBox(NULL, err, _T("TortoiseGit"), MB_OK|MB_ICONERROR); return -1; } CString force; // if the filenames only differ in case, we have to pass "-f" if (path.GetGitPathString().CompareNoCase(path.GetGitOldPathString()) == 0) force = _T("-f "); cmd.Format(_T("git.exe mv %s-- \"%s\" \"%s\""), force, path.GetGitPathString(), path.GetGitOldPathString()); if (Run(cmd, &out, CP_UTF8)) { ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR); return -1; } cmd.Format(_T("git.exe checkout %s -f -- \"%s\""), commit, path.GetGitOldPathString()); if (Run(cmd, &out, CP_UTF8)) { ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR); return -1; } } else if(path.m_Action & CTGitPath::LOGACTIONS_ADDED) { //To init git repository, there are not HEAD, so we can use git reset command cmd.Format(_T("git.exe rm -f --cached -- \"%s\""),path.GetGitPathString()); if (Run(cmd, &out, CP_UTF8)) { ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR); return -1; } } else { cmd.Format(_T("git.exe checkout %s -f -- \"%s\""), commit, path.GetGitPathString()); if (Run(cmd, &out, CP_UTF8)) { ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR); return -1; } } if (path.m_Action & CTGitPath::LOGACTIONS_DELETED) { cmd.Format(_T("git.exe add -f -- \"%s\""), path.GetGitPathString()); if (Run(cmd, &out, CP_UTF8)) { ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR); return -1; } } return 0; }
CStatusCacheEntry CCachedDirectory::GetStatusFromGit(const CTGitPath &path, CString sProjectRoot) { CString subpaths = path.GetGitPathString(); if(subpaths.GetLength() >= sProjectRoot.GetLength()) { if(subpaths[sProjectRoot.GetLength()] == _T('/')) subpaths=subpaths.Right(subpaths.GetLength() - sProjectRoot.GetLength()-1); else subpaths=subpaths.Right(subpaths.GetLength() - sProjectRoot.GetLength()); } GitStatus *pGitStatus = &CGitStatusCache::Instance().m_GitStatus; CGitHash head; pGitStatus->GetHeadHash(sProjectRoot,head); bool isVersion =true; pGitStatus->IsUnderVersionControl(sProjectRoot, subpaths, path.IsDirectory(), &isVersion); if(!isVersion) { //untracked file bool isIgnoreFileChanged=false; isIgnoreFileChanged = pGitStatus->IsGitReposChanged(sProjectRoot, subpaths, GIT_MODE_IGNORE); if( isIgnoreFileChanged) { pGitStatus->LoadIgnoreFile(sProjectRoot, subpaths); } if(path.IsDirectory()) { CCachedDirectory * dirEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(path, false); /* we needn't watch untracked directory*/ if(dirEntry) { AutoLocker lock(dirEntry->m_critSec); git_wc_status_kind dirstatus = dirEntry->GetCurrentFullStatus() ; if( dirstatus == git_wc_status_none || dirstatus >= git_wc_status_normal || isIgnoreFileChanged ) {/* status have not initialized*/ git_wc_status2_t status2; bool isignore = false; pGitStatus->IsIgnore(sProjectRoot,subpaths,&isignore); status2.text_status = status2.prop_status = (isignore? git_wc_status_ignored:git_wc_status_unversioned); dirEntry->m_ownStatus.SetStatus(&status2); dirEntry->m_ownStatus.SetKind(git_node_dir); } return dirEntry->m_ownStatus; } } else /* path is file */ { AutoLocker lock(m_critSec); CString strCacheKey = GetCacheKey(path); CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey); if(itMap == m_entryCache.end() || isIgnoreFileChanged) { git_wc_status2_t status2; bool isignore = false; pGitStatus->IsIgnore(sProjectRoot,subpaths,&isignore); status2.text_status = status2.prop_status = (isignore? git_wc_status_ignored:git_wc_status_unversioned); AddEntry(path, &status2); return m_entryCache[strCacheKey]; } else { return itMap->second; } } return CStatusCacheEntry(); } else { EnumFiles((CTGitPath*)&path, TRUE); UpdateCurrentStatus(); return CStatusCacheEntry(m_ownStatus); } }
bool DropMoveCommand::Execute() { CString droppath = parser.GetVal(_T("droptarget")); CString ProjectTop; if (!CTGitPath(droppath).HasAdminDir(&ProjectTop)) return FALSE; if (ProjectTop != g_Git.m_CurrentDir ) { CMessageBox::Show(NULL,_T("Target and source must be the same git repository"),_T("TortoiseGit"),MB_OK); return FALSE; } droppath = droppath.Right(droppath.GetLength()-ProjectTop.GetLength()-1); unsigned long count = 0; pathList.RemoveAdminPaths(); CString sNewName; if ((parser.HasKey(_T("rename")))&&(pathList.GetCount()==1)) { // ask for a new name of the source item do { CRenameDlg renDlg; renDlg.m_windowtitle.LoadString(IDS_PROC_MOVERENAME); renDlg.m_name = pathList[0].GetFileOrDirectoryName(); if (renDlg.DoModal() != IDOK) { return FALSE; } sNewName = renDlg.m_name; } while(sNewName.IsEmpty() || PathFileExists(droppath+_T("\\")+sNewName)); } CSysProgressDlg progress; if (progress.IsValid()) { progress.SetTitle(IDS_PROC_MOVING); progress.SetAnimation(IDR_MOVEANI); progress.SetTime(true); progress.ShowModeless(CWnd::FromHandle(hwndExplorer)); } for(int nPath = 0; nPath < pathList.GetCount(); nPath++) { CTGitPath destPath; if (sNewName.IsEmpty()) destPath = CTGitPath(droppath+_T("\\")+pathList[nPath].GetFileOrDirectoryName()); else destPath = CTGitPath(droppath+_T("\\")+sNewName); if (destPath.Exists()) { CString name = pathList[nPath].GetFileOrDirectoryName(); if (!sNewName.IsEmpty()) name = sNewName; progress.Stop(); CRenameDlg dlg; dlg.m_name = name; dlg.m_windowtitle.Format(IDS_PROC_NEWNAMEMOVE, (LPCTSTR)name); if (dlg.DoModal() != IDOK) { return FALSE; } destPath.SetFromWin(droppath+_T("\\")+dlg.m_name); } CString cmd,out; cmd.Format(_T("git.exe mv -- \"%s\" \"%s\""),pathList[nPath].GetGitPathString(),destPath.GetGitPathString()); if(g_Git.Run(cmd,&out,CP_ACP)) { if (CMessageBox::Show(hwndExplorer, out, _T("TortoiseGit"), MB_YESNO)==IDYES) { #if 0 if (!svn.Move(CTSVNPathList(pathList[nPath]), destPath, TRUE)) { CMessageBox::Show(hwndExplorer, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR); return FALSE; //get out of here } CShellUpdater::Instance().AddPathForUpdate(destPath); #endif } else { //TRACE(_T("%s\n"), (LPCTSTR)svn.GetLastErrorMessage()); CMessageBox::Show(hwndExplorer, _T("Cancel"), _T("TortoiseGit"), MB_ICONERROR); return FALSE; //get out of here } } else CShellUpdater::Instance().AddPathForUpdate(destPath); count++; if (progress.IsValid()) { progress.FormatPathLine(1, IDS_PROC_MOVINGPROG, pathList[nPath].GetWinPath()); progress.FormatPathLine(2, IDS_PROC_CPYMVPROG2, destPath.GetWinPath()); progress.SetProgress(count, pathList.GetCount()); } if ((progress.IsValid())&&(progress.HasUserCancelled())) { CMessageBox::Show(hwndExplorer, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION); return FALSE; } } return true; }
bool PasteMoveCommand::Execute() { CString sDroppath = parser.GetVal(_T("droptarget")); CTGitPath dropPath(sDroppath); if (dropPath.IsAdminDir()) return FALSE; if(!dropPath.HasAdminDir(&g_Git.m_CurrentDir)) return FALSE; GitStatus status; unsigned long count = 0; orgPathList.RemoveAdminPaths(); CString sNewName; CSysProgressDlg progress; progress.SetTitle(IDS_PROC_MOVING); progress.SetAnimation(IDR_MOVEANI); progress.SetTime(true); progress.ShowModeless(CWnd::FromHandle(hwndExplorer)); for (int nPath = 0; nPath < orgPathList.GetCount(); ++nPath) { CTGitPath destPath; if (sNewName.IsEmpty()) destPath = CTGitPath(sDroppath+_T("\\")+orgPathList[nPath].GetFileOrDirectoryName()); else destPath = CTGitPath(sDroppath+_T("\\")+sNewName); if (destPath.Exists()) { CString name = orgPathList[nPath].GetFileOrDirectoryName(); if (!sNewName.IsEmpty()) name = sNewName; progress.Stop(); CRenameDlg dlg; dlg.m_name = name; dlg.m_windowtitle.Format(IDS_PROC_NEWNAMEMOVE, (LPCTSTR)name); if (dlg.DoModal() != IDOK) { return FALSE; } destPath.SetFromWin(sDroppath+_T("\\")+dlg.m_name); } CString top; top.Empty(); orgPathList[nPath].HasAdminDir(&top); git_wc_status_kind s = status.GetAllStatus(orgPathList[nPath]); if ((s == git_wc_status_none)||(s == git_wc_status_unversioned)||(s == git_wc_status_ignored)||top != g_Git.m_CurrentDir) { // source file is unversioned: move the file to the target, then add it MoveFile(orgPathList[nPath].GetWinPath(), destPath.GetWinPath()); CString cmd,output; cmd.Format(_T("git.exe add -- \"%s\""),destPath.GetWinPath()); if (g_Git.Run(cmd, &output, CP_UTF8)) //if (!Git.Add(CTGitorgPathList(destPath), &props, Git_depth_infinity, true, false, true)) { TRACE(_T("%s\n"), output); CMessageBox::Show(hwndExplorer, output, _T("TortoiseGit"), MB_ICONERROR); return FALSE; //get out of here } CShellUpdater::Instance().AddPathForUpdate(destPath); } else { CString cmd,output; cmd.Format(_T("git.exe mv \"%s\" \"%s\""),orgPathList[nPath].GetGitPathString(),destPath.GetGitPathString()); if (g_Git.Run(cmd, &output, CP_UTF8)) //if (!Git.Move(CTGitorgPathList(orgPathList[nPath]), destPath, FALSE)) { #if 0 if (Git.Err && (Git.Err->apr_err == Git_ERR_UNVERSIONED_RESOURCE || Git.Err->apr_err == Git_ERR_CLIENT_MODIFIED)) { // file/folder seems to have local modifications. Ask the user if // a force is requested. CString temp = Git.GetLastErrorMessage(); CString sQuestion(MAKEINTRESOURCE(IDS_PROC_FORCEMOVE)); temp += _T("\n") + sQuestion; if (CMessageBox::Show(hwndExplorer, temp, _T("TortoiseGit"), MB_YESNO)==IDYES) { if (!Git.Move(CTGitPathList(pathList[nPath]), destPath, TRUE)) { CMessageBox::Show(hwndExplorer, Git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR); return FALSE; //get out of here } CShellUpdater::Instance().AddPathForUpdate(destPath); } } else #endif { TRACE(_T("%s\n"), (LPCTSTR)output); CMessageBox::Show(hwndExplorer, output, _T("TortoiseGit"), MB_ICONERROR); return FALSE; //get out of here } } else CShellUpdater::Instance().AddPathForUpdate(destPath); } ++count; if (progress.IsValid()) { progress.FormatPathLine(1, IDS_PROC_MOVINGPROG, orgPathList[nPath].GetWinPath()); progress.FormatPathLine(2, IDS_PROC_CPYMVPROG2, destPath.GetWinPath()); progress.SetProgress(count, orgPathList.GetCount()); } if ((progress.IsValid())&&(progress.HasUserCancelled())) { CMessageBox::Show(hwndExplorer, IDS_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION); return FALSE; } } return true; }
int CGitDiff::Diff(const CTGitPath* pPath, const CTGitPath* pPath2, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/, int jumpToLine, bool bAlternativeTool) { // make sure we have HASHes here, otherwise filenames might be invalid if (rev1 != GIT_REV_ZERO) { CGitHash rev1Hash; if (g_Git.GetHash(rev1Hash, rev1)) { MessageBox(nullptr, g_Git.GetGitLastErr(L"Could not get hash of \"" + rev1 + L"\"."), L"TortoiseGit", MB_ICONERROR); return -1; } rev1 = rev1Hash.ToString(); } if (rev2 != GIT_REV_ZERO) { CGitHash rev2Hash; if (g_Git.GetHash(rev2Hash, rev2)) { MessageBox(nullptr, g_Git.GetGitLastErr(L"Could not get hash of \"" + rev2 + L"\"."), L"TortoiseGit", MB_ICONERROR); return -1; } rev2 = rev2Hash.ToString(); } CString file1; CString title1; CString cmd; if(pPath->IsDirectory() || pPath2->IsDirectory()) { int result; // refresh if result = 1 CTGitPath path = *pPath; CTGitPath path2 = *pPath2; while ((result = SubmoduleDiff(&path, &path2, rev1, rev2)) == 1) { path.SetFromGit(pPath->GetGitPathString()); path2.SetFromGit(pPath2->GetGitPathString()); } return result; } if(rev1 != GIT_REV_ZERO ) { // use original file extension, an external diff tool might need it file1 = CTempFiles::Instance().GetTempFilePath(false, *pPath, rev1).GetWinPathString(); title1 = pPath->GetGitPathString() + L": " + rev1.Left(g_Git.GetShortHASHLength()); if (g_Git.GetOneFile(rev1, *pPath, file1)) { CString out; out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, (LPCTSTR)pPath->GetGitPathString(), (LPCTSTR)rev1, (LPCTSTR)file1); CMessageBox::Show(nullptr, g_Git.GetGitLastErr(out, CGit::GIT_CMD_GETONEFILE), L"TortoiseGit", MB_OK); return -1; } ::SetFileAttributes(file1, FILE_ATTRIBUTE_READONLY); } else { file1 = g_Git.CombinePath(pPath); title1.Format(IDS_DIFF_WCNAME, (LPCTSTR)pPath->GetGitPathString()); if (!PathFileExists(file1)) { CString sMsg; sMsg.Format(IDS_PROC_DIFFERROR_FILENOTINWORKINGTREE, (LPCTSTR)file1); if (MessageBox(nullptr, sMsg, L"TortoiseGit", MB_ICONEXCLAMATION | MB_YESNO) != IDYES) return 1; if (!CCommonAppUtils::FileOpenSave(file1, nullptr, IDS_SELECTFILE, IDS_COMMONFILEFILTER, true)) return 1; title1 = file1; } } CString file2; CString title2; if(rev2 != GIT_REV_ZERO) { CTGitPath fileName = *pPath2; if (pPath2->m_Action & CTGitPath::LOGACTIONS_REPLACED) fileName = CTGitPath(pPath2->GetGitOldPathString()); file2 = CTempFiles::Instance().GetTempFilePath(false, fileName, rev2).GetWinPathString(); title2 = fileName.GetGitPathString() + L": " + rev2.Left(g_Git.GetShortHASHLength()); if (g_Git.GetOneFile(rev2, fileName, file2)) { CString out; out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, (LPCTSTR)pPath2->GetGitPathString(), (LPCTSTR)rev2, (LPCTSTR)file2); CMessageBox::Show(nullptr, g_Git.GetGitLastErr(out, CGit::GIT_CMD_GETONEFILE), L"TortoiseGit", MB_OK); return -1; } ::SetFileAttributes(file2, FILE_ATTRIBUTE_READONLY); } else { file2 = g_Git.CombinePath(pPath2); title2.Format(IDS_DIFF_WCNAME, pPath2->GetGitPathString()); } CAppUtils::DiffFlags flags; flags.bAlternativeTool = bAlternativeTool; CAppUtils::StartExtDiff(file2,file1, title2, title1, g_Git.CombinePath(pPath2), g_Git.CombinePath(pPath), rev2, rev1, flags, jumpToLine); return 0; }
BOOL CTortoiseGitBlameDoc::OnOpenDocument(LPCTSTR lpszPathName,CString Rev) { if(Rev.IsEmpty()) Rev = _T("HEAD"); // enable blame for files which do not exist in current working tree if (!PathFileExists(lpszPathName) && Rev != _T("HEAD")) { if (!CDocument::OnOpenDocument(GetTempFile())) return FALSE; } else { if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; } m_CurrentFileName = lpszPathName; m_Rev=Rev; // (SDI documents will reuse this document) if(!g_Git.CheckMsysGitDir()) { CCommonAppUtils::RunTortoiseGitProc(_T(" /command:settings")); return FALSE; } GitAdminDir admindir; CString topdir; if(!admindir.HasAdminDir(m_CurrentFileName, &topdir)) { CString temp; temp.Format(IDS_CANNOTBLAMENOGIT, CString(m_CurrentFileName)); CMessageBox::Show(NULL, temp, _T("TortoiseGitBlame"), MB_OK); return FALSE; } else { GitAdminDir lastAdminDir; CString oldTopDir; if (topdir != g_Git.m_CurrentDir && CTGitPath(g_Git.m_CurrentDir).HasAdminDir(&oldTopDir) && oldTopDir != topdir) { CString sMsg; sMsg.Format(IDS_ERR_DIFFENERTPREPO, oldTopDir, topdir); MessageBox(NULL, sMsg, _T("TortoiseGitBlame"), MB_OK | MB_ICONERROR); return FALSE; } m_IsGitFile=TRUE; sOrigCWD = g_Git.m_CurrentDir = topdir; CString PathName = m_CurrentFileName; if(topdir[topdir.GetLength()-1] == _T('\\') || topdir[topdir.GetLength()-1] == _T('/')) PathName=PathName.Right(PathName.GetLength()-g_Git.m_CurrentDir.GetLength()); else PathName=PathName.Right(PathName.GetLength()-g_Git.m_CurrentDir.GetLength()-1); CTGitPath path; path.SetFromWin(PathName); if(!g_Git.m_CurrentDir.IsEmpty()) SetCurrentDirectory(g_Git.m_CurrentDir); try { // make sure all config files are read in order to check that none contains an error g_Git.GetConfigValue(_T("doesnot.exist")); } catch (char * libgiterr) { MessageBox(NULL, CString(libgiterr), _T("TortoiseGitBlame"), MB_ICONERROR); return FALSE; } CString cmd; cmd.Format(_T("git.exe blame -s -l %s -- \"%s\""),Rev,path.GetGitPathString()); m_BlameData.clear(); BYTE_VECTOR err; if(g_Git.Run(cmd, &m_BlameData, &err)) { CString str; if (!m_BlameData.empty()) g_Git.StringAppend(&str, &m_BlameData[0], CP_UTF8); if (!err.empty()) g_Git.StringAppend(&str, &err[0], CP_UTF8); MessageBox(NULL, CString(MAKEINTRESOURCE(IDS_BLAMEERROR)) + _T("\n\n") + str, _T("TortoiseGitBlame"), MB_OK); return FALSE; } if(!m_TempFileName.IsEmpty()) { ::DeleteFile(m_TempFileName); m_TempFileName.Empty(); } m_TempFileName=GetTempFile(); cmd.Format(_T("git.exe cat-file blob %s:\"%s\""),Rev,path.GetGitPathString()); if(g_Git.RunLogFile(cmd, m_TempFileName)) { CString str; str.Format(IDS_CHECKOUTFAILED, path.GetGitPathString()); MessageBox(NULL, CString(MAKEINTRESOURCE(IDS_BLAMEERROR)) + _T("\n\n") + str, _T("TortoiseGitBlame"), MB_OK); return FALSE; } m_GitPath = path; if (GetMainFrame()->m_wndOutput.LoadHistory(path.GetGitPathString(), m_Rev, (theApp.GetInt(_T("FollowRenames"), 0) == 1))) return FALSE; CTortoiseGitBlameView *pView=DYNAMIC_DOWNCAST(CTortoiseGitBlameView,GetMainFrame()->GetActiveView()); if(pView == NULL) { CWnd* pWnd = GetMainFrame()->GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE); if (pWnd != NULL && pWnd->IsKindOf(RUNTIME_CLASS(CTortoiseGitBlameView))) { pView = (CTortoiseGitBlameView*)pWnd; } else { return FALSE; } } pView->UpdateInfo(); } return TRUE; }
BOOL CTortoiseGitBlameDoc::OnOpenDocument(LPCTSTR lpszPathName,CString Rev) { if(Rev.IsEmpty()) Rev = _T("HEAD"); // enable blame for files which do not exist in current working tree if (!PathFileExists(lpszPathName) && Rev != _T("HEAD")) { if (!CDocument::OnOpenDocument(GetTempFile())) return FALSE; } else { if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; } m_CurrentFileName = lpszPathName; m_Rev=Rev; // (SDI documents will reuse this document) if(!g_Git.CheckMsysGitDir()) { CCommonAppUtils::RunTortoiseGitProc(_T(" /command:settings")); return FALSE; } CString topdir; if (!GitAdminDir::HasAdminDir(m_CurrentFileName, &topdir)) { CString temp; temp.Format(IDS_CANNOTBLAMENOGIT, (LPCTSTR)m_CurrentFileName); MessageBox(nullptr, temp, _T("TortoiseGitBlame"), MB_OK | MB_ICONERROR); return FALSE; } else { m_IsGitFile=TRUE; sOrigCWD = g_Git.m_CurrentDir = topdir; CString PathName = m_CurrentFileName; if(topdir[topdir.GetLength()-1] == _T('\\') || topdir[topdir.GetLength()-1] == _T('/')) PathName=PathName.Right(PathName.GetLength()-g_Git.m_CurrentDir.GetLength()); else PathName=PathName.Right(PathName.GetLength()-g_Git.m_CurrentDir.GetLength()-1); CTGitPath path; path.SetFromWin(PathName); if(!g_Git.m_CurrentDir.IsEmpty()) SetCurrentDirectory(g_Git.m_CurrentDir); try { // make sure all config files are read in order to check that none contains an error g_Git.GetConfigValue(_T("doesnot.exist")); } catch (char * libgiterr) { MessageBox(nullptr, CString(libgiterr), _T("TortoiseGitBlame"), MB_ICONERROR); return FALSE; } CString cmd, option; int dwDetectMovedOrCopiedLines = theApp.GetInt(_T("DetectMovedOrCopiedLines"), BLAME_DETECT_MOVED_OR_COPIED_LINES_DISABLED); int dwDetectMovedOrCopiedLinesNumCharactersWithinFile = theApp.GetInt(_T("DetectMovedOrCopiedLinesNumCharactersWithinFile"), BLAME_DETECT_MOVED_OR_COPIED_LINES_NUM_CHARACTERS_WITHIN_FILE_DEFAULT); int dwDetectMovedOrCopiedLinesNumCharactersFromFiles = theApp.GetInt(_T("DetectMovedOrCopiedLinesNumCharactersFromFiles"), BLAME_DETECT_MOVED_OR_COPIED_LINES_NUM_CHARACTERS_FROM_FILES_DEFAULT); switch(dwDetectMovedOrCopiedLines) { default: case BLAME_DETECT_MOVED_OR_COPIED_LINES_DISABLED: option.Empty(); break; case BLAME_DETECT_MOVED_OR_COPIED_LINES_WITHIN_FILE: option.Format(_T("-M%d"), dwDetectMovedOrCopiedLinesNumCharactersWithinFile); break; case BLAME_DETECT_MOVED_OR_COPIED_LINES_FROM_MODIFIED_FILES: option.Format(_T("-C%d"), dwDetectMovedOrCopiedLinesNumCharactersFromFiles); break; case BLAME_DETECT_MOVED_OR_COPIED_LINES_FROM_EXISTING_FILES_AT_FILE_CREATION: option.Format(_T("-C -C%d"), dwDetectMovedOrCopiedLinesNumCharactersFromFiles); break; case BLAME_DETECT_MOVED_OR_COPIED_LINES_FROM_EXISTING_FILES: option.Format(_T("-C -C -C%d"), dwDetectMovedOrCopiedLinesNumCharactersFromFiles); break; } if (theApp.GetInt(_T("IgnoreWhitespace"), 0) == 1) option += _T(" -w"); cmd.Format(_T("git.exe blame -p %s %s -- \"%s\""), (LPCTSTR)option, (LPCTSTR)Rev, (LPCTSTR)path.GetGitPathString()); m_BlameData.clear(); BYTE_VECTOR err; if(g_Git.Run(cmd, &m_BlameData, &err)) { CString str; if (!m_BlameData.empty()) CGit::StringAppend(&str, &m_BlameData[0], CP_UTF8); if (!err.empty()) CGit::StringAppend(&str, &err[0], CP_UTF8); MessageBox(nullptr, CString(MAKEINTRESOURCE(IDS_BLAMEERROR)) + _T("\n\n") + str, _T("TortoiseGitBlame"), MB_OK | MB_ICONERROR); return FALSE; } #ifdef USE_TEMPFILENAME if(!m_TempFileName.IsEmpty()) { ::DeleteFile(m_TempFileName); m_TempFileName.Empty(); } m_TempFileName=GetTempFile(); cmd.Format(_T("git.exe cat-file blob %s:\"%s\""), (LPCTSTR)Rev, (LPCTSTR)path.GetGitPathString()); if(g_Git.RunLogFile(cmd, m_TempFileName)) { CString str; str.Format(IDS_CHECKOUTFAILED, (LPCTSTR)path.GetGitPathString()); MessageBox(nullptr, CString(MAKEINTRESOURCE(IDS_BLAMEERROR)) + _T("\n\n") + str, _T("TortoiseGitBlame"), MB_OK | MB_ICONERROR); return FALSE; } #endif m_GitPath = path; CTortoiseGitBlameView *pView=DYNAMIC_DOWNCAST(CTortoiseGitBlameView,GetMainFrame()->GetActiveView()); if (!pView) { CWnd* pWnd = GetMainFrame()->GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE); if (pWnd && pWnd->IsKindOf(RUNTIME_CLASS(CTortoiseGitBlameView))) { pView = (CTortoiseGitBlameView*)pWnd; } else { return FALSE; } } pView->ParseBlame(); BOOL bShowCompleteLog = (theApp.GetInt(_T("ShowCompleteLog"), 1) == 1); if (bShowCompleteLog && BlameIsLimitedToOneFilename(dwDetectMovedOrCopiedLines)) { if (GetMainFrame()->m_wndOutput.LoadHistory(path.GetGitPathString(), m_Rev, (theApp.GetInt(_T("FollowRenames"), 0) == 1))) return FALSE; } else { std::set<CGitHash> hashes; pView->m_data.GetHashes(hashes); if (GetMainFrame()->m_wndOutput.LoadHistory(hashes)) return FALSE; } pView->MapLineToLogIndex(); pView->UpdateInfo(); if (m_lLine > 0) pView->GotoLine(m_lLine); SetPathName(m_CurrentFileName, FALSE); } return TRUE; }
void CFileDiffDlg::OnContextMenu(CWnd* pWnd, CPoint point) { if ((pWnd==0)||(pWnd != &m_cFileList)) return; if (m_cFileList.GetSelectedCount() == 0) return; // if the context menu is invoked through the keyboard, we have to use // a calculated position on where to anchor the menu on if ((point.x == -1) && (point.y == -1)) { CRect rect; m_cFileList.GetItemRect(m_cFileList.GetSelectionMark(), &rect, LVIR_LABEL); m_cFileList.ClientToScreen(&rect); point = rect.CenterPoint(); } CIconMenu popup; if (popup.CreatePopupMenu()) { int firstEntry = -1; POSITION firstPos = m_cFileList.GetFirstSelectedItemPosition(); if (firstPos) firstEntry = m_cFileList.GetNextSelectedItem(firstPos); CString menuText; popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF); popup.AppendMenuIcon(ID_GNUDIFFCOMPARE, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF); popup.AppendMenu(MF_SEPARATOR, NULL); if (!m_bIsBare) { menuText.Format(IDS_FILEDIFF_POPREVERTTOREV, (LPCTSTR)m_rev1.m_CommitHash.ToString().Left(g_Git.GetShortHASHLength())); popup.AppendMenuIcon(ID_REVERT1, menuText, IDI_REVERT); menuText.Format(IDS_FILEDIFF_POPREVERTTOREV, (LPCTSTR)m_rev2.m_CommitHash.ToString().Left(g_Git.GetShortHASHLength())); popup.AppendMenuIcon(ID_REVERT2, menuText, IDI_REVERT); popup.AppendMenu(MF_SEPARATOR, NULL); } popup.AppendMenuIcon(ID_LOG, IDS_FILEDIFF_LOG, IDI_LOG); if (firstEntry >= 0 && !m_arFilteredList[firstEntry]->IsDirectory()) { if (!m_bIsBare) { popup.AppendMenuIcon(ID_BLAME, IDS_FILEDIFF_POPBLAME, IDI_BLAME); popup.AppendMenu(MF_SEPARATOR, NULL); } popup.AppendMenuIcon(ID_EXPORT, IDS_FILEDIFF_POPEXPORT, IDI_EXPORT); } else if (firstEntry >= 0) popup.AppendMenuIcon(ID_LOGSUBMODULE, IDS_MENULOGSUBMODULE, IDI_LOG); popup.AppendMenu(MF_SEPARATOR, NULL); popup.AppendMenuIcon(ID_SAVEAS, IDS_FILEDIFF_POPSAVELIST, IDI_SAVEAS); popup.AppendMenuIcon(ID_CLIPBOARD_PATH, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP); popup.AppendMenuIcon(ID_CLIPBOARD_ALL, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP); int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0); m_bCancelled = false; switch (cmd) { case ID_COMPARE: { if (!CheckMultipleDiffs()) break; POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); DoDiff(index, false); } } break; case ID_GNUDIFFCOMPARE: { if (!CheckMultipleDiffs()) break; POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { CTGitPath *fd2 = m_arFilteredList[m_cFileList.GetNextSelectedItem(pos)]; CTGitPath *fd1 = fd2; if (fd2->m_Action & CTGitPath::LOGACTIONS_REPLACED) fd1 = new CTGitPath(fd2->GetGitOldPathString()); CAppUtils::StartShowUnifiedDiff(m_hWnd, *fd2, m_rev2.m_CommitHash.ToString(), *fd1, m_rev1.m_CommitHash.ToString()); if (fd1 != fd2) delete fd1; } } break; case ID_REVERT1: RevertSelectedItemToVersion(m_rev1.m_CommitHash.ToString()); break; case ID_REVERT2: RevertSelectedItemToVersion(m_rev2.m_CommitHash.ToString()); break; case ID_BLAME: { if (!CheckMultipleDiffs()) break; POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); CAppUtils::LaunchTortoiseBlame(m_arFilteredList[index]->GetWinPathString(), m_rev1.m_CommitHash.ToString()); } } break; case ID_LOG: case ID_LOGSUBMODULE: { if (!CheckMultipleDiffs()) break; POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); CString sCmd = _T("/command:log"); if (sCmd == ID_LOGSUBMODULE) sCmd += _T(" /submodule"); sCmd += _T(" /path:\"") + m_arFilteredList[index]->GetWinPathString() + _T("\" "); sCmd += _T(" /endrev:") + m_rev1.m_CommitHash.ToString(); CAppUtils::RunTortoiseGitProc(sCmd); } } break; case ID_SAVEAS: { if (m_cFileList.GetSelectedCount() > 0) { CString temp; CTGitPath savePath; CString pathSave; if (!CAppUtils::FileOpenSave(pathSave, NULL, IDS_REPOBROWSE_SAVEAS, IDS_TEXTFILEFILTER, false, m_hWnd, _T(".txt"))) { break; } savePath = CTGitPath(pathSave); // now open the selected file for writing try { CStdioFile file(savePath.GetWinPathString(), CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate); if (m_path1.IsEmpty() && m_path2.IsEmpty()) temp.Format(IDS_FILEDIFF_CHANGEDLISTINTROROOT, (LPCTSTR)m_rev1.m_CommitHash.ToString(), (LPCTSTR)m_rev2.m_CommitHash.ToString()); else temp.Format(IDS_FILEDIFF_CHANGEDLISTINTRO, (LPCTSTR)m_path1.GetGitPathString(), (LPCTSTR)m_rev1.m_CommitHash.ToString(), (LPCTSTR)m_path2.GetGitPathString(), (LPCTSTR)m_rev2.m_CommitHash.ToString()); file.WriteString(temp + _T("\r\n")); POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); CTGitPath* fd = m_arFilteredList[index]; file.WriteString(fd->GetGitPathString()); file.WriteString(_T("\r\n")); } file.Close(); } catch (CFileException* pE) { pE->ReportError(); } } } break; case ID_CLIPBOARD_PATH: { CopySelectionToClipboard(); } break; case ID_CLIPBOARD_ALL: { CopySelectionToClipboard(TRUE); } break; case ID_EXPORT: { // export all changed files to a folder CBrowseFolder browseFolder; browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS; if (browseFolder.Show(GetSafeHwnd(), m_strExportDir) == CBrowseFolder::OK) { POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); CTGitPath* fd = m_arFilteredList[index]; // we cannot export directories or folders if (fd->m_Action == CTGitPath::LOGACTIONS_DELETED || fd->IsDirectory()) continue; CPathUtils::MakeSureDirectoryPathExists(m_strExportDir + _T("\\") + fd->GetContainingDirectory().GetWinPathString()); CString filename = m_strExportDir + _T("\\") + fd->GetWinPathString(); if(m_rev1.m_CommitHash.ToString() == GIT_REV_ZERO) { if(!CopyFile(g_Git.CombinePath(fd), filename, false)) { MessageBox(CFormatMessageWrapper(), _T("TortoiseGit"), MB_OK | MB_ICONERROR); return; } } else { if(g_Git.GetOneFile(m_rev1.m_CommitHash, *fd, filename)) { CString out; out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, (LPCTSTR)fd->GetGitPathString(), (LPCTSTR)m_rev1.m_CommitHash.ToString(), (LPCTSTR)filename); if (CMessageBox::Show(nullptr, g_Git.GetGitLastErr(out, CGit::GIT_CMD_GETONEFILE), _T("TortoiseGit"), 2, IDI_WARNING, CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2) return; } } } } } break; } } }
BOOL CTortoiseGitBlameDoc::OnOpenDocument(LPCTSTR lpszPathName,CString Rev) { if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; m_CurrentFileName=lpszPathName; m_Rev=Rev; // (SDI documents will reuse this document) if(!g_Git.CheckMsysGitDir()) { CMessageBox::Show(NULL,_T("MsysGit have not install or config fail"),_T("TortoiseGitBlame"),MB_OK); return FALSE; } GitAdminDir admindir; CString topdir; if(!admindir.HasAdminDir(lpszPathName,&topdir)) { CMessageBox::Show(NULL,CString(lpszPathName)+_T(" is not controled by git\nJust Show File Context"),_T("TortoiseGitBlame"),MB_OK); } else { m_IsGitFile=TRUE; g_Git.m_CurrentDir=topdir; CString PathName=lpszPathName; if(topdir[topdir.GetLength()-1] == _T('\\') || topdir[topdir.GetLength()-1] == _T('/')) PathName=PathName.Right(PathName.GetLength()-g_Git.m_CurrentDir.GetLength()); else PathName=PathName.Right(PathName.GetLength()-g_Git.m_CurrentDir.GetLength()-1); CTGitPath path; path.SetFromWin(PathName); if(!g_Git.m_CurrentDir.IsEmpty()) SetCurrentDirectory(g_Git.m_CurrentDir); m_GitPath = path; GetMainFrame()->m_wndOutput.LoadHistory(path.GetGitPathString()); CString cmd; if(Rev.IsEmpty()) Rev=_T("HEAD"); cmd.Format(_T("git.exe blame -s -l %s -- \"%s\""),Rev,path.GetGitPathString()); m_BlameData.clear(); BYTE_VECTOR err; if(g_Git.Run(cmd, &m_BlameData, &err)) { CString str; g_Git.StringAppend(&str, &m_BlameData[0], CP_ACP); g_Git.StringAppend(&str, &err[0], CP_ACP); CMessageBox::Show(NULL,CString(_T("Blame Error")) + str,_T("TortoiseGitBlame"),MB_OK); } if(!m_TempFileName.IsEmpty()) { ::DeleteFile(m_TempFileName); m_TempFileName.Empty(); } m_TempFileName=GetTempFile(); if(Rev.IsEmpty()) Rev=_T("HEAD"); cmd.Format(_T("git.exe cat-file blob %s:\"%s\""),Rev,path.GetGitPathString()); if(g_Git.RunLogFile(cmd, m_TempFileName)) { CString str; str.Format(_T("Fail get file %s"), path.GetGitPathString()); CMessageBox::Show(NULL,CString(_T("Blame Error")) + str,_T("TortoiseGitBlame"),MB_OK); } CTortoiseGitBlameView *pView=DYNAMIC_DOWNCAST(CTortoiseGitBlameView,GetMainFrame()->GetActiveView()); if(pView == NULL) { CWnd* pWnd = GetMainFrame()->GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE); if (pWnd != NULL && pWnd->IsKindOf(RUNTIME_CLASS(CTortoiseGitBlameView))) { pView = (CTortoiseGitBlameView*)pWnd; } else { return FALSE; } } pView->UpdateInfo(); } return TRUE; }
void CFileDiffDlg::OnContextMenu(CWnd* pWnd, CPoint point) { if ((pWnd==0)||(pWnd != &m_cFileList)) return; if (m_cFileList.GetSelectedCount() == 0) return; // if the context menu is invoked through the keyboard, we have to use // a calculated position on where to anchor the menu on if ((point.x == -1) && (point.y == -1)) { CRect rect; m_cFileList.GetItemRect(m_cFileList.GetSelectionMark(), &rect, LVIR_LABEL); m_cFileList.ClientToScreen(&rect); point = rect.CenterPoint(); } CIconMenu popup; if (popup.CreatePopupMenu()) { popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF); popup.AppendMenuIcon(ID_BLAME, IDS_FILEDIFF_POPBLAME, IDI_BLAME); popup.AppendMenuIcon(ID_LOG, IDS_FILEDIFF_LOG, IDI_LOG); popup.AppendMenu(MF_SEPARATOR, NULL); popup.AppendMenuIcon(ID_EXPORT, IDS_FILEDIFF_POPEXPORT, IDI_EXPORT); popup.AppendMenu(MF_SEPARATOR, NULL); popup.AppendMenuIcon(ID_SAVEAS, IDS_FILEDIFF_POPSAVELIST, IDI_SAVEAS); popup.AppendMenuIcon(ID_CLIPBOARD_PATH, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP); popup.AppendMenuIcon(ID_CLIPBOARD_ALL, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP); int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0); m_bCancelled = false; switch (cmd) { case ID_COMPARE: { POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); DoDiff(index, false); } } break; case ID_BLAME: { POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); CAppUtils::LaunchTortoiseBlame(m_arFilteredList[index]->GetWinPathString(), m_rev1.m_CommitHash.ToString()); } } break; case ID_LOG: { POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); CString cmd = _T("/command:log"); cmd += _T(" /path:\"")+m_arFilteredList[index]->GetWinPathString()+_T("\" "); cmd += _T(" /endrev:")+m_rev1.m_CommitHash.ToString(); CAppUtils::RunTortoiseProc(cmd); } } break; case ID_SAVEAS: { if (m_cFileList.GetSelectedCount() > 0) { CString temp; CTGitPath savePath; CString pathSave; if (!CAppUtils::FileOpenSave(pathSave, NULL, IDS_REPOBROWSE_SAVEAS, IDS_COMMONFILEFILTER, false, m_hWnd)) { break; } savePath = CTGitPath(pathSave); // now open the selected file for writing try { CStdioFile file(savePath.GetWinPathString(), CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate); // temp.Format(IDS_FILEDIFF_CHANGEDLISTINTRO, (LPCTSTR)m_path1.GetGitPathString(), (LPCTSTR)m_rev1.ToString(), (LPCTSTR)m_path2.GetGitPathString(), (LPCTSTR)m_rev2.ToString()); file.WriteString(temp + _T("\n")); POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); CTGitPath* fd = m_arFilteredList[index]; file.WriteString(fd->GetGitPathString()); file.WriteString(_T("\n")); } file.Close(); } catch (CFileException* pE) { pE->ReportError(); } } } break; case ID_CLIPBOARD_PATH: { CopySelectionToClipboard(); } break; case ID_CLIPBOARD_ALL: { CopySelectionToClipboard(TRUE); } break; case ID_EXPORT: { // export all changed files to a folder CBrowseFolder browseFolder; browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS; if (browseFolder.Show(GetSafeHwnd(), m_strExportDir) == CBrowseFolder::OK) { POSITION pos = m_cFileList.GetFirstSelectedItemPosition(); while (pos) { int index = m_cFileList.GetNextSelectedItem(pos); CTGitPath* fd = m_arFilteredList[index]; // we cannot export directories or folders if (fd->m_Action == CTGitPath::LOGACTIONS_DELETED || fd->IsDirectory()) continue; CAppUtils::CreateMultipleDirectory(m_strExportDir + _T("\\") + fd->GetDirectory().GetWinPathString()); CString filename = m_strExportDir + _T("\\") + fd->GetWinPathString(); if(m_rev1.m_CommitHash.ToString() == GIT_REV_ZERO) { if(!CopyFile(g_Git.m_CurrentDir + _T("\\") + fd->GetWinPath(), filename, false)) { MessageBox(CFormatMessageWrapper(), _T("TortoiseGit"), MB_OK | MB_ICONERROR); return; } } else { if(g_Git.GetOneFile(m_rev1.m_CommitHash, *fd, filename)) { CString out; out.Format(_T("Fail checkout one file %s;%s"), m_rev1.m_CommitHash.ToString(), fd->GetWinPath()); CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK); return; } } } } } break; } } }