1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2003-2011 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "svnpropertypage.h"
24 #include "UnicodeUtils.h"
25 #include "PathUtils.h"
26 #include "GitStatus.h"
28 #define MAX_STRING_LENGTH 4096 //should be big enough
30 // Nonmember function prototypes
31 BOOL CALLBACK
PageProc (HWND
, UINT
, WPARAM
, LPARAM
);
32 UINT CALLBACK
PropPageCallbackProc ( HWND hwnd
, UINT uMsg
, LPPROPSHEETPAGE ppsp
);
34 /////////////////////////////////////////////////////////////////////////////
35 // Dialog procedures and other callback functions
37 BOOL CALLBACK
PageProc (HWND hwnd
, UINT uMessage
, WPARAM wParam
, LPARAM lParam
)
39 CGitPropertyPage
* sheetpage
;
41 if (uMessage
== WM_INITDIALOG
)
43 sheetpage
= (CGitPropertyPage
*) ((LPPROPSHEETPAGE
) lParam
)->lParam
;
44 SetWindowLongPtr (hwnd
, GWLP_USERDATA
, (LONG_PTR
) sheetpage
);
45 sheetpage
->SetHwnd(hwnd
);
49 sheetpage
= (CGitPropertyPage
*) GetWindowLongPtr (hwnd
, GWLP_USERDATA
);
53 return sheetpage
->PageProc(hwnd
, uMessage
, wParam
, lParam
);
58 UINT CALLBACK
PropPageCallbackProc ( HWND
/*hwnd*/, UINT uMsg
, LPPROPSHEETPAGE ppsp
)
60 // Delete the page before closing.
61 if (PSPCB_RELEASE
== uMsg
)
63 CGitPropertyPage
* sheetpage
= (CGitPropertyPage
*) ppsp
->lParam
;
64 if (sheetpage
!= NULL
)
70 // *********************** CGitPropertyPage *************************
72 CGitPropertyPage::CGitPropertyPage(const std::vector
<stdstring
> &newFilenames
)
73 :filenames(newFilenames
)
77 CGitPropertyPage::~CGitPropertyPage(void)
81 void CGitPropertyPage::SetHwnd(HWND newHwnd
)
86 BOOL
CGitPropertyPage::PageProc (HWND
/*hwnd*/, UINT uMessage
, WPARAM wParam
, LPARAM lParam
)
97 LPNMHDR point
= (LPNMHDR
)lParam
;
98 int code
= point
->code
;
100 // Respond to notifications.
102 if (code
== PSN_APPLY
)
105 SetWindowLongPtr (m_hwnd
, DWLP_MSGRESULT
, FALSE
);
113 switch (HIWORD(wParam
))
116 if (LOWORD(wParam
) == IDC_SHOWLOG
)
119 PROCESS_INFORMATION process
;
120 memset(&startup
, 0, sizeof(startup
));
121 startup
.cb
= sizeof(startup
);
122 memset(&process
, 0, sizeof(process
));
123 CRegStdString
tortoiseProcPath(_T("Software\\TortoiseGit\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE
);
124 stdstring svnCmd
= _T(" /command:");
125 svnCmd
+= _T("log /path:\"");
126 svnCmd
+= filenames
.front().c_str();
128 if (CreateProcess(((stdstring
)tortoiseProcPath
).c_str(), const_cast<TCHAR
*>(svnCmd
.c_str()), NULL
, NULL
, FALSE
, 0, 0, 0, &startup
, &process
))
130 CloseHandle(process
.hThread
);
131 CloseHandle(process
.hProcess
);
134 if (LOWORD(wParam
) == IDC_EDITPROPERTIES
)
136 DWORD pathlength
= GetTempPath(0, NULL
);
137 TCHAR
* path
= new TCHAR
[pathlength
+1];
138 TCHAR
* tempFile
= new TCHAR
[pathlength
+ 100];
139 GetTempPath (pathlength
+1, path
);
140 GetTempFileName (path
, _T("svn"), 0, tempFile
);
141 stdstring retFilePath
= stdstring(tempFile
);
143 HANDLE file
= ::CreateFile (tempFile
,
148 FILE_ATTRIBUTE_TEMPORARY
,
153 if (file
!= INVALID_HANDLE_VALUE
)
156 for (std::vector
<stdstring
>::iterator I
= filenames
.begin(); I
!= filenames
.end(); ++I
)
158 ::WriteFile (file
, I
->c_str(), I
->size()*sizeof(TCHAR
), &written
, 0);
159 ::WriteFile (file
, _T("\n"), 2, &written
, 0);
164 PROCESS_INFORMATION process
;
165 memset(&startup
, 0, sizeof(startup
));
166 startup
.cb
= sizeof(startup
);
167 memset(&process
, 0, sizeof(process
));
168 CRegStdString
tortoiseProcPath(_T("Software\\TortoiseGit\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE
);
169 stdstring svnCmd
= _T(" /command:");
170 svnCmd
+= _T("properties /pathfile:\"");
171 svnCmd
+= retFilePath
.c_str();
173 svnCmd
+= _T(" /deletepathfile");
174 if (CreateProcess(((stdstring
)tortoiseProcPath
).c_str(), const_cast<TCHAR
*>(svnCmd
.c_str()), NULL
, NULL
, FALSE
, 0, 0, 0, &startup
, &process
))
176 CloseHandle(process
.hThread
);
177 CloseHandle(process
.hProcess
);
182 } // switch (HIWORD(wParam))
183 } // switch (uMessage)
186 void CGitPropertyPage::Time64ToTimeString(__time64_t time
, TCHAR
* buf
, size_t buflen
)
190 TCHAR timebuf
[MAX_STRING_LENGTH
];
191 TCHAR datebuf
[MAX_STRING_LENGTH
];
193 LCID locale
= (WORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
));
194 locale
= MAKELCID(locale
, SORT_DEFAULT
);
199 _localtime64_s(&newtime
, &time
);
201 systime
.wDay
= (WORD
)newtime
.tm_mday
;
202 systime
.wDayOfWeek
= (WORD
)newtime
.tm_wday
;
203 systime
.wHour
= (WORD
)newtime
.tm_hour
;
204 systime
.wMilliseconds
= 0;
205 systime
.wMinute
= (WORD
)newtime
.tm_min
;
206 systime
.wMonth
= (WORD
)newtime
.tm_mon
+1;
207 systime
.wSecond
= (WORD
)newtime
.tm_sec
;
208 systime
.wYear
= (WORD
)newtime
.tm_year
+1900;
209 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
210 GetDateFormat(locale
, DATE_SHORTDATE
, &systime
, NULL
, datebuf
, MAX_STRING_LENGTH
);
212 GetDateFormat(locale
, DATE_LONGDATE
, &systime
, NULL
, datebuf
, MAX_STRING_LENGTH
);
213 GetTimeFormat(locale
, 0, &systime
, NULL
, timebuf
, MAX_STRING_LENGTH
);
215 _tcsncat_s(buf
, buflen
, timebuf
, MAX_STRING_LENGTH
-1);
216 _tcsncat_s(buf
, buflen
, _T(", "), MAX_STRING_LENGTH
-1);
217 _tcsncat_s(buf
, buflen
, datebuf
, MAX_STRING_LENGTH
-1);
221 void CGitPropertyPage::InitWorkfileView()
225 if( filenames
.size() ==0)
228 CTGitPath
path(filenames
.front().c_str());
229 CString ProjectTopDir
;
231 if(!path
.HasAdminDir(&ProjectTopDir
))
234 git
.SetCurrentDir(ProjectTopDir
);
235 //can't git.exe when create process
236 git
.Run(_T("git.exe config user.name"),&username
,CP_ACP
);
238 git
.Run(_T("git.exe config user.email"),&useremail
,CP_ACP
);
240 git
.Run(_T("git.exe config core.autocrlf"),&autocrlf
,CP_ACP
);
242 git
.Run(_T("git.exe config core.safecrlf"),&safecrlf
,CP_ACP
);
246 CString remotebranch
;
248 git
.Run(_T("git.exe symbolic-ref HEAD"),&branch
,CP_ACP
);
251 if(!branch
.IsEmpty())
253 if( branch
.Find(_T("refs/heads/"),0) >=0 )
256 branch
=branch
.Mid(11);
258 branch
=branch
.Tokenize(_T("\n"),start
);
260 branch
=branch
.Tokenize(_T("\r"),start
);
261 cmd
.Format(_T("git.exe config branch.%s.merge"),branch
);
262 git
.Run(cmd
,&remotebranch
,CP_ACP
);
263 cmd
.Format(_T("git.exe config branch.%s.remote"),branch
);
264 git
.Run(cmd
,&remote
,CP_ACP
);
265 if((!remote
.IsEmpty()) && (!remotebranch
.IsEmpty()))
267 remotebranch
=remotebranch
.Mid(11);
268 remotebranch
=remote
+_T("/")+remotebranch
;
273 TCHAR oldpath
[MAX_PATH
+1];
275 ::GetCurrentDirectory(MAX_PATH
, oldpath
);
277 ::SetCurrentDirectory(ProjectTopDir
);
281 if (autocrlf
.Trim().IsEmpty())
282 autocrlf
= _T("false");
283 if (safecrlf
.Trim().IsEmpty())
284 safecrlf
= _T("false");
286 SetDlgItemText(m_hwnd
,IDC_CONFIG_USERNAME
,username
.Trim());
287 SetDlgItemText(m_hwnd
,IDC_CONFIG_USEREMAIL
,useremail
.Trim());
288 SetDlgItemText(m_hwnd
,IDC_CONFIG_AUTOCRLF
,autocrlf
.Trim());
289 SetDlgItemText(m_hwnd
,IDC_CONFIG_SAFECRLF
,safecrlf
.Trim());
291 SetDlgItemText(m_hwnd
,IDC_SHELL_CURRENT_BRANCH
,branch
.Trim());
292 remotebranch
.Trim().Replace(_T("\n"), _T("; "));
293 SetDlgItemText(m_hwnd
,IDC_SHELL_REMOTE_BRANCH
, remotebranch
);
297 AutoLocker
lock(g_Git
.m_critGitDllSec
);
298 g_Git
.CheckAndInitDll();
299 rev
.GetCommit(CString(_T("HEAD")));
301 SetDlgItemText(m_hwnd
,IDC_HEAD_HASH
,rev
.m_CommitHash
.ToString());
302 SetDlgItemText(m_hwnd
,IDC_HEAD_SUBJECT
,rev
.GetSubject());
303 SetDlgItemText(m_hwnd
,IDC_HEAD_AUTHOR
,rev
.GetAuthorName());
304 SetDlgItemText(m_hwnd
,IDC_HEAD_DATE
,rev
.GetAuthorDate().Format(_T("%Y-%m-%d %H:%M:%S")));
306 if (filenames
.size() == 1)
308 CTGitPath
path(filenames
.front().c_str());
309 CTGitPath relatepath
;
310 CString strpath
=path
.GetWinPathString();
311 CString ProjectTopDir
;
313 if(!path
.HasAdminDir(&ProjectTopDir
))
316 if(ProjectTopDir
[ProjectTopDir
.GetLength()-1] == _T('\\'))
318 relatepath
.SetFromWin( strpath
.Right(strpath
.GetLength()-ProjectTopDir
.GetLength()));
322 relatepath
.SetFromWin( strpath
.Right(strpath
.GetLength()-ProjectTopDir
.GetLength()-1));
326 if(! relatepath
.GetGitPathString().IsEmpty())
328 cmd
=_T("-z --topo-order -n1 --parents -- \"");
329 cmd
+=relatepath
.GetGitPathString();
335 if(git_open_log(&handle
, CUnicodeUtils::GetUTF8(cmd
).GetBuffer()))
337 if(git_get_log_firstcommit(handle
))
341 if(git_get_log_nextcommit(handle
,&commit
))
344 git_close_log(handle
);
346 rev
.ParserFromCommit(&commit
);
347 git_free_commit(&commit
);
350 if (handle
!= NULL
) {
351 git_close_log(handle
);
355 SetDlgItemText(m_hwnd
,IDC_LAST_HASH
,rev
.m_CommitHash
.ToString());
356 SetDlgItemText(m_hwnd
,IDC_LAST_SUBJECT
,rev
.GetSubject());
357 SetDlgItemText(m_hwnd
,IDC_LAST_AUTHOR
,rev
.GetAuthorName());
358 SetDlgItemText(m_hwnd
,IDC_LAST_DATE
,rev
.GetAuthorDate().Format(_T("%Y-%m-%d %H:%M:%S")));
364 ::SetCurrentDirectory(oldpath
);
369 // CShellExt member functions (needed for IShellPropSheetExt)
370 STDMETHODIMP
CShellExt::AddPages (LPFNADDPROPSHEETPAGE lpfnAddPage
,
374 CString ProjectTopDir
;
376 for (std::vector
<stdstring
>::iterator I
= files_
.begin(); I
!= files_
.end(); ++I
)
379 GitStatus svn = GitStatus();
380 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
381 return NOERROR; // file/directory not under version control
383 if (svn.status->entry == NULL)
386 if( CTGitPath(I
->c_str()).HasAdminDir(&ProjectTopDir
))
392 if (files_
.size() == 0)
397 SecureZeroMemory(&psp
, sizeof(PROPSHEETPAGE
));
398 HPROPSHEETPAGE hPage
;
399 CGitPropertyPage
*sheetpage
= new CGitPropertyPage(files_
);
401 psp
.dwSize
= sizeof (psp
);
402 psp
.dwFlags
= PSP_USEREFPARENT
| PSP_USETITLE
| PSP_USEICONID
| PSP_USECALLBACK
;
403 psp
.hInstance
= g_hResInst
;
404 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_PROPPAGE
);
405 psp
.pszIcon
= MAKEINTRESOURCE(IDI_APPSMALL
);
406 psp
.pszTitle
= _T("Git");
407 psp
.pfnDlgProc
= (DLGPROC
) PageProc
;
408 psp
.lParam
= (LPARAM
) sheetpage
;
409 psp
.pfnCallback
= PropPageCallbackProc
;
410 psp
.pcRefParent
= &g_cRefThisDll
;
412 hPage
= CreatePropertySheetPage (&psp
);
416 if (!lpfnAddPage (hPage
, lParam
))
419 DestroyPropertySheetPage (hPage
);
426 STDMETHODIMP
CShellExt::ReplacePage (UINT
/*uPageID*/, LPFNADDPROPSHEETPAGE
/*lpfnReplaceWith*/, LPARAM
/*lParam*/)