Estimate the required stack size for getting latest change using libgit2
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blob00af10052f4397f5d45625b2ff9a77d2f6f5b901
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2008-2013 - 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.
20 #include "stdafx.h"
22 #include "ShellExt.h"
23 #include "gitpropertypage.h"
24 #include "UnicodeUtils.h"
25 #include "PathUtils.h"
26 #include "UnicodeUtils.h"
27 #include "CreateProcessHelper.h"
28 #include "FormatMessageWrapper.h"
29 #include "DirFileEnum.h"
31 #define MAX_STRING_LENGTH 4096 //should be big enough
33 // Nonmember function prototypes
34 BOOL CALLBACK PageProc (HWND, UINT, WPARAM, LPARAM);
35 UINT CALLBACK PropPageCallbackProc ( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp );
37 /////////////////////////////////////////////////////////////////////////////
38 // Dialog procedures and other callback functions
40 BOOL CALLBACK PageProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
42 CGitPropertyPage * sheetpage;
44 if (uMessage == WM_INITDIALOG)
46 sheetpage = (CGitPropertyPage*) ((LPPROPSHEETPAGE) lParam)->lParam;
47 SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) sheetpage);
48 sheetpage->SetHwnd(hwnd);
50 else
52 sheetpage = (CGitPropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
55 if (sheetpage != 0L)
56 return sheetpage->PageProc(hwnd, uMessage, wParam, lParam);
57 else
58 return FALSE;
61 UINT CALLBACK PropPageCallbackProc ( HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp )
63 // Delete the page before closing.
64 if (PSPCB_RELEASE == uMsg)
66 CGitPropertyPage* sheetpage = (CGitPropertyPage*) ppsp->lParam;
67 if (sheetpage != NULL)
68 delete sheetpage;
70 return 1;
73 // *********************** CGitPropertyPage *************************
74 const UINT CGitPropertyPage::m_UpdateLastCommit = RegisterWindowMessage(_T("TORTOISEGIT_PROP_UPDATELASTCOMMIT"));
76 CGitPropertyPage::CGitPropertyPage(const std::vector<stdstring> &newFilenames)
77 :filenames(newFilenames)
78 ,m_bChanged(false)
79 , m_hwnd(NULL)
83 CGitPropertyPage::~CGitPropertyPage(void)
87 void CGitPropertyPage::SetHwnd(HWND newHwnd)
89 m_hwnd = newHwnd;
92 BOOL CGitPropertyPage::PageProc (HWND /*hwnd*/, UINT uMessage, WPARAM wParam, LPARAM lParam)
94 switch (uMessage)
96 case WM_INITDIALOG:
98 InitWorkfileView();
99 return TRUE;
101 case WM_NOTIFY:
103 LPNMHDR point = (LPNMHDR)lParam;
104 int code = point->code;
106 // Respond to notifications.
108 if (code == PSN_APPLY && m_bChanged)
112 CTGitPath path(filenames.front().c_str());
113 CString projectTopDir;
114 if(!path.HasAdminDir(&projectTopDir) || path.IsDirectory())
115 break;
117 int stripLength = projectTopDir.GetLength();
118 if (projectTopDir[stripLength - 1] != _T('\\'))
119 stripLength++;
121 CStringA gitdir = CUnicodeUtils::GetMulti(projectTopDir, CP_UTF8);
122 git_repository *repository = NULL;
123 git_index *index = NULL;
125 int ret = git_repository_open(&repository, gitdir.GetBuffer());
126 gitdir.ReleaseBuffer();
127 if (ret)
128 break;
130 if (git_repository_index(&index, repository))
132 git_repository_free(repository);
133 break;
136 BOOL assumeValid = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_GETCHECK, 0, 0);
137 BOOL skipWorktree = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_GETCHECK, 0, 0);
138 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
140 bool changed = false;
142 for (auto it = filenames.cbegin(); it < filenames.cend(); it++)
144 CTGitPath file;
145 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
146 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
147 int idx = git_index_find(index, pathA);
148 if (idx >= 0)
150 git_index_entry *e = const_cast<git_index_entry *>(git_index_get_byindex(index, idx)); // HACK
152 if (assumeValid == BST_CHECKED)
154 if (!(e->flags & GIT_IDXENTRY_VALID))
156 e->flags |= GIT_IDXENTRY_VALID;
157 changed = true;
160 else if (assumeValid != BST_INDETERMINATE)
162 if (e->flags & GIT_IDXENTRY_VALID)
164 e->flags &= ~GIT_IDXENTRY_VALID;
165 changed = true;
168 if (skipWorktree == BST_CHECKED)
170 if (!(e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE))
172 e->flags_extended |= GIT_IDXENTRY_SKIP_WORKTREE;
173 changed = true;
176 else if (skipWorktree != BST_INDETERMINATE)
178 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
180 e->flags_extended &= ~GIT_IDXENTRY_SKIP_WORKTREE;
181 changed = true;
184 if (executable == BST_CHECKED)
186 if (!(e->mode & 0111))
188 e->mode |= 0111;
189 changed = true;
192 else if (executable != BST_INDETERMINATE)
194 if (e->mode & 0111)
196 e->mode &= ~0111;
197 changed = true;
200 if (changed)
201 git_index_add(index, e);
205 if (changed)
207 if (!git_index_write(index))
208 m_bChanged = false;
211 git_index_free(index);
212 } while (0);
214 SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE);
215 return TRUE;
217 case WM_DESTROY:
218 return TRUE;
220 case WM_COMMAND:
221 PageProcOnCommand(wParam);
222 break;
223 } // switch (uMessage)
225 if (uMessage == m_UpdateLastCommit)
227 DisplayCommit((git_commit *)lParam, IDC_LAST_HASH, IDC_LAST_SUBJECT, IDC_LAST_AUTHOR, IDC_LAST_DATE);
228 return TRUE;
231 return FALSE;
233 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam)
235 if(HIWORD(wParam) != BN_CLICKED)
236 return;
238 switch (LOWORD(wParam))
240 case IDC_SHOWLOG:
242 tstring gitCmd = _T(" /command:");
243 gitCmd += _T("log /path:\"");
244 gitCmd += filenames.front().c_str();
245 gitCmd += _T("\"");
246 RunCommand(gitCmd);
248 break;
249 case IDC_SHOWSETTINGS:
251 CTGitPath path(filenames.front().c_str());
252 CString projectTopDir;
253 if(!path.HasAdminDir(&projectTopDir))
254 return;
256 tstring gitCmd = _T(" /command:");
257 gitCmd += _T("settings /path:\"");
258 gitCmd += projectTopDir;
259 gitCmd += _T("\"");
260 RunCommand(gitCmd);
262 break;
263 case IDC_ASSUMEVALID:
264 case IDC_SKIPWORKTREE:
265 case IDC_EXECUTABLE:
266 m_bChanged = true;
267 SendMessage(GetParent(m_hwnd), PSM_CHANGED, (WPARAM)m_hwnd, 0);
268 break;
272 void CGitPropertyPage::RunCommand(const tstring& command)
274 tstring tortoiseProcPath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TortoiseGitProc.exe");
275 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str())))
277 // process started - exit
278 return;
281 MessageBox(NULL, CFormatMessageWrapper(), _T("TortoiseProc launch failed"), MB_OK | MB_ICONINFORMATION);
284 void CGitPropertyPage::Time64ToTimeString(__time64_t time, TCHAR * buf, size_t buflen)
286 struct tm newtime;
287 SYSTEMTIME systime;
288 TCHAR timebuf[MAX_STRING_LENGTH];
289 TCHAR datebuf[MAX_STRING_LENGTH];
291 LCID locale = (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
292 locale = MAKELCID(locale, SORT_DEFAULT);
294 *buf = '\0';
295 if (time)
297 _localtime64_s(&newtime, &time);
299 systime.wDay = (WORD)newtime.tm_mday;
300 systime.wDayOfWeek = (WORD)newtime.tm_wday;
301 systime.wHour = (WORD)newtime.tm_hour;
302 systime.wMilliseconds = 0;
303 systime.wMinute = (WORD)newtime.tm_min;
304 systime.wMonth = (WORD)newtime.tm_mon+1;
305 systime.wSecond = (WORD)newtime.tm_sec;
306 systime.wYear = (WORD)newtime.tm_year+1900;
307 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
308 GetDateFormat(locale, DATE_SHORTDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
309 else
310 GetDateFormat(locale, DATE_LONGDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
311 GetTimeFormat(locale, 0, &systime, NULL, timebuf, MAX_STRING_LENGTH);
312 *buf = '\0';
313 _tcsncat_s(buf, buflen, timebuf, MAX_STRING_LENGTH-1);
314 _tcsncat_s(buf, buflen, _T(", "), MAX_STRING_LENGTH-1);
315 _tcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH-1);
319 struct TreewalkStruct
321 const char *folder;
322 const char *name;
323 git_oid oid;
326 static int TreewalkCB_FindFileRecentCommit(const char *root, const git_tree_entry *entry, void *payload)
328 TreewalkStruct *treewalkstruct = (TreewalkStruct *)payload;
329 char folder[MAX_PATH];
330 strcpy(folder, root);
331 strcat(folder, git_tree_entry_name(entry));
332 strcat(folder, "/");
333 if (strstr(treewalkstruct->folder, folder))
334 return 0;
336 if (!strcmp(treewalkstruct->folder, root))
338 if (!strcmp(git_tree_entry_name(entry), treewalkstruct->name))
340 git_oid_cpy(&treewalkstruct->oid, git_tree_entry_id(entry));
341 return -1;
344 return 1;
347 return 1;
350 static git_commit * FindFileRecentCommit(git_repository *repository, CString path)
352 git_commit *commit = NULL, *commit2 = NULL;
353 git_revwalk *walk;
354 if (git_revwalk_new(&walk, repository))
355 return NULL;
357 CStringA pathA = CUnicodeUtils::GetUTF8(path);
358 const char *pathC = pathA.GetBuffer();
359 char folder[MAX_PATH], file[MAX_PATH];
360 const char *slash = strrchr(pathC, '/');
361 if (slash)
363 strncpy(folder, pathC, slash - pathC + 1);
364 folder[slash - pathC + 1] = '\0';
365 strcpy(file, slash + 1);
367 else
369 folder[0] = '\0';
370 strcpy(file, pathC);
372 pathA.ReleaseBuffer();
374 TreewalkStruct treewalkstruct = { folder, file };
375 TreewalkStruct treewalkstruct2 = { folder, file };
379 if (git_revwalk_push_head(walk))
380 throw "git_revwalk_push_head";
382 git_oid oid;
383 while (!git_revwalk_next(&oid, walk))
385 if (commit != NULL)
387 git_commit_free(commit);
388 commit = NULL;
391 if (git_commit_lookup(&commit, repository, &oid))
393 commit = NULL;
394 throw "git_commit_lookup 1";
397 git_tree *tree;
398 if (git_commit_tree(&tree, commit))
399 throw "git_commit_tree 1";
401 memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
402 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);
403 git_tree_free(tree);
404 if (ret < 0 && ret != GIT_EUSER)
405 throw "git_tree_walk 1";
407 // check if file not found
408 if (git_oid_iszero(&treewalkstruct.oid))
410 git_commit_free(commit);
411 commit = NULL;
412 break;
415 bool diff = true;
416 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
417 // if no parent then of course it is different
418 for (unsigned int i = 0; i < git_commit_parentcount(commit); i++)
420 if (git_commit_parent(&commit2, commit, i))
422 commit2 = NULL;
423 throw "git_commit_parent";
426 if (git_commit_tree(&tree, commit2))
427 throw "git_commit_tree 2";
429 git_commit_free(commit2);
430 memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
431 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);
432 git_tree_free(tree);
433 if (ret < 0 && ret != GIT_EUSER)
434 throw "git_tree_walk 2";
436 if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
437 diff = false;
438 else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
439 throw "git_revwalk_hide";
442 if (diff)
443 break;
446 catch (...)
448 if (commit != NULL)
450 git_commit_free(commit);
451 commit = NULL;
453 if (commit2 != NULL)
455 git_commit_free(commit2);
456 commit2 = NULL;
460 git_revwalk_free(walk);
461 return commit;
464 void CGitPropertyPage::DisplayCommit(git_commit * commit, UINT hashLabel, UINT subjectLabel, UINT authorLabel, UINT dateLabel)
466 if (commit == NULL)
468 SetDlgItemText(m_hwnd, hashLabel, _T(""));
469 SetDlgItemText(m_hwnd, subjectLabel, _T(""));
470 SetDlgItemText(m_hwnd, authorLabel, _T(""));
471 SetDlgItemText(m_hwnd, dateLabel, _T(""));
472 return;
475 int encode = CP_UTF8;
476 const char * encodingString = git_commit_message_encoding(commit);
477 if (encodingString != NULL)
479 CString str;
480 g_Git.StringAppend(&str, (BYTE*)encodingString, CP_UTF8);
481 encode = CUnicodeUtils::GetCPCode(str);
484 const git_signature * author = git_commit_author(commit);
485 CString authorName;
486 g_Git.StringAppend(&authorName, (BYTE*)author->name, encode);
488 CString message;
489 g_Git.StringAppend(&message, (BYTE*)git_commit_message(commit), encode);
491 int start = 0;
492 message = message.Tokenize(L"\n", start);
494 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
495 SetDlgItemText(m_hwnd, subjectLabel, message);
496 SetDlgItemText(m_hwnd, authorLabel, authorName);
498 CString authorDate;
499 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
500 SetDlgItemText(m_hwnd, dateLabel, authorDate);
503 int CGitPropertyPage::LogThread()
505 CTGitPath path(filenames.front().c_str());
507 CString ProjectTopDir;
508 if(!path.HasAdminDir(&ProjectTopDir))
509 return 0;
511 CStringA gitdir = CUnicodeUtils::GetMulti(ProjectTopDir, CP_UTF8);
512 git_repository *repository = NULL;
514 int ret = git_repository_open(&repository, gitdir.GetBuffer());
515 gitdir.ReleaseBuffer();
516 if (ret)
517 return 0;
519 int stripLength = ProjectTopDir.GetLength();
520 if (ProjectTopDir[stripLength - 1] != _T('\\'))
521 stripLength++;
523 CTGitPath relatepath;
524 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
526 git_commit *commit = FindFileRecentCommit(repository, relatepath.GetGitPathString());
527 if (commit != NULL)
529 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)commit);
530 git_commit_free(commit);
532 else
534 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
537 git_repository_free(repository);
539 return 0;
542 void CGitPropertyPage::LogThreadEntry(void *param)
544 ((CGitPropertyPage*)param)->LogThread();
547 void CGitPropertyPage::InitWorkfileView()
549 if (filenames.empty())
550 return;
552 CTGitPath path(filenames.front().c_str());
554 CString ProjectTopDir;
555 if(!path.HasAdminDir(&ProjectTopDir))
556 return;
558 CStringA gitdir = CUnicodeUtils::GetMulti(ProjectTopDir, CP_UTF8);
559 git_repository *repository = NULL;
561 int ret = git_repository_open(&repository, gitdir.GetBuffer());
562 gitdir.ReleaseBuffer();
563 if (ret)
564 return;
566 CGit git;
568 git.SetCurrentDir(ProjectTopDir);
569 CString username;
570 git.Run(_T("git.exe config user.name"), &username, CP_UTF8);
571 CString useremail;
572 git.Run(_T("git.exe config user.email"), &useremail, CP_UTF8);
573 CString autocrlf;
574 git.Run(_T("git.exe config core.autocrlf"), &autocrlf, CP_UTF8);
575 CString safecrlf;
576 git.Run(_T("git.exe config core.safecrlf"), &safecrlf, CP_UTF8);
578 CString branch;
579 CString remotebranch;
581 if (!g_Git.GetCurrentBranchFromFile(ProjectTopDir, branch))
583 CString cmd, remote;
584 cmd.Format(_T("git.exe config branch.%s.merge"), branch.Trim());
585 git.Run(cmd, &remotebranch, CP_UTF8);
586 cmd.Format(_T("git.exe config branch.%s.remote"), branch.Trim());
587 git.Run(cmd, &remote, CP_UTF8);
588 remote.Trim();
589 remotebranch.Trim();
590 if((!remote.IsEmpty()) && (!remotebranch.IsEmpty()))
592 remotebranch = remotebranch.Mid(11);
593 remotebranch = remote + _T("/") + remotebranch;
596 else
597 branch = _T("detached HEAD");
599 if (autocrlf.Trim().IsEmpty())
600 autocrlf = _T("false");
601 if (safecrlf.Trim().IsEmpty())
602 safecrlf = _T("false");
604 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
605 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
606 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
607 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
609 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
610 remotebranch.Trim().Replace(_T("\n"), _T("; "));
611 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
613 git_oid oid;
614 git_commit *HEADcommit = NULL;
615 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(&HEADcommit, repository, &oid) && HEADcommit != NULL)
617 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
618 git_commit_free(HEADcommit);
622 int stripLength = ProjectTopDir.GetLength();
623 if (ProjectTopDir[stripLength - 1] != _T('\\'))
624 stripLength++;
626 bool allAreFiles = true;
627 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
629 if (PathIsDirectory(it->c_str()))
631 allAreFiles = false;
632 break;
635 if (allAreFiles)
637 int assumevalid = 0;
638 int skipworktree = 0;
639 int executable = 0;
642 git_index *index = NULL;
644 if (git_repository_index(&index, repository))
645 break;
647 for (auto it = filenames.cbegin(); it < filenames.cend(); it++)
649 CTGitPath file;
650 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
651 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
652 int idx = git_index_find(index, pathA);
653 if (idx >= 0)
655 const git_index_entry *e = git_index_get_byindex(index, idx);
657 if (e->flags & GIT_IDXENTRY_VALID)
658 ++assumevalid;
660 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
661 ++skipworktree;
663 if (e->mode & 0111)
664 ++executable;
666 else
668 // do not show checkboxes for unversioned files
669 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
670 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
671 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
672 break;
675 git_index_free(index);
676 } while (0);
678 if (assumevalid != 0 && assumevalid != filenames.size())
680 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
681 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
683 else
684 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
686 if (skipworktree != 0 && skipworktree != filenames.size())
688 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
689 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
691 else
692 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
694 if (executable != 0 && executable != filenames.size())
696 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
697 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
699 else
700 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
702 else
704 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
705 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
706 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
709 git_repository_free(repository);
711 if (filenames.size() == 1)
713 SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, _T("Loading ..."));
714 CString adminDir;
715 g_GitAdminDir.GetAdminDirPath(ProjectTopDir, adminDir);
716 CDirFileEnum dir(adminDir);
717 UINT64 size = dir.GetSize();
718 // Suppose there is one commit for every 2000 bytes of git metadata
719 // Suppose for each commit, it takes 4 units of local variables and stack pointer
720 // Provide a minimum of 1MB stack size
721 // Maximum stack size: 256MB for 32-bit, 512MB for 64-bit
722 UINT stack = 1024 * 1024 + min(64 * 1024 * 1024 * sizeof(void*), (UINT)(size / 2000) * 4 * sizeof(void*));
723 _beginthread(LogThreadEntry, stack, this);
728 // CShellExt member functions (needed for IShellPropSheetExt)
729 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
731 __try
733 return AddPages_Wrap(lpfnAddPage, lParam);
735 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
738 return E_FAIL;
741 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
743 CString ProjectTopDir;
745 for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)
748 GitStatus svn = GitStatus();
749 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
750 return S_OK; // file/directory not under version control
752 if (svn.status->entry == NULL)
753 return S_OK;
755 if( CTGitPath(I->c_str()).HasAdminDir(&ProjectTopDir))
756 break;
757 else
758 return S_OK;
761 if (files_.empty())
762 return S_OK;
764 LoadLangDll();
765 PROPSHEETPAGE psp;
766 SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
767 HPROPSHEETPAGE hPage;
768 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
770 psp.dwSize = sizeof (psp);
771 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
772 psp.hInstance = g_hResInst;
773 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
774 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
775 psp.pszTitle = _T("Git");
776 psp.pfnDlgProc = (DLGPROC) PageProc;
777 psp.lParam = (LPARAM) sheetpage;
778 psp.pfnCallback = PropPageCallbackProc;
779 psp.pcRefParent = (UINT*)&g_cRefThisDll;
781 hPage = CreatePropertySheetPage (&psp);
783 if (hPage != NULL)
785 if (!lpfnAddPage (hPage, lParam))
787 delete sheetpage;
788 DestroyPropertySheetPage (hPage);
792 return S_OK;
795 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
797 return E_FAIL;