Replace var++ by ++var and use size_t where necessary
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blob29e4c4dcd22b738862f9424c9524af8d3978ce39
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 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
33 #define MAX_STRING_LENGTH 4096 //should be big enough
35 // Nonmember function prototypes
36 BOOL CALLBACK PageProc (HWND, UINT, WPARAM, LPARAM);
37 UINT CALLBACK PropPageCallbackProc ( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp );
39 /////////////////////////////////////////////////////////////////////////////
40 // Dialog procedures and other callback functions
42 BOOL CALLBACK PageProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
44 CGitPropertyPage * sheetpage;
46 if (uMessage == WM_INITDIALOG)
48 sheetpage = (CGitPropertyPage*) ((LPPROPSHEETPAGE) lParam)->lParam;
49 SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) sheetpage);
50 sheetpage->SetHwnd(hwnd);
52 else
54 sheetpage = (CGitPropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
57 if (sheetpage != 0L)
58 return sheetpage->PageProc(hwnd, uMessage, wParam, lParam);
59 else
60 return FALSE;
63 UINT CALLBACK PropPageCallbackProc ( HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp )
65 // Delete the page before closing.
66 if (PSPCB_RELEASE == uMsg)
68 CGitPropertyPage* sheetpage = (CGitPropertyPage*) ppsp->lParam;
69 if (sheetpage != NULL)
70 delete sheetpage;
72 return 1;
75 // *********************** CGitPropertyPage *************************
76 const UINT CGitPropertyPage::m_UpdateLastCommit = RegisterWindowMessage(_T("TORTOISEGIT_PROP_UPDATELASTCOMMIT"));
78 CGitPropertyPage::CGitPropertyPage(const std::vector<stdstring> &newFilenames)
79 :filenames(newFilenames)
80 ,m_bChanged(false)
81 , m_hwnd(NULL)
85 CGitPropertyPage::~CGitPropertyPage(void)
89 void CGitPropertyPage::SetHwnd(HWND newHwnd)
91 m_hwnd = newHwnd;
94 BOOL CGitPropertyPage::PageProc (HWND /*hwnd*/, UINT uMessage, WPARAM wParam, LPARAM lParam)
96 switch (uMessage)
98 case WM_INITDIALOG:
100 InitWorkfileView();
101 return TRUE;
103 case WM_NOTIFY:
105 LPNMHDR point = (LPNMHDR)lParam;
106 int code = point->code;
108 // Respond to notifications.
110 if (code == PSN_APPLY && m_bChanged)
114 CTGitPath path(filenames.front().c_str());
115 CString projectTopDir;
116 if(!path.HasAdminDir(&projectTopDir) || path.IsDirectory())
117 break;
119 int stripLength = projectTopDir.GetLength();
120 if (projectTopDir[stripLength - 1] != _T('\\'))
121 ++stripLength;
123 CStringA gitdir = CUnicodeUtils::GetMulti(projectTopDir, CP_UTF8);
124 git_repository *repository = NULL;
125 git_index *index = NULL;
127 int ret = git_repository_open(&repository, gitdir.GetBuffer());
128 gitdir.ReleaseBuffer();
129 if (ret)
130 break;
132 if (git_repository_index(&index, repository))
134 git_repository_free(repository);
135 break;
138 BOOL assumeValid = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_GETCHECK, 0, 0);
139 BOOL skipWorktree = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_GETCHECK, 0, 0);
140 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
142 bool changed = false;
144 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
146 CTGitPath file;
147 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
148 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
149 int idx = git_index_find(index, pathA);
150 if (idx >= 0)
152 git_index_entry *e = const_cast<git_index_entry *>(git_index_get_byindex(index, idx)); // HACK
154 if (assumeValid == BST_CHECKED)
156 if (!(e->flags & GIT_IDXENTRY_VALID))
158 e->flags |= GIT_IDXENTRY_VALID;
159 changed = true;
162 else if (assumeValid != BST_INDETERMINATE)
164 if (e->flags & GIT_IDXENTRY_VALID)
166 e->flags &= ~GIT_IDXENTRY_VALID;
167 changed = true;
170 if (skipWorktree == BST_CHECKED)
172 if (!(e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE))
174 e->flags_extended |= GIT_IDXENTRY_SKIP_WORKTREE;
175 changed = true;
178 else if (skipWorktree != BST_INDETERMINATE)
180 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
182 e->flags_extended &= ~GIT_IDXENTRY_SKIP_WORKTREE;
183 changed = true;
186 if (executable == BST_CHECKED)
188 if (!(e->mode & 0111))
190 e->mode |= 0111;
191 changed = true;
194 else if (executable != BST_INDETERMINATE)
196 if (e->mode & 0111)
198 e->mode &= ~0111;
199 changed = true;
202 if (changed)
203 git_index_add(index, e);
207 if (changed)
209 if (!git_index_write(index))
210 m_bChanged = false;
213 git_index_free(index);
214 } while (0);
216 SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE);
217 return TRUE;
219 case WM_DESTROY:
220 return TRUE;
222 case WM_COMMAND:
223 PageProcOnCommand(wParam);
224 break;
225 } // switch (uMessage)
227 if (uMessage == m_UpdateLastCommit)
229 DisplayCommit((git_commit *)lParam, IDC_LAST_HASH, IDC_LAST_SUBJECT, IDC_LAST_AUTHOR, IDC_LAST_DATE);
230 return TRUE;
233 return FALSE;
235 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam)
237 if(HIWORD(wParam) != BN_CLICKED)
238 return;
240 switch (LOWORD(wParam))
242 case IDC_SHOWLOG:
244 tstring gitCmd = _T(" /command:");
245 gitCmd += _T("log /path:\"");
246 gitCmd += filenames.front().c_str();
247 gitCmd += _T("\"");
248 RunCommand(gitCmd);
250 break;
251 case IDC_SHOWSETTINGS:
253 CTGitPath path(filenames.front().c_str());
254 CString projectTopDir;
255 if(!path.HasAdminDir(&projectTopDir))
256 return;
258 tstring gitCmd = _T(" /command:");
259 gitCmd += _T("settings /path:\"");
260 gitCmd += projectTopDir;
261 gitCmd += _T("\"");
262 RunCommand(gitCmd);
264 break;
265 case IDC_ASSUMEVALID:
266 case IDC_SKIPWORKTREE:
267 case IDC_EXECUTABLE:
268 m_bChanged = true;
269 SendMessage(GetParent(m_hwnd), PSM_CHANGED, (WPARAM)m_hwnd, 0);
270 break;
274 void CGitPropertyPage::RunCommand(const tstring& command)
276 tstring tortoiseProcPath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TortoiseGitProc.exe");
277 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str())))
279 // process started - exit
280 return;
283 MessageBox(NULL, CFormatMessageWrapper(), _T("TortoiseProc launch failed"), MB_OK | MB_ICONINFORMATION);
286 void CGitPropertyPage::Time64ToTimeString(__time64_t time, TCHAR * buf, size_t buflen)
288 struct tm newtime;
289 SYSTEMTIME systime;
290 TCHAR timebuf[MAX_STRING_LENGTH];
291 TCHAR datebuf[MAX_STRING_LENGTH];
293 LCID locale = (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
294 locale = MAKELCID(locale, SORT_DEFAULT);
296 *buf = '\0';
297 if (time)
299 _localtime64_s(&newtime, &time);
301 systime.wDay = (WORD)newtime.tm_mday;
302 systime.wDayOfWeek = (WORD)newtime.tm_wday;
303 systime.wHour = (WORD)newtime.tm_hour;
304 systime.wMilliseconds = 0;
305 systime.wMinute = (WORD)newtime.tm_min;
306 systime.wMonth = (WORD)newtime.tm_mon+1;
307 systime.wSecond = (WORD)newtime.tm_sec;
308 systime.wYear = (WORD)newtime.tm_year+1900;
309 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
310 GetDateFormat(locale, DATE_SHORTDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
311 else
312 GetDateFormat(locale, DATE_LONGDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
313 GetTimeFormat(locale, 0, &systime, NULL, timebuf, MAX_STRING_LENGTH);
314 *buf = '\0';
315 _tcsncat_s(buf, buflen, timebuf, MAX_STRING_LENGTH-1);
316 _tcsncat_s(buf, buflen, _T(", "), MAX_STRING_LENGTH-1);
317 _tcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH-1);
321 struct TreewalkStruct
323 const char *folder;
324 const char *name;
325 git_oid oid;
328 static int TreewalkCB_FindFileRecentCommit(const char *root, const git_tree_entry *entry, void *payload)
330 TreewalkStruct *treewalkstruct = (TreewalkStruct *)payload;
331 char folder[MAX_PATH];
332 strcpy(folder, root);
333 strcat(folder, git_tree_entry_name(entry));
334 strcat(folder, "/");
335 if (strstr(treewalkstruct->folder, folder))
336 return 0;
338 if (!strcmp(treewalkstruct->folder, root))
340 if (!strcmp(git_tree_entry_name(entry), treewalkstruct->name))
342 git_oid_cpy(&treewalkstruct->oid, git_tree_entry_id(entry));
343 return -1;
346 return 1;
349 return 1;
352 static git_commit * FindFileRecentCommit(git_repository *repository, CString path)
354 git_commit *commit = NULL, *commit2 = NULL;
355 git_revwalk *walk;
356 if (git_revwalk_new(&walk, repository))
357 return NULL;
359 CStringA pathA = CUnicodeUtils::GetUTF8(path);
360 const char *pathC = pathA.GetBuffer();
361 char folder[MAX_PATH], file[MAX_PATH];
362 const char *slash = strrchr(pathC, '/');
363 if (slash)
365 strncpy(folder, pathC, slash - pathC + 1);
366 folder[slash - pathC + 1] = '\0';
367 strcpy(file, slash + 1);
369 else
371 folder[0] = '\0';
372 strcpy(file, pathC);
374 pathA.ReleaseBuffer();
376 TreewalkStruct treewalkstruct = { folder, file };
377 TreewalkStruct treewalkstruct2 = { folder, file };
381 if (git_revwalk_push_head(walk))
382 throw "git_revwalk_push_head";
384 git_oid oid;
385 while (!git_revwalk_next(&oid, walk))
387 if (commit != NULL)
389 git_commit_free(commit);
390 commit = NULL;
393 if (git_commit_lookup(&commit, repository, &oid))
395 commit = NULL;
396 throw "git_commit_lookup 1";
399 git_tree *tree;
400 if (git_commit_tree(&tree, commit))
401 throw "git_commit_tree 1";
403 memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
404 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);
405 git_tree_free(tree);
406 if (ret < 0 && ret != GIT_EUSER)
407 throw "git_tree_walk 1";
409 // check if file not found
410 if (git_oid_iszero(&treewalkstruct.oid))
412 git_commit_free(commit);
413 commit = NULL;
414 break;
417 bool diff = true;
418 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
419 // if no parent then of course it is different
420 for (unsigned int i = 0; i < git_commit_parentcount(commit); ++i)
422 if (git_commit_parent(&commit2, commit, i))
424 commit2 = NULL;
425 throw "git_commit_parent";
428 if (git_commit_tree(&tree, commit2))
429 throw "git_commit_tree 2";
431 git_commit_free(commit2);
432 memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
433 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);
434 git_tree_free(tree);
435 if (ret < 0 && ret != GIT_EUSER)
436 throw "git_tree_walk 2";
438 if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
439 diff = false;
440 else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
441 throw "git_revwalk_hide";
444 if (diff)
445 break;
448 catch (...)
450 if (commit != NULL)
452 git_commit_free(commit);
453 commit = NULL;
455 if (commit2 != NULL)
457 git_commit_free(commit2);
458 commit2 = NULL;
462 git_revwalk_free(walk);
463 return commit;
466 void CGitPropertyPage::DisplayCommit(git_commit * commit, UINT hashLabel, UINT subjectLabel, UINT authorLabel, UINT dateLabel)
468 if (commit == NULL)
470 SetDlgItemText(m_hwnd, hashLabel, _T(""));
471 SetDlgItemText(m_hwnd, subjectLabel, _T(""));
472 SetDlgItemText(m_hwnd, authorLabel, _T(""));
473 SetDlgItemText(m_hwnd, dateLabel, _T(""));
474 return;
477 int encode = CP_UTF8;
478 const char * encodingString = git_commit_message_encoding(commit);
479 if (encodingString != NULL)
481 CString str;
482 g_Git.StringAppend(&str, (BYTE*)encodingString, CP_UTF8);
483 encode = CUnicodeUtils::GetCPCode(str);
486 const git_signature * author = git_commit_author(commit);
487 CString authorName;
488 g_Git.StringAppend(&authorName, (BYTE*)author->name, encode);
490 CString message;
491 g_Git.StringAppend(&message, (BYTE*)git_commit_message(commit), encode);
493 int start = 0;
494 message = message.Tokenize(L"\n", start);
496 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
497 SetDlgItemText(m_hwnd, subjectLabel, message);
498 SetDlgItemText(m_hwnd, authorLabel, authorName);
500 CString authorDate;
501 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
502 SetDlgItemText(m_hwnd, dateLabel, authorDate);
505 int CGitPropertyPage::LogThread()
507 CTGitPath path(filenames.front().c_str());
509 CAutoLocker lock(g_Git.m_critGitDllSec); // HACK for libgit2
511 CString ProjectTopDir;
512 if(!path.HasAdminDir(&ProjectTopDir))
513 return 0;
515 CStringA gitdir = CUnicodeUtils::GetMulti(ProjectTopDir, CP_UTF8);
516 git_repository *repository = NULL;
518 int ret = git_repository_open(&repository, gitdir.GetBuffer());
519 gitdir.ReleaseBuffer();
520 if (ret)
521 return 0;
523 int stripLength = ProjectTopDir.GetLength();
524 if (ProjectTopDir[stripLength - 1] != _T('\\'))
525 ++stripLength;
527 CTGitPath relatepath;
528 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
530 git_commit *commit = FindFileRecentCommit(repository, relatepath.GetGitPathString());
531 if (commit != NULL)
533 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)commit);
534 git_commit_free(commit);
536 else
538 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
541 git_repository_free(repository);
543 return 0;
546 void CGitPropertyPage::LogThreadEntry(void *param)
548 ((CGitPropertyPage*)param)->LogThread();
551 void CGitPropertyPage::InitWorkfileView()
553 if (filenames.empty())
554 return;
556 CTGitPath path(filenames.front().c_str());
558 CString ProjectTopDir;
559 if(!path.HasAdminDir(&ProjectTopDir))
560 return;
562 CStringA gitdir = CUnicodeUtils::GetMulti(ProjectTopDir, CP_UTF8);
563 git_repository *repository = NULL;
565 int ret = git_repository_open(&repository, gitdir.GetBuffer());
566 gitdir.ReleaseBuffer();
567 if (ret)
568 return;
570 CGit git;
572 git.SetCurrentDir(ProjectTopDir);
573 CString username;
574 git.Run(_T("git.exe config user.name"), &username, CP_UTF8);
575 CString useremail;
576 git.Run(_T("git.exe config user.email"), &useremail, CP_UTF8);
577 CString autocrlf;
578 git.Run(_T("git.exe config core.autocrlf"), &autocrlf, CP_UTF8);
579 CString safecrlf;
580 git.Run(_T("git.exe config core.safecrlf"), &safecrlf, CP_UTF8);
582 CString branch;
583 CString remotebranch;
585 if (!g_Git.GetCurrentBranchFromFile(ProjectTopDir, branch))
587 CString cmd, remote;
588 cmd.Format(_T("git.exe config branch.%s.merge"), branch.Trim());
589 git.Run(cmd, &remotebranch, CP_UTF8);
590 cmd.Format(_T("git.exe config branch.%s.remote"), branch.Trim());
591 git.Run(cmd, &remote, CP_UTF8);
592 remote.Trim();
593 remotebranch.Trim();
594 if((!remote.IsEmpty()) && (!remotebranch.IsEmpty()))
596 remotebranch = remotebranch.Mid(11);
597 remotebranch = remote + _T("/") + remotebranch;
600 else
601 branch = _T("detached HEAD");
603 if (autocrlf.Trim().IsEmpty())
604 autocrlf = _T("false");
605 if (safecrlf.Trim().IsEmpty())
606 safecrlf = _T("false");
608 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
609 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
610 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
611 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
613 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
614 remotebranch.Trim().Replace(_T("\n"), _T("; "));
615 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
617 git_oid oid;
618 git_commit *HEADcommit = NULL;
619 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(&HEADcommit, repository, &oid) && HEADcommit != NULL)
621 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
622 git_commit_free(HEADcommit);
626 int stripLength = ProjectTopDir.GetLength();
627 if (ProjectTopDir[stripLength - 1] != _T('\\'))
628 ++stripLength;
630 bool allAreFiles = true;
631 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
633 if (PathIsDirectory(it->c_str()))
635 allAreFiles = false;
636 break;
639 if (allAreFiles)
641 size_t assumevalid = 0;
642 size_t skipworktree = 0;
643 size_t executable = 0;
646 git_index *index = NULL;
648 if (git_repository_index(&index, repository))
649 break;
651 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
653 CTGitPath file;
654 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
655 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
656 int idx = git_index_find(index, pathA);
657 if (idx >= 0)
659 const git_index_entry *e = git_index_get_byindex(index, idx);
661 if (e->flags & GIT_IDXENTRY_VALID)
662 ++assumevalid;
664 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
665 ++skipworktree;
667 if (e->mode & 0111)
668 ++executable;
670 else
672 // do not show checkboxes for unversioned files
673 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
674 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
675 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
676 break;
679 git_index_free(index);
680 } while (0);
682 if (assumevalid != 0 && assumevalid != filenames.size())
684 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
685 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
687 else
688 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
690 if (skipworktree != 0 && skipworktree != filenames.size())
692 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
693 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
695 else
696 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
698 if (executable != 0 && executable != filenames.size())
700 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
701 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
703 else
704 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
706 else
708 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
709 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
710 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
713 git_repository_free(repository);
715 if (filenames.size() == 1)
717 SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, CString(MAKEINTRESOURCE(IDS_LOADING)));
718 CString adminDir;
719 g_GitAdminDir.GetAdminDirPath(ProjectTopDir, adminDir);
720 CDirFileEnum dir(adminDir);
721 UINT64 size = dir.GetSize();
722 // Suppose there is one commit for every 2000 bytes of git metadata
723 // Suppose for each commit, it takes 4 units of local variables and stack pointer
724 // Provide a minimum of 1MB stack size
725 // Maximum stack size: 256MB for 32-bit, 512MB for 64-bit
726 UINT stack = 1024 * 1024 + min(64 * 1024 * 1024 * sizeof(void*), (UINT)(size / 2000) * 4 * sizeof(void*));
727 _beginthread(LogThreadEntry, stack, this);
732 // CShellExt member functions (needed for IShellPropSheetExt)
733 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
735 __try
737 return AddPages_Wrap(lpfnAddPage, lParam);
739 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
742 return E_FAIL;
745 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
747 CString ProjectTopDir;
749 for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)
752 GitStatus svn = GitStatus();
753 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
754 return S_OK; // file/directory not under version control
756 if (svn.status->entry == NULL)
757 return S_OK;
759 if( CTGitPath(I->c_str()).HasAdminDir(&ProjectTopDir))
760 break;
761 else
762 return S_OK;
765 if (files_.empty())
766 return S_OK;
768 LoadLangDll();
769 PROPSHEETPAGE psp;
770 SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
771 HPROPSHEETPAGE hPage;
772 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
774 psp.dwSize = sizeof (psp);
775 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
776 psp.hInstance = g_hResInst;
777 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
778 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
779 psp.pszTitle = _T("Git");
780 psp.pfnDlgProc = (DLGPROC) PageProc;
781 psp.lParam = (LPARAM) sheetpage;
782 psp.pfnCallback = PropPageCallbackProc;
783 psp.pcRefParent = (UINT*)&g_cRefThisDll;
785 hPage = CreatePropertySheetPage (&psp);
787 if (hPage != NULL)
789 if (!lpfnAddPage (hPage, lParam))
791 delete sheetpage;
792 DestroyPropertySheetPage (hPage);
796 return S_OK;
799 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
801 return E_FAIL;