OSDN Git Service

Add TortoiseUDiff Code
[tortoisegit/TortoiseGitJp.git] / TortoiseProc / Blame.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseSVN\r
4 \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
9 \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
14 \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
18 //\r
19 #include "stdafx.h"\r
20 #include "TortoiseProc.h"\r
21 #include "Blame.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
27 \r
28 CBlame::CBlame()\r
29 {\r
30         m_bCancelled = FALSE;\r
31         m_lowestrev = -1;\r
32         m_highestrev = -1;\r
33         m_nCounter = 0;\r
34         m_nHeadRev = -1;\r
35         m_bNoLineNo = false;\r
36 }\r
37 CBlame::~CBlame()\r
38 {\r
39         m_progressDlg.Stop();\r
40 }\r
41 \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
45 {\r
46         CStringA infolineA;\r
47         CStringA fulllineA;\r
48         svn_revnum_t origrev = revision;\r
49 \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
54 \r
55         CStringA dateA(date);\r
56         CStringA authorA(author);\r
57         CStringA pathA(merged_path);\r
58         TCHAR c = ' ';\r
59         if (!merged_author.IsEmpty() && (merged_revision > 0))\r
60         {\r
61                 dateA = CStringA(merged_date);\r
62                 authorA = CStringA(merged_author);\r
63                 revision = merged_revision;\r
64                 c = 'G';\r
65                 m_bHasMerges = true;\r
66         }\r
67 \r
68         if (pathA.Find(' ') >= 60)\r
69         {\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
76         }\r
77         if (authorA.GetLength() > 30 )\r
78                 authorA = authorA.Left(30);\r
79         if (m_bNoLineNo)\r
80                 infolineA.Format("%c %6ld %6ld %-30s %-60s %-30s ", c, revision, origrev, (LPCSTR)dateA, (LPCSTR)pathA, (LPCSTR)authorA);\r
81         else\r
82                 infolineA.Format("%c %6ld %6ld %6ld %-30s %-60s %-30s ", c, linenumber, revision, origrev, (LPCSTR)dateA, (LPCSTR)pathA, (LPCSTR)authorA);\r
83         fulllineA = line;\r
84         fulllineA.TrimRight("\r\n");\r
85         fulllineA += "\n";\r
86         if (m_saveFile.m_hFile != INVALID_HANDLE_VALUE)\r
87         {\r
88                 m_saveFile.WriteString(infolineA);\r
89                 m_saveFile.WriteString(fulllineA);\r
90         }\r
91         else\r
92                 return FALSE;\r
93         return TRUE;\r
94 }\r
95 \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
97 {\r
98         m_progressDlg.SetProgress(m_highestrev - revision, m_highestrev);\r
99         if (m_saveLog.m_hFile != INVALID_HANDLE_VALUE)\r
100         {\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
106         }\r
107         return TRUE;\r
108 }\r
109 \r
110 BOOL CBlame::Cancel()\r
111 {\r
112         if (m_progressDlg.HasUserCancelled())\r
113                 m_bCancelled = TRUE;\r
114         return m_bCancelled;\r
115 }\r
116 \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
120 {\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
125 \r
126         CString temp;\r
127         m_sSavePath = CTempFiles::Instance().GetTempFilePath(false).GetWinPathString();\r
128         if (m_sSavePath.IsEmpty())\r
129                 return _T("");\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
134                 return _T("");\r
135         CString headline;\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
143         if (showprogress)\r
144         {\r
145                 m_progressDlg.ShowModeless(CWnd::FromHandle(hWndExplorer));\r
146         }\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
155 \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
159         {\r
160                 // retry with the end rev as peg rev\r
161                 if (this->Blame(path, startrev, endrev, endrev, options, !!ignoremimetype, !!includemerge))\r
162                 {\r
163                         bBlameSuccesful = TRUE;\r
164                         pegrev = endrev;\r
165                 }\r
166         }\r
167         if (!bBlameSuccesful)\r
168         {\r
169                 m_saveFile.Close();\r
170                 DeleteFile(m_sSavePath);\r
171                 m_sSavePath.Empty();\r
172         }\r
173         else if (!extBlame)\r
174         {\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
179                 {\r
180                         logfile.Empty();\r
181                         return m_sSavePath;\r
182                 }\r
183                 BOOL bRet = ReceiveLog(CTSVNPathList(path), pegrev, m_nHeadRev, m_lowestrev, 0, FALSE, m_bHasMerges, false);\r
184                 if (!bRet)\r
185                 {\r
186                         m_saveLog.Close();\r
187                         DeleteFile(logfile);\r
188                         logfile.Empty();\r
189                 }\r
190                 else\r
191                 {\r
192                         m_saveLog.Close();\r
193                 }\r
194         }\r
195         m_progressDlg.Stop();\r
196         if (m_saveFile.m_hFile != INVALID_HANDLE_VALUE)\r
197                 m_saveFile.Close();\r
198 \r
199         return m_sSavePath;\r
200 }\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
207 {\r
208         m_progressDlg.FormatNonPathLine(2, IDS_BLAME_PROGRESSINFO2, rev, m_nHeadRev);\r
209         m_progressDlg.SetProgress(rev, m_nHeadRev);\r
210         return TRUE;\r
211 }\r
212 \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
215 {\r
216         CString temp;\r
217         if (!m_saveFile.Open(tofile.GetWinPathString(), CFile::typeText | CFile::modeReadWrite | CFile::modeCreate))\r
218                 return false;\r
219         m_bNoLineNo = true;\r
220         m_nHeadRev = endrev;\r
221         if (m_nHeadRev < 0)\r
222                 m_nHeadRev = GetHEADRevision(path);\r
223 \r
224         BOOL bBlameSuccesful = this->Blame(path, startrev, endrev, peg, options, !!ignoremimetype, !!includemerge);\r
225         if ( !bBlameSuccesful && !peg.IsValid() )\r
226         {\r
227                 // retry with the end rev as peg rev\r
228                 if (this->Blame(path, startrev, endrev, endrev, options, !!ignoremimetype, !!includemerge))\r
229                 {\r
230                         bBlameSuccesful = TRUE;\r
231                         peg = endrev;\r
232                 }\r
233         }\r
234 \r
235         if (!bBlameSuccesful)\r
236         {\r
237                 m_saveFile.Close();\r
238                 return false;\r
239         }\r
240 \r
241         if (m_saveFile.m_hFile != INVALID_HANDLE_VALUE)\r
242                 m_saveFile.Close();\r
243 \r
244         return true;\r
245 }\r