Fixed issue #4126: Capitalize the first letter in the Push dialog
[TortoiseGit.git] / src / TortoiseGitBlame / TortoiseGitBlameDoc.cpp
blob4ac016d978d437380afce4d9315cd4778f7cc10f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2017, 2019-2021, 2023 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 // TortoiseGitBlameDoc.cpp : implementation of the CTortoiseGitBlameDoc class
24 #include "stdafx.h"
25 #include "TortoiseGitBlame.h"
27 #include "TortoiseGitBlameDoc.h"
28 #include "GitAdminDir.h"
29 #include "Git.h"
30 #include "MainFrm.h"
31 #include "TGitPath.h"
32 #include "TortoiseGitBlameView.h"
33 #include "CmdLineParser.h"
34 #include "CommonAppUtils.h"
35 #include "BlameDetectMovedOrCopiedLines.h"
36 #include "TempFile.h"
37 #include "GitMailmap.h"
39 #ifdef _DEBUG
40 #define new DEBUG_NEW
41 #endif
44 // CTortoiseGitBlameDoc
46 IMPLEMENT_DYNCREATE(CTortoiseGitBlameDoc, CDocument)
48 BEGIN_MESSAGE_MAP(CTortoiseGitBlameDoc, CDocument)
49 END_MESSAGE_MAP()
51 // CTortoiseGitBlameDoc construction/destruction
53 CTortoiseGitBlameDoc::CTortoiseGitBlameDoc()
57 CTortoiseGitBlameDoc::~CTortoiseGitBlameDoc()
61 BOOL CTortoiseGitBlameDoc::OnNewDocument()
63 return TRUE;
65 BOOL CTortoiseGitBlameDoc::OnOpenDocument(LPCWSTR lpszPathName)
67 CCmdLineParser parser(AfxGetApp()->m_lpCmdLine);
68 if (m_bFirstStartup)
70 m_Rev = parser.GetVal(L"rev");
71 m_lLine = static_cast<int>(parser.GetLongVal(L"line"));
72 m_bFirstStartup = false;
74 else
76 m_Rev.Empty();
77 m_lLine = 1;
80 return OnOpenDocument(lpszPathName,m_Rev);
83 BOOL CTortoiseGitBlameDoc::OnOpenDocument(LPCWSTR lpszPathName, CString Rev)
85 if(Rev.IsEmpty())
86 Rev = L"HEAD";
88 // enable blame for files which do not exist in current working tree
89 if (!PathFileExists(lpszPathName) && Rev != L"HEAD")
91 if (!CDocument::OnOpenDocument(CTempFiles::Instance().GetTempFilePath(true).GetWinPathString()))
92 return FALSE;
94 else
96 if (!CDocument::OnOpenDocument(lpszPathName))
97 return FALSE;
100 m_CurrentFileName = lpszPathName;
102 m_Rev=Rev;
104 // (SDI documents will reuse this document)
105 if(!g_Git.CheckMsysGitDir())
107 CCommonAppUtils::RunTortoiseGitProc(L" /command:settings");
108 return FALSE;
110 CString topdir;
111 if (!GitAdminDir::HasAdminDir(m_CurrentFileName, &topdir))
113 CString temp;
114 temp.Format(IDS_CANNOTBLAMENOGIT, static_cast<LPCWSTR>(m_CurrentFileName));
115 MessageBox(nullptr, temp, L"TortoiseGitBlame", MB_OK | MB_ICONERROR);
116 return FALSE;
118 else
120 sOrigCWD = g_Git.m_CurrentDir = topdir;
122 CString PathName = m_CurrentFileName;
123 if (topdir[topdir.GetLength() - 1] == L'\\' || topdir[topdir.GetLength() - 1] == L'/')
124 PathName=PathName.Right(PathName.GetLength()-g_Git.m_CurrentDir.GetLength());
125 else
126 PathName=PathName.Right(PathName.GetLength()-g_Git.m_CurrentDir.GetLength()-1);
128 CTGitPath path;
129 path.SetFromWin(PathName);
131 if(!g_Git.m_CurrentDir.IsEmpty())
132 SetCurrentDirectory(g_Git.m_CurrentDir);
136 // make sure all config files are read in order to check that none contains an error
137 g_Git.GetConfigValue(L"doesnot.exist");
139 // make sure git_init() works and that .git-dir is ok, even if we open a file from another working tree
140 CAutoLocker lock(g_Git.m_critGitDllSec);
141 g_Git.ForceReInitDll();
143 catch (const char* libgiterr)
145 MessageBox(nullptr, CUnicodeUtils::GetUnicode(libgiterr), L"TortoiseGitBlame", MB_ICONERROR);
146 return FALSE;
149 CString cmd, option;
150 int dwDetectMovedOrCopiedLines = theApp.GetInt(L"DetectMovedOrCopiedLines", BLAME_DETECT_MOVED_OR_COPIED_LINES_DISABLED);
151 int dwDetectMovedOrCopiedLinesNumCharactersWithinFile = theApp.GetInt(L"DetectMovedOrCopiedLinesNumCharactersWithinFile", BLAME_DETECT_MOVED_OR_COPIED_LINES_NUM_CHARACTERS_WITHIN_FILE_DEFAULT);
152 int dwDetectMovedOrCopiedLinesNumCharactersFromFiles = theApp.GetInt(L"DetectMovedOrCopiedLinesNumCharactersFromFiles", BLAME_DETECT_MOVED_OR_COPIED_LINES_NUM_CHARACTERS_FROM_FILES_DEFAULT);
153 switch(dwDetectMovedOrCopiedLines)
155 default:
156 case BLAME_DETECT_MOVED_OR_COPIED_LINES_DISABLED:
157 option.Empty();
158 break;
159 case BLAME_DETECT_MOVED_OR_COPIED_LINES_WITHIN_FILE:
160 option.Format(L"-M%d", dwDetectMovedOrCopiedLinesNumCharactersWithinFile);
161 break;
162 case BLAME_DETECT_MOVED_OR_COPIED_LINES_FROM_MODIFIED_FILES:
163 option.Format(L"-C%d", dwDetectMovedOrCopiedLinesNumCharactersFromFiles);
164 break;
165 case BLAME_DETECT_MOVED_OR_COPIED_LINES_FROM_EXISTING_FILES_AT_FILE_CREATION:
166 option.Format(L"-C -C%d", dwDetectMovedOrCopiedLinesNumCharactersFromFiles);
167 break;
168 case BLAME_DETECT_MOVED_OR_COPIED_LINES_FROM_EXISTING_FILES:
169 option.Format(L"-C -C -C%d", dwDetectMovedOrCopiedLinesNumCharactersFromFiles);
170 break;
173 if (theApp.GetInt(L"IgnoreWhitespace", 0) == 1)
174 option += L" -w";
176 bool onlyFirstParent = theApp.GetInt(L"OnlyFirstParent", 0) == 1;
177 if (onlyFirstParent)
179 CString tmpfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
180 cmd.Format(L"git.exe rev-list --first-parent --end-of-options %s --", static_cast<LPCWSTR>(Rev));
181 CString err;
182 CAutoFILE file = _wfsopen(tmpfile, L"wb", SH_DENYWR);
183 if (!file)
185 MessageBox(nullptr, CString(MAKEINTRESOURCE(IDS_BLAMEERROR)) + L"\n\nCould not create temp file!", L"TortoiseGitBlame", MB_OK | MB_ICONERROR);
186 return FALSE;
189 CStringA lastline;
190 if (g_Git.Run(cmd, [&](const CStringA& line)
192 fwrite(lastline + ' ' + line + '\n', sizeof(char), lastline.GetLength() + 1 + line.GetLength() + 1, file);
193 lastline = line;
195 , &err))
197 MessageBox(nullptr, CString(MAKEINTRESOURCE(IDS_BLAMEERROR)) + L"\n\n" + err, L"TortoiseGitBlame", MB_OK | MB_ICONERROR);
198 return FALSE;
200 option.AppendFormat(L" -S \"%s\"", static_cast<LPCWSTR>(tmpfile));
203 cmd.Format(L"git.exe blame -p %s %s -- \"%s\"", static_cast<LPCWSTR>(option), static_cast<LPCWSTR>(Rev), static_cast<LPCWSTR>(path.GetGitPathString()));
204 m_BlameData.clear();
205 BYTE_VECTOR err;
206 if(g_Git.Run(cmd, &m_BlameData, &err))
208 MessageBox(nullptr, CString(MAKEINTRESOURCE(IDS_BLAMEERROR)) + L"\n\n" + err, L"TortoiseGitBlame", MB_OK | MB_ICONERROR);
209 return FALSE;
212 #ifdef USE_TEMPFILENAME
213 m_TempFileName = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
215 cmd.Format(L"git.exe cat-file blob %s:\"%s\"", static_cast<LPCWSTR>(Rev), static_cast<LPCWSTR>(path.GetGitPathString()));
217 if(g_Git.RunLogFile(cmd, m_TempFileName))
219 CString str;
220 str.Format(IDS_CHECKOUTFAILED, static_cast<LPCWSTR>(path.GetGitPathString()));
221 MessageBox(nullptr, CString(MAKEINTRESOURCE(IDS_BLAMEERROR)) + L"\n\n" + str, L"TortoiseGitBlame", MB_OK | MB_ICONERROR);
222 return FALSE;
224 #endif
225 m_GitPath = path;
227 if (CGitMailmap::ShouldLoadMailmap())
228 GitRevLoglist::s_Mailmap = std::make_shared<CGitMailmap>();
229 else if (GitRevLoglist::s_Mailmap.load())
230 GitRevLoglist::s_Mailmap.store(nullptr);
232 CTortoiseGitBlameView *pView=DYNAMIC_DOWNCAST(CTortoiseGitBlameView,GetMainFrame()->GetActiveView());
233 if (!pView)
235 CWnd* pWnd = GetMainFrame()->GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE);
236 if (pWnd && pWnd->IsKindOf(RUNTIME_CLASS(CTortoiseGitBlameView)))
237 pView = static_cast<CTortoiseGitBlameView*>(pWnd);
238 else
239 return FALSE;
241 pView->ParseBlame();
243 BOOL bShowCompleteLog = (theApp.GetInt(L"ShowCompleteLog", 1) == 1);
244 if (bShowCompleteLog && BlameIsLimitedToOneFilename(dwDetectMovedOrCopiedLines) && !onlyFirstParent)
246 if (GetMainFrame()->m_wndOutput.LoadHistory(path.GetGitPathString(), m_Rev, (theApp.GetInt(L"FollowRenames", 0) == 1)))
247 return FALSE;
249 else
251 std::unordered_set<CGitHash> hashes;
252 pView->m_data.GetHashes(hashes);
253 if (GetMainFrame()->m_wndOutput.LoadHistory(hashes))
254 return FALSE;
257 pView->MapLineToLogIndex();
258 pView->UpdateInfo();
259 if (m_lLine > 0)
260 pView->GotoLine(m_lLine);
262 SetPathName(m_CurrentFileName, FALSE);
265 return TRUE;
268 void CTortoiseGitBlameDoc::SetPathName(LPCWSTR lpszPathName, BOOL bAddToMRU)
270 CDocument::SetPathName(lpszPathName, bAddToMRU && (m_Rev == L"HEAD"));
272 this->SetTitle(CString(lpszPathName) + L':' + m_Rev);
275 // CTortoiseGitBlameDoc diagnostics
277 #ifdef _DEBUG
278 void CTortoiseGitBlameDoc::AssertValid() const
280 CDocument::AssertValid();
283 void CTortoiseGitBlameDoc::Dump(CDumpContext& dc) const
285 CDocument::Dump(dc);
287 #endif //_DEBUG
290 // CTortoiseGitBlameDoc commands