1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2008-2012 - 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 "gitpropertypage.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 PageProcOnCommand(wParam
);
115 } // switch (uMessage)
118 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam
)
120 if(HIWORD(wParam
) != BN_CLICKED
)
123 if (LOWORD(wParam
) == IDC_SHOWLOG
)
126 PROCESS_INFORMATION process
;
127 memset(&startup
, 0, sizeof(startup
));
128 startup
.cb
= sizeof(startup
);
129 memset(&process
, 0, sizeof(process
));
130 CRegStdString
tortoiseProcPath(_T("Software\\TortoiseGit\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE
);
131 stdstring gitCmd
= _T(" /command:");
132 gitCmd
+= _T("log /path:\"");
133 gitCmd
+= filenames
.front().c_str();
135 if (CreateProcess(((stdstring
)tortoiseProcPath
).c_str(), const_cast<TCHAR
*>(gitCmd
.c_str()), NULL
, NULL
, FALSE
, 0, 0, 0, &startup
, &process
))
137 CloseHandle(process
.hThread
);
138 CloseHandle(process
.hProcess
);
142 void CGitPropertyPage::Time64ToTimeString(__time64_t time
, TCHAR
* buf
, size_t buflen
)
146 TCHAR timebuf
[MAX_STRING_LENGTH
];
147 TCHAR datebuf
[MAX_STRING_LENGTH
];
149 LCID locale
= (WORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
));
150 locale
= MAKELCID(locale
, SORT_DEFAULT
);
155 _localtime64_s(&newtime
, &time
);
157 systime
.wDay
= (WORD
)newtime
.tm_mday
;
158 systime
.wDayOfWeek
= (WORD
)newtime
.tm_wday
;
159 systime
.wHour
= (WORD
)newtime
.tm_hour
;
160 systime
.wMilliseconds
= 0;
161 systime
.wMinute
= (WORD
)newtime
.tm_min
;
162 systime
.wMonth
= (WORD
)newtime
.tm_mon
+1;
163 systime
.wSecond
= (WORD
)newtime
.tm_sec
;
164 systime
.wYear
= (WORD
)newtime
.tm_year
+1900;
165 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
166 GetDateFormat(locale
, DATE_SHORTDATE
, &systime
, NULL
, datebuf
, MAX_STRING_LENGTH
);
168 GetDateFormat(locale
, DATE_LONGDATE
, &systime
, NULL
, datebuf
, MAX_STRING_LENGTH
);
169 GetTimeFormat(locale
, 0, &systime
, NULL
, timebuf
, MAX_STRING_LENGTH
);
171 _tcsncat_s(buf
, buflen
, timebuf
, MAX_STRING_LENGTH
-1);
172 _tcsncat_s(buf
, buflen
, _T(", "), MAX_STRING_LENGTH
-1);
173 _tcsncat_s(buf
, buflen
, datebuf
, MAX_STRING_LENGTH
-1);
177 void CGitPropertyPage::InitWorkfileView()
181 if( filenames
.size() ==0)
184 CTGitPath
path(filenames
.front().c_str());
185 CString ProjectTopDir
;
187 if(!path
.HasAdminDir(&ProjectTopDir
))
190 git
.SetCurrentDir(ProjectTopDir
);
191 //can't git.exe when create process
192 git
.Run(_T("git.exe config user.name"),&username
,CP_ACP
);
194 git
.Run(_T("git.exe config user.email"),&useremail
,CP_ACP
);
196 git
.Run(_T("git.exe config core.autocrlf"),&autocrlf
,CP_ACP
);
198 git
.Run(_T("git.exe config core.safecrlf"),&safecrlf
,CP_ACP
);
202 CString remotebranch
;
204 git
.Run(_T("git.exe symbolic-ref HEAD"),&branch
,CP_ACP
);
207 if(!branch
.IsEmpty())
209 if( branch
.Find(_T("refs/heads/"),0) >=0 )
212 branch
=branch
.Mid(11);
214 branch
=branch
.Tokenize(_T("\n"),start
);
216 branch
=branch
.Tokenize(_T("\r"),start
);
217 cmd
.Format(_T("git.exe config branch.%s.merge"),branch
);
218 git
.Run(cmd
,&remotebranch
,CP_ACP
);
219 cmd
.Format(_T("git.exe config branch.%s.remote"),branch
);
220 git
.Run(cmd
,&remote
,CP_ACP
);
221 if((!remote
.IsEmpty()) && (!remotebranch
.IsEmpty()))
223 remotebranch
=remotebranch
.Mid(11);
224 remotebranch
=remote
+_T("/")+remotebranch
;
229 TCHAR oldpath
[MAX_PATH
+1];
231 ::GetCurrentDirectory(MAX_PATH
, oldpath
);
233 ::SetCurrentDirectory(ProjectTopDir
);
237 if (autocrlf
.Trim().IsEmpty())
238 autocrlf
= _T("false");
239 if (safecrlf
.Trim().IsEmpty())
240 safecrlf
= _T("false");
242 SetDlgItemText(m_hwnd
,IDC_CONFIG_USERNAME
,username
.Trim());
243 SetDlgItemText(m_hwnd
,IDC_CONFIG_USEREMAIL
,useremail
.Trim());
244 SetDlgItemText(m_hwnd
,IDC_CONFIG_AUTOCRLF
,autocrlf
.Trim());
245 SetDlgItemText(m_hwnd
,IDC_CONFIG_SAFECRLF
,safecrlf
.Trim());
247 SetDlgItemText(m_hwnd
,IDC_SHELL_CURRENT_BRANCH
,branch
.Trim());
248 remotebranch
.Trim().Replace(_T("\n"), _T("; "));
249 SetDlgItemText(m_hwnd
,IDC_SHELL_REMOTE_BRANCH
, remotebranch
);
253 AutoLocker
lock(g_Git
.m_critGitDllSec
);
254 g_Git
.CheckAndInitDll();
255 rev
.GetCommit(CString(_T("HEAD")));
257 SetDlgItemText(m_hwnd
,IDC_HEAD_HASH
,rev
.m_CommitHash
.ToString());
258 SetDlgItemText(m_hwnd
,IDC_HEAD_SUBJECT
,rev
.GetSubject());
259 SetDlgItemText(m_hwnd
,IDC_HEAD_AUTHOR
,rev
.GetAuthorName());
260 SetDlgItemText(m_hwnd
,IDC_HEAD_DATE
,rev
.GetAuthorDate().Format(_T("%Y-%m-%d %H:%M:%S")));
262 if (filenames
.size() == 1)
264 CTGitPath
path(filenames
.front().c_str());
265 CTGitPath relatepath
;
266 CString strpath
=path
.GetWinPathString();
267 CString ProjectTopDir
;
269 if(!path
.HasAdminDir(&ProjectTopDir
))
272 if(ProjectTopDir
[ProjectTopDir
.GetLength()-1] == _T('\\'))
274 relatepath
.SetFromWin( strpath
.Right(strpath
.GetLength()-ProjectTopDir
.GetLength()));
278 relatepath
.SetFromWin( strpath
.Right(strpath
.GetLength()-ProjectTopDir
.GetLength()-1));
282 if(! relatepath
.GetGitPathString().IsEmpty())
284 cmd
=_T("-z --topo-order -n1 --parents -- \"");
285 cmd
+=relatepath
.GetGitPathString();
291 if(git_open_log(&handle
, CUnicodeUtils::GetUTF8(cmd
).GetBuffer()))
293 if(git_get_log_firstcommit(handle
))
297 if (git_get_log_nextcommit(handle
, &commit
, 0))
300 git_close_log(handle
);
302 rev
.ParserFromCommit(&commit
);
303 git_free_commit(&commit
);
306 if (handle
!= NULL
) {
307 git_close_log(handle
);
311 SetDlgItemText(m_hwnd
,IDC_LAST_HASH
,rev
.m_CommitHash
.ToString());
312 SetDlgItemText(m_hwnd
,IDC_LAST_SUBJECT
,rev
.GetSubject());
313 SetDlgItemText(m_hwnd
,IDC_LAST_AUTHOR
,rev
.GetAuthorName());
314 SetDlgItemText(m_hwnd
,IDC_LAST_DATE
,rev
.GetAuthorDate().Format(_T("%Y-%m-%d %H:%M:%S")));
320 ::SetCurrentDirectory(oldpath
);
325 // CShellExt member functions (needed for IShellPropSheetExt)
326 STDMETHODIMP
CShellExt::AddPages (LPFNADDPROPSHEETPAGE lpfnAddPage
,
330 CString ProjectTopDir
;
332 for (std::vector
<stdstring
>::iterator I
= files_
.begin(); I
!= files_
.end(); ++I
)
335 GitStatus svn = GitStatus();
336 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
337 return S_OK; // file/directory not under version control
339 if (svn.status->entry == NULL)
342 if( CTGitPath(I
->c_str()).HasAdminDir(&ProjectTopDir
))
348 if (files_
.size() == 0)
353 SecureZeroMemory(&psp
, sizeof(PROPSHEETPAGE
));
354 HPROPSHEETPAGE hPage
;
355 CGitPropertyPage
*sheetpage
= new CGitPropertyPage(files_
);
357 psp
.dwSize
= sizeof (psp
);
358 psp
.dwFlags
= PSP_USEREFPARENT
| PSP_USETITLE
| PSP_USEICONID
| PSP_USECALLBACK
;
359 psp
.hInstance
= g_hResInst
;
360 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_PROPPAGE
);
361 psp
.pszIcon
= MAKEINTRESOURCE(IDI_APPSMALL
);
362 psp
.pszTitle
= _T("Git");
363 psp
.pfnDlgProc
= (DLGPROC
) PageProc
;
364 psp
.lParam
= (LPARAM
) sheetpage
;
365 psp
.pfnCallback
= PropPageCallbackProc
;
366 psp
.pcRefParent
= &g_cRefThisDll
;
368 hPage
= CreatePropertySheetPage (&psp
);
372 if (!lpfnAddPage (hPage
, lParam
))
375 DestroyPropertySheetPage (hPage
);
382 STDMETHODIMP
CShellExt::ReplacePage (UINT
/*uPageID*/, LPFNADDPROPSHEETPAGE
/*lpfnReplaceWith*/, LPARAM
/*lParam*/)