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 "TSVNPath.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, svn_revnum_t revision, const CString& author, const CString& date,
\r
43 svn_revnum_t merged_revision, const CString& merged_author, const CString& merged_date, const CString& merged_path,
\r
44 const CStringA& line)
\r
48 svn_revnum_t origrev = revision;
\r
50 if (((m_lowestrev < 0)||(m_lowestrev > revision))&&(revision >= 0))
\r
51 m_lowestrev = revision;
\r
52 if (m_highestrev < revision)
\r
53 m_highestrev = revision;
\r
55 CStringA dateA(date);
\r
56 CStringA authorA(author);
\r
57 CStringA pathA(merged_path);
\r
59 if (!merged_author.IsEmpty() && (merged_revision > 0))
\r
61 dateA = CStringA(merged_date);
\r
62 authorA = CStringA(merged_author);
\r
63 revision = merged_revision;
\r
65 m_bHasMerges = true;
\r
68 if (pathA.Find(' ') >= 60)
\r
70 // the merge path has spaces in it:
\r
71 // TortoiseBlame can't deal with such paths if the space is after
\r
72 // the 60 char which is reserved for the path length in the blame file
\r
73 // To avoid these problems, we escape the space
\r
74 // (not the best solution, but it works)
\r
75 pathA.Replace(" ", "%20");
\r
77 if (authorA.GetLength() > 30 )
\r
78 authorA = authorA.Left(30);
\r
80 infolineA.Format("%c %6ld %6ld %-30s %-60s %-30s ", c, revision, origrev, (LPCSTR)dateA, (LPCSTR)pathA, (LPCSTR)authorA);
\r
82 infolineA.Format("%c %6ld %6ld %6ld %-30s %-60s %-30s ", c, linenumber, revision, origrev, (LPCSTR)dateA, (LPCSTR)pathA, (LPCSTR)authorA);
\r
84 fulllineA.TrimRight("\r\n");
\r
86 if (m_saveFile.m_hFile != INVALID_HANDLE_VALUE)
\r
88 m_saveFile.WriteString(infolineA);
\r
89 m_saveFile.WriteString(fulllineA);
\r
96 BOOL CBlame::Log(svn_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
98 m_progressDlg.SetProgress(m_highestrev - revision, m_highestrev);
\r
99 if (m_saveLog.m_hFile != INVALID_HANDLE_VALUE)
\r
101 CStringA msgutf8 = CUnicodeUtils::GetUTF8(message);
\r
102 int length = msgutf8.GetLength();
\r
103 m_saveLog.Write(&revision, sizeof(LONG));
\r
104 m_saveLog.Write(&length, sizeof(int));
\r
105 m_saveLog.Write((LPCSTR)msgutf8, length);
\r
110 BOOL CBlame::Cancel()
\r
112 if (m_progressDlg.HasUserCancelled())
\r
113 m_bCancelled = TRUE;
\r
114 return m_bCancelled;
\r
117 CString CBlame::BlameToTempFile(const CTSVNPath& path, SVNRev startrev, SVNRev endrev, SVNRev pegrev,
\r
118 CString& logfile, const CString& options, BOOL includemerge,
\r
119 BOOL showprogress, BOOL ignoremimetype)
\r
121 // if the user specified to use another tool to show the blames, there's no
\r
122 // need to fetch the log later: only TortoiseBlame uses those logs to give
\r
123 // the user additional information for the blame.
\r
124 BOOL extBlame = CRegDWORD(_T("Software\\TortoiseSVN\\TextBlame"), FALSE);
\r
127 m_sSavePath = CTempFiles::Instance().GetTempFilePath(false).GetWinPathString();
\r
128 if (m_sSavePath.IsEmpty())
\r
130 temp = path.GetFileExtension();
\r
131 if (!temp.IsEmpty() && !extBlame)
\r
132 m_sSavePath += temp;
\r
133 if (!m_saveFile.Open(m_sSavePath, CFile::typeText | CFile::modeReadWrite | CFile::modeCreate))
\r
136 m_bNoLineNo = false;
\r
137 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
138 m_saveFile.WriteString(headline);
\r
139 m_saveFile.WriteString(_T("\n"));
\r
140 m_progressDlg.SetTitle(IDS_BLAME_PROGRESSTITLE);
\r
141 m_progressDlg.SetAnimation(IDR_DOWNLOAD);
\r
142 m_progressDlg.SetShowProgressBar(TRUE);
\r
145 m_progressDlg.ShowModeless(CWnd::FromHandle(hWndExplorer));
\r
147 m_progressDlg.FormatNonPathLine(1, IDS_BLAME_PROGRESSINFO);
\r
148 m_progressDlg.FormatNonPathLine(2, IDS_BLAME_PROGRESSINFOSTART);
\r
149 m_progressDlg.SetCancelMsg(IDS_BLAME_PROGRESSCANCEL);
\r
150 m_progressDlg.SetTime(FALSE);
\r
151 m_nHeadRev = endrev;
\r
152 if (m_nHeadRev < 0)
\r
153 m_nHeadRev = GetHEADRevision(path);
\r
154 m_progressDlg.SetProgress(0, m_nHeadRev);
\r
156 m_bHasMerges = false;
\r
157 BOOL bBlameSuccesful = this->Blame(path, startrev, endrev, pegrev, options, !!ignoremimetype, !!includemerge);
\r
158 if ( !bBlameSuccesful && !pegrev.IsValid() )
\r
160 // retry with the end rev as peg rev
\r
161 if (this->Blame(path, startrev, endrev, endrev, options, !!ignoremimetype, !!includemerge))
\r
163 bBlameSuccesful = TRUE;
\r
167 if (!bBlameSuccesful)
\r
169 m_saveFile.Close();
\r
170 DeleteFile(m_sSavePath);
\r
171 m_sSavePath.Empty();
\r
173 else if (!extBlame)
\r
175 m_progressDlg.FormatNonPathLine(2, IDS_BLAME_PROGRESSLOGSTART);
\r
176 m_progressDlg.SetProgress(0, m_highestrev);
\r
177 logfile = CTempFiles::Instance().GetTempFilePath(false).GetWinPathString();
\r
178 if (!m_saveLog.Open(logfile, CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate))
\r
181 return m_sSavePath;
\r
183 BOOL bRet = ReceiveLog(CTSVNPathList(path), pegrev, m_nHeadRev, m_lowestrev, 0, FALSE, m_bHasMerges, false);
\r
187 DeleteFile(logfile);
\r
195 m_progressDlg.Stop();
\r
196 if (m_saveFile.m_hFile != INVALID_HANDLE_VALUE)
\r
197 m_saveFile.Close();
\r
199 return m_sSavePath;
\r
201 BOOL CBlame::Notify(const CTSVNPath& /*path*/, svn_wc_notify_action_t /*action*/,
\r
202 svn_node_kind_t /*kind*/, const CString& /*mime_type*/,
\r
203 svn_wc_notify_state_t /*content_state*/,
\r
204 svn_wc_notify_state_t /*prop_state*/, LONG rev,
\r
205 const svn_lock_t * /*lock*/, svn_wc_notify_lock_state_t /*lock_state*/,
\r
206 svn_error_t * /*err*/, apr_pool_t * /*pool*/)
\r
208 m_progressDlg.FormatNonPathLine(2, IDS_BLAME_PROGRESSINFO2, rev, m_nHeadRev);
\r
209 m_progressDlg.SetProgress(rev, m_nHeadRev);
\r
213 bool CBlame::BlameToFile(const CTSVNPath& path, SVNRev startrev, SVNRev endrev, SVNRev peg,
\r
214 const CTSVNPath& tofile, const CString& options, BOOL ignoremimetype, BOOL includemerge)
\r
217 if (!m_saveFile.Open(tofile.GetWinPathString(), CFile::typeText | CFile::modeReadWrite | CFile::modeCreate))
\r
219 m_bNoLineNo = true;
\r
220 m_nHeadRev = endrev;
\r
221 if (m_nHeadRev < 0)
\r
222 m_nHeadRev = GetHEADRevision(path);
\r
224 BOOL bBlameSuccesful = this->Blame(path, startrev, endrev, peg, options, !!ignoremimetype, !!includemerge);
\r
225 if ( !bBlameSuccesful && !peg.IsValid() )
\r
227 // retry with the end rev as peg rev
\r
228 if (this->Blame(path, startrev, endrev, endrev, options, !!ignoremimetype, !!includemerge))
\r
230 bBlameSuccesful = TRUE;
\r
235 if (!bBlameSuccesful)
\r
237 m_saveFile.Close();
\r
241 if (m_saveFile.m_hFile != INVALID_HANDLE_VALUE)
\r
242 m_saveFile.Close();
\r