1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 #include "TortoiseProc.h"
\r
22 #include "ProgressDlg.h"
\r
23 #include "TGitPath.h"
\r
24 #include "Registry.h"
\r
25 #include "UnicodeUtils.h"
\r
26 #include "TempFile.h"
\r
30 m_bCancelled = FALSE;
\r
35 m_bNoLineNo = false;
\r
39 // m_progressDlg.Stop();
\r
42 BOOL CBlame::BlameCallback(LONG linenumber, git_revnum_t revision, const CString& author, const CString& date,
\r
43 git_revnum_t merged_revision, const CString& merged_author, const CString& merged_date, const CString& merged_path,
\r
44 const CStringA& line)
\r
50 git_revnum_t origrev = revision;
\r
52 if (((m_lowestrev < 0)||(m_lowestrev > revision))&&(revision >= 0))
\r
53 m_lowestrev = revision;
\r
54 if (m_highestrev < revision)
\r
55 m_highestrev = revision;
\r
57 CStringA dateA(date);
\r
58 CStringA authorA(author);
\r
59 CStringA pathA(merged_path);
\r
61 if (!merged_author.IsEmpty() && (merged_revision > 0))
\r
63 dateA = CStringA(merged_date);
\r
64 authorA = CStringA(merged_author);
\r
65 revision = merged_revision;
\r
67 m_bHasMerges = true;
\r
70 if (pathA.Find(' ') >= 60)
\r
72 // the merge path has spaces in it:
\r
73 // TortoiseBlame can't deal with such paths if the space is after
\r
74 // the 60 char which is reserved for the path length in the blame file
\r
75 // To avoid these problems, we escape the space
\r
76 // (not the best solution, but it works)
\r
77 pathA.Replace(" ", "%20");
\r
79 if (authorA.GetLength() > 30 )
\r
80 authorA = authorA.Left(30);
\r
82 infolineA.Format("%c %6ld %6ld %-30s %-60s %-30s ", c, revision, origrev, (LPCSTR)dateA, (LPCSTR)pathA, (LPCSTR)authorA);
\r
84 infolineA.Format("%c %6ld %6ld %6ld %-30s %-60s %-30s ", c, linenumber, revision, origrev, (LPCSTR)dateA, (LPCSTR)pathA, (LPCSTR)authorA);
\r
86 fulllineA.TrimRight("\r\n");
\r
88 if (m_saveFile.m_hFile != INVALID_HANDLE_VALUE)
\r
90 m_saveFile.WriteString(infolineA);
\r
91 m_saveFile.WriteString(fulllineA);
\r
100 BOOL CBlame::Log(git_revnum_t revision, const CString& /*author*/, const CString& /*date*/, const CString& message, LogChangedPathArray * /*cpaths*/, apr_time_t /*time*/, int /*filechanges*/, BOOL /*copies*/, DWORD /*actions*/, BOOL /*children*/)
\r
102 m_progressDlg.SetProgress(m_highestrev - revision, m_highestrev);
\r
103 if (m_saveLog.m_hFile != INVALID_HANDLE_VALUE)
\r
105 CStringA msgutf8 = CUnicodeUtils::GetUTF8(message);
\r
106 int length = msgutf8.GetLength();
\r
107 m_saveLog.Write(&revision, sizeof(LONG));
\r
108 m_saveLog.Write(&length, sizeof(int));
\r
109 m_saveLog.Write((LPCSTR)msgutf8, length);
\r
115 BOOL CBlame::Cancel()
\r
117 // if (m_progressDlg.HasUserCancelled())
\r
118 // m_bCancelled = TRUE;
\r
119 return m_bCancelled;
\r
122 CString CBlame::BlameToTempFile(const CTGitPath& path, GitRev startrev, GitRev endrev, GitRev pegrev,
\r
123 CString& logfile, const CString& options, BOOL includemerge,
\r
124 BOOL showprogress, BOOL ignoremimetype)
\r
127 // if the user specified to use another tool to show the blames, there's no
\r
128 // need to fetch the log later: only TortoiseBlame uses those logs to give
\r
129 // the user additional information for the blame.
\r
130 BOOL extBlame = CRegDWORD(_T("Software\\TortoiseGit\\TextBlame"), FALSE);
\r
133 m_sSavePath = CTempFiles::Instance().GetTempFilePath(false).GetWinPathString();
\r
134 if (m_sSavePath.IsEmpty())
\r
136 temp = path.GetFileExtension();
\r
137 if (!temp.IsEmpty() && !extBlame)
\r
138 m_sSavePath += temp;
\r
139 if (!m_saveFile.Open(m_sSavePath, CFile::typeText | CFile::modeReadWrite | CFile::modeCreate))
\r
142 m_bNoLineNo = false;
\r
143 headline.Format(_T("%c %-6s %-6s %-6s %-30s %-60s %-30s %-s \n"), ' ', _T("line"), _T("rev"), _T("rev"), _T("date"), _T("path"), _T("author"), _T("content"));
\r
144 m_saveFile.WriteString(headline);
\r
145 m_saveFile.WriteString(_T("\n"));
\r
146 m_progressDlg.SetTitle(IDS_BLAME_PROGRESSTITLE);
\r
147 m_progressDlg.SetAnimation(IDR_DOWNLOAD);
\r
148 m_progressDlg.SetShowProgressBar(TRUE);
\r
151 m_progressDlg.ShowModeless(CWnd::FromHandle(hWndExplorer));
\r
153 m_progressDlg.FormatNonPathLine(1, IDS_BLAME_PROGRESSINFO);
\r
154 m_progressDlg.FormatNonPathLine(2, IDS_BLAME_PROGRESSINFOSTART);
\r
155 m_progressDlg.SetCancelMsg(IDS_BLAME_PROGRESSCANCEL);
\r
156 m_progressDlg.SetTime(FALSE);
\r
157 m_nHeadRev = endrev;
\r
158 if (m_nHeadRev < 0)
\r
159 m_nHeadRev = GetHEADRevision(path);
\r
160 m_progressDlg.SetProgress(0, m_nHeadRev);
\r
162 m_bHasMerges = false;
\r
163 BOOL bBlameSuccesful = this->Blame(path, startrev, endrev, pegrev, options, !!ignoremimetype, !!includemerge);
\r
164 if ( !bBlameSuccesful && !pegrev.IsValid() )
\r
166 // retry with the end rev as peg rev
\r
167 if (this->Blame(path, startrev, endrev, endrev, options, !!ignoremimetype, !!includemerge))
\r
169 bBlameSuccesful = TRUE;
\r
173 if (!bBlameSuccesful)
\r
175 m_saveFile.Close();
\r
176 DeleteFile(m_sSavePath);
\r
177 m_sSavePath.Empty();
\r
179 else if (!extBlame)
\r
181 m_progressDlg.FormatNonPathLine(2, IDS_BLAME_PROGRESSLOGSTART);
\r
182 m_progressDlg.SetProgress(0, m_highestrev);
\r
183 logfile = CTempFiles::Instance().GetTempFilePath(false).GetWinPathString();
\r
184 if (!m_saveLog.Open(logfile, CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate))
\r
187 return m_sSavePath;
\r
189 BOOL bRet = ReceiveLog(CTGitPathList(path), pegrev, m_nHeadRev, m_lowestrev, 0, FALSE, m_bHasMerges, false);
\r
193 DeleteFile(logfile);
\r
201 m_progressDlg.Stop();
\r
202 if (m_saveFile.m_hFile != INVALID_HANDLE_VALUE)
\r
203 m_saveFile.Close();
\r
205 return m_sSavePath;
\r
208 BOOL CBlame::Notify(const CTGitPath& /*path*/, svn_wc_notify_action_t /*action*/,
\r
209 svn_node_kind_t /*kind*/, const CString& /*mime_type*/,
\r
210 svn_wc_notify_state_t /*content_state*/,
\r
211 svn_wc_notify_state_t /*prop_state*/, LONG rev,
\r
212 const svn_lock_t * /*lock*/, svn_wc_notify_lock_state_t /*lock_state*/,
\r
213 svn_error_t * /*err*/, apr_pool_t * /*pool*/)
\r
215 m_progressDlg.FormatNonPathLine(2, IDS_BLAME_PROGRESSINFO2, rev, m_nHeadRev);
\r
216 m_progressDlg.SetProgress(rev, m_nHeadRev);
\r
220 bool CBlame::BlameToFile(const CTGitPath& path, GitRev startrev, GitRev endrev, GitRev peg,
\r
221 const CTGitPath& tofile, const CString& options, BOOL ignoremimetype, BOOL includemerge)
\r
224 if (!m_saveFile.Open(tofile.GetWinPathString(), CFile::typeText | CFile::modeReadWrite | CFile::modeCreate))
\r
226 m_bNoLineNo = true;
\r
227 m_nHeadRev = endrev;
\r
228 if (m_nHeadRev < 0)
\r
229 m_nHeadRev = GetHEADRevision(path);
\r
231 BOOL bBlameSuccesful = this->Blame(path, startrev, endrev, peg, options, !!ignoremimetype, !!includemerge);
\r
232 if ( !bBlameSuccesful && !peg.IsValid() )
\r
234 // retry with the end rev as peg rev
\r
235 if (this->Blame(path, startrev, endrev, endrev, options, !!ignoremimetype, !!includemerge))
\r
237 bBlameSuccesful = TRUE;
\r
242 if (!bBlameSuccesful)
\r
244 m_saveFile.Close();
\r
248 if (m_saveFile.m_hFile != INVALID_HANDLE_VALUE)
\r
249 m_saveFile.Close();
\r