Synced ResText with TortoiseSVN
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blob36e87bdb978384dedb2d2aa99e6fdb3fd745fbc7
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"
30 #define MAX_STRING_LENGTH 4096 //should be big enough
32 // Nonmember function prototypes
33 BOOL CALLBACK PageProc (HWND, UINT, WPARAM, LPARAM);
34 UINT CALLBACK PropPageCallbackProc ( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp );
36 /////////////////////////////////////////////////////////////////////////////
37 // Dialog procedures and other callback functions
39 BOOL CALLBACK PageProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
41 CGitPropertyPage * sheetpage;
43 if (uMessage == WM_INITDIALOG)
45 sheetpage = (CGitPropertyPage*) ((LPPROPSHEETPAGE) lParam)->lParam;
46 SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) sheetpage);
47 sheetpage->SetHwnd(hwnd);
49 else
51 sheetpage = (CGitPropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
54 if (sheetpage != 0L)
55 return sheetpage->PageProc(hwnd, uMessage, wParam, lParam);
56 else
57 return FALSE;
60 UINT CALLBACK PropPageCallbackProc ( HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp )
62 // Delete the page before closing.
63 if (PSPCB_RELEASE == uMsg)
65 CGitPropertyPage* sheetpage = (CGitPropertyPage*) ppsp->lParam;
66 if (sheetpage != NULL)
67 delete sheetpage;
69 return 1;
72 // *********************** CGitPropertyPage *************************
74 CGitPropertyPage::CGitPropertyPage(const std::vector<stdstring> &newFilenames)
75 :filenames(newFilenames)
76 ,m_bChanged(false)
77 , m_hwnd(NULL)
81 CGitPropertyPage::~CGitPropertyPage(void)
85 void CGitPropertyPage::SetHwnd(HWND newHwnd)
87 m_hwnd = newHwnd;
90 BOOL CGitPropertyPage::PageProc (HWND /*hwnd*/, UINT uMessage, WPARAM wParam, LPARAM lParam)
92 switch (uMessage)
94 case WM_INITDIALOG:
96 InitWorkfileView();
97 return TRUE;
99 case WM_NOTIFY:
101 LPNMHDR point = (LPNMHDR)lParam;
102 int code = point->code;
104 // Respond to notifications.
106 if (code == PSN_APPLY && m_bChanged)
110 CTGitPath path(filenames.front().c_str());
111 CString projectTopDir;
112 if(!path.HasAdminDir(&projectTopDir) || path.IsDirectory())
113 break;
115 int stripLength = projectTopDir.GetLength();
116 if (projectTopDir[stripLength - 1] != _T('\\'))
117 stripLength++;
119 CStringA gitdir = CUnicodeUtils::GetMulti(projectTopDir, CP_UTF8);
120 git_repository *repository = NULL;
121 git_index *index = NULL;
123 int ret = git_repository_open(&repository, gitdir.GetBuffer());
124 gitdir.ReleaseBuffer();
125 if (ret)
126 break;
128 if (git_repository_index(&index, repository))
130 git_repository_free(repository);
131 break;
134 BOOL assumeValid = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_GETCHECK, 0, 0);
135 BOOL skipWorktree = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_GETCHECK, 0, 0);
136 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
138 bool changed = false;
140 for (auto it = filenames.cbegin(); it < filenames.cend(); it++)
142 CTGitPath file;
143 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
144 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
145 int idx = git_index_find(index, pathA);
146 if (idx >= 0)
148 git_index_entry *e = const_cast<git_index_entry *>(git_index_get_byindex(index, idx)); // HACK
150 if (assumeValid == BST_CHECKED)
152 if (!(e->flags & GIT_IDXENTRY_VALID))
154 e->flags |= GIT_IDXENTRY_VALID;
155 changed = true;
158 else if (assumeValid != BST_INDETERMINATE)
160 if (e->flags & GIT_IDXENTRY_VALID)
162 e->flags &= ~GIT_IDXENTRY_VALID;
163 changed = true;
166 if (skipWorktree == BST_CHECKED)
168 if (!(e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE))
170 e->flags_extended |= GIT_IDXENTRY_SKIP_WORKTREE;
171 changed = true;
174 else if (skipWorktree != BST_INDETERMINATE)
176 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
178 e->flags_extended &= ~GIT_IDXENTRY_SKIP_WORKTREE;
179 changed = true;
182 if (executable == BST_CHECKED)
184 if (!(e->mode & 0111))
186 e->mode |= 0111;
187 changed = true;
190 else if (executable != BST_INDETERMINATE)
192 if (e->mode & 0111)
194 e->mode &= ~0111;
195 changed = true;
198 if (changed)
199 git_index_add(index, e);
203 if (changed)
205 if (!git_index_write(index))
206 m_bChanged = false;
209 git_index_free(index);
210 } while (0);
212 SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE);
213 return TRUE;
215 case WM_DESTROY:
216 return TRUE;
218 case WM_COMMAND:
219 PageProcOnCommand(wParam);
220 break;
221 } // switch (uMessage)
222 return FALSE;
224 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam)
226 if(HIWORD(wParam) != BN_CLICKED)
227 return;
229 switch (LOWORD(wParam))
231 case IDC_SHOWLOG:
233 tstring gitCmd = _T(" /command:");
234 gitCmd += _T("log /path:\"");
235 gitCmd += filenames.front().c_str();
236 gitCmd += _T("\"");
237 RunCommand(gitCmd);
239 break;
240 case IDC_SHOWSETTINGS:
242 CTGitPath path(filenames.front().c_str());
243 CString projectTopDir;
244 if(!path.HasAdminDir(&projectTopDir))
245 return;
247 tstring gitCmd = _T(" /command:");
248 gitCmd += _T("settings /path:\"");
249 gitCmd += projectTopDir;
250 gitCmd += _T("\"");
251 RunCommand(gitCmd);
253 break;
254 case IDC_ASSUMEVALID:
255 case IDC_SKIPWORKTREE:
256 case IDC_EXECUTABLE:
257 m_bChanged = true;
258 SendMessage(GetParent(m_hwnd), PSM_CHANGED, (WPARAM)m_hwnd, 0);
259 break;
263 void CGitPropertyPage::RunCommand(const tstring& command)
265 tstring tortoiseProcPath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TortoiseGitProc.exe");
266 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str())))
268 // process started - exit
269 return;
272 MessageBox(NULL, CFormatMessageWrapper(), _T("TortoiseProc launch failed"), MB_OK | MB_ICONINFORMATION);
275 void CGitPropertyPage::Time64ToTimeString(__time64_t time, TCHAR * buf, size_t buflen)
277 struct tm newtime;
278 SYSTEMTIME systime;
279 TCHAR timebuf[MAX_STRING_LENGTH];
280 TCHAR datebuf[MAX_STRING_LENGTH];
282 LCID locale = (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
283 locale = MAKELCID(locale, SORT_DEFAULT);
285 *buf = '\0';
286 if (time)
288 _localtime64_s(&newtime, &time);
290 systime.wDay = (WORD)newtime.tm_mday;
291 systime.wDayOfWeek = (WORD)newtime.tm_wday;
292 systime.wHour = (WORD)newtime.tm_hour;
293 systime.wMilliseconds = 0;
294 systime.wMinute = (WORD)newtime.tm_min;
295 systime.wMonth = (WORD)newtime.tm_mon+1;
296 systime.wSecond = (WORD)newtime.tm_sec;
297 systime.wYear = (WORD)newtime.tm_year+1900;
298 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
299 GetDateFormat(locale, DATE_SHORTDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
300 else
301 GetDateFormat(locale, DATE_LONGDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
302 GetTimeFormat(locale, 0, &systime, NULL, timebuf, MAX_STRING_LENGTH);
303 *buf = '\0';
304 _tcsncat_s(buf, buflen, timebuf, MAX_STRING_LENGTH-1);
305 _tcsncat_s(buf, buflen, _T(", "), MAX_STRING_LENGTH-1);
306 _tcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH-1);
310 struct TreewalkStruct
312 const char *folder;
313 const char *name;
314 git_oid oid;
317 static int TreewalkCB_FindFileRecentCommit(const char *root, const git_tree_entry *entry, void *payload)
319 TreewalkStruct *treewalkstruct = (TreewalkStruct *)payload;
320 char folder[MAX_PATH];
321 strcpy(folder, root);
322 strcat(folder, git_tree_entry_name(entry));
323 strcat(folder, "/");
324 if (strstr(treewalkstruct->folder, folder))
325 return 0;
327 if (!strcmp(treewalkstruct->folder, root))
329 if (!strcmp(git_tree_entry_name(entry), treewalkstruct->name))
331 git_oid_cpy(&treewalkstruct->oid, git_tree_entry_id(entry));
332 return -1;
335 return 1;
338 return 1;
341 static git_commit * FindFileRecentCommit(git_repository *repository, CString path)
343 git_commit *commit = NULL, *commit2 = NULL;
344 git_revwalk *walk;
345 if (git_revwalk_new(&walk, repository))
346 return NULL;
348 CStringA pathA = CUnicodeUtils::GetUTF8(path);
349 const char *pathC = pathA.GetBuffer();
350 char folder[MAX_PATH], file[MAX_PATH];
351 const char *slash = strrchr(pathC, '/');
352 if (slash)
354 strncpy(folder, pathC, slash - pathC + 1);
355 folder[slash - pathC + 1] = '\0';
356 strcpy(file, slash + 1);
358 else
360 folder[0] = '\0';
361 strcpy(file, pathC);
363 pathA.ReleaseBuffer();
365 TreewalkStruct treewalkstruct = { folder, file };
366 TreewalkStruct treewalkstruct2 = { folder, file };
370 if (git_revwalk_push_head(walk))
371 throw "git_revwalk_push_head";
373 git_oid oid;
374 while (!git_revwalk_next(&oid, walk))
376 if (commit != NULL)
378 git_commit_free(commit);
379 commit = NULL;
382 if (git_commit_lookup(&commit, repository, &oid))
384 commit = NULL;
385 throw "git_commit_lookup 1";
388 git_tree *tree;
389 if (git_commit_tree(&tree, commit))
390 throw "git_commit_tree 1";
392 memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
393 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);
394 git_tree_free(tree);
395 if (ret < 0 && ret != GIT_EUSER)
396 throw "git_tree_walk 1";
398 // check if file not found
399 if (git_oid_iszero(&treewalkstruct.oid))
401 git_commit_free(commit);
402 commit = NULL;
403 break;
406 bool diff = true;
407 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
408 // if no parent then of course it is different
409 for (unsigned int i = 0; i < git_commit_parentcount(commit); i++)
411 if (git_commit_parent(&commit2, commit, i))
413 commit2 = NULL;
414 throw "git_commit_parent";
417 if (git_commit_tree(&tree, commit2))
418 throw "git_commit_tree 2";
420 git_commit_free(commit2);
421 memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
422 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);
423 git_tree_free(tree);
424 if (ret < 0 && ret != GIT_EUSER)
425 throw "git_tree_walk 2";
427 if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
428 diff = false;
429 else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
430 throw "git_revwalk_hide";
433 if (diff)
434 break;
437 catch (...)
439 if (commit != NULL)
441 git_commit_free(commit);
442 commit = NULL;
444 if (commit2 != NULL)
446 git_commit_free(commit2);
447 commit2 = NULL;
451 git_revwalk_free(walk);
452 return commit;
455 void CGitPropertyPage::DisplayCommit(git_commit * commit, UINT hashLabel, UINT subjectLabel, UINT authorLabel, UINT dateLabel)
457 int encode = CP_UTF8;
458 const char * encodingString = git_commit_message_encoding(commit);
459 if (encodingString != NULL)
461 CString str;
462 g_Git.StringAppend(&str, (BYTE*)encodingString, CP_UTF8);
463 encode = CUnicodeUtils::GetCPCode(str);
466 const git_signature * author = git_commit_author(commit);
467 CString authorName;
468 g_Git.StringAppend(&authorName, (BYTE*)author->name, encode);
470 CString message;
471 g_Git.StringAppend(&message, (BYTE*)git_commit_message(commit), encode);
473 int start = 0;
474 message = message.Tokenize(L"\n", start);
476 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
477 SetDlgItemText(m_hwnd, subjectLabel, message);
478 SetDlgItemText(m_hwnd, authorLabel, authorName);
480 CString authorDate;
481 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
482 SetDlgItemText(m_hwnd, dateLabel, authorDate);
485 void CGitPropertyPage::InitWorkfileView()
487 if (filenames.empty())
488 return;
490 CTGitPath path(filenames.front().c_str());
492 CString ProjectTopDir;
493 if(!path.HasAdminDir(&ProjectTopDir))
494 return;
496 CStringA gitdir = CUnicodeUtils::GetMulti(ProjectTopDir, CP_UTF8);
497 git_repository *repository = NULL;
499 int ret = git_repository_open(&repository, gitdir.GetBuffer());
500 gitdir.ReleaseBuffer();
501 if (ret)
502 return;
504 CGit git;
506 git.SetCurrentDir(ProjectTopDir);
507 CString username;
508 git.Run(_T("git.exe config user.name"), &username, CP_UTF8);
509 CString useremail;
510 git.Run(_T("git.exe config user.email"), &useremail, CP_UTF8);
511 CString autocrlf;
512 git.Run(_T("git.exe config core.autocrlf"), &autocrlf, CP_UTF8);
513 CString safecrlf;
514 git.Run(_T("git.exe config core.safecrlf"), &safecrlf, CP_UTF8);
516 CString branch;
517 CString remotebranch;
519 if (!g_Git.GetCurrentBranchFromFile(ProjectTopDir, branch))
521 CString cmd, remote;
522 cmd.Format(_T("git.exe config branch.%s.merge"), branch.Trim());
523 git.Run(cmd, &remotebranch, CP_UTF8);
524 cmd.Format(_T("git.exe config branch.%s.remote"), branch.Trim());
525 git.Run(cmd, &remote, CP_UTF8);
526 remote.Trim();
527 remotebranch.Trim();
528 if((!remote.IsEmpty()) && (!remotebranch.IsEmpty()))
530 remotebranch = remotebranch.Mid(11);
531 remotebranch = remote + _T("/") + remotebranch;
534 else
535 branch = _T("detached HEAD");
537 if (autocrlf.Trim().IsEmpty())
538 autocrlf = _T("false");
539 if (safecrlf.Trim().IsEmpty())
540 safecrlf = _T("false");
542 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
543 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
544 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
545 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
547 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
548 remotebranch.Trim().Replace(_T("\n"), _T("; "));
549 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
551 git_oid oid;
552 git_commit *HEADcommit = NULL;
553 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(&HEADcommit, repository, &oid) && HEADcommit != NULL)
555 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
556 git_commit_free(HEADcommit);
560 int stripLength = ProjectTopDir.GetLength();
561 if (ProjectTopDir[stripLength - 1] != _T('\\'))
562 stripLength++;
564 if (filenames.size() == 1)
566 CTGitPath relatepath;
567 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
569 git_commit *commit = FindFileRecentCommit(repository, relatepath.GetGitPathString());
570 if (commit != NULL)
572 DisplayCommit(commit, IDC_LAST_HASH, IDC_LAST_SUBJECT, IDC_LAST_AUTHOR, IDC_LAST_DATE);
573 git_commit_free(commit);
577 bool allAreFiles = true;
578 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
580 if (PathIsDirectory(it->c_str()))
582 allAreFiles = false;
583 break;
586 if (allAreFiles)
588 int assumevalid = 0;
589 int skipworktree = 0;
590 int executable = 0;
593 git_index *index = NULL;
595 if (git_repository_index(&index, repository))
596 break;
598 for (auto it = filenames.cbegin(); it < filenames.cend(); it++)
600 CTGitPath file;
601 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
602 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
603 int idx = git_index_find(index, pathA);
604 if (idx >= 0)
606 const git_index_entry *e = git_index_get_byindex(index, idx);
608 if (e->flags & GIT_IDXENTRY_VALID)
609 ++assumevalid;
611 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
612 ++skipworktree;
614 if (e->mode & 0111)
615 ++executable;
617 else
619 // do not show checkboxes for unversioned files
620 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
621 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
622 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
623 break;
626 git_index_free(index);
627 } while (0);
629 if (assumevalid != 0 && assumevalid != filenames.size())
631 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
632 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
634 else
635 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
637 if (skipworktree != 0 && skipworktree != filenames.size())
639 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
640 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
642 else
643 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
645 if (executable != 0 && executable != filenames.size())
647 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
648 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
650 else
651 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
653 else
655 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
656 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
657 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
660 git_repository_free(repository);
664 // CShellExt member functions (needed for IShellPropSheetExt)
665 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
667 __try
669 return AddPages_Wrap(lpfnAddPage, lParam);
671 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
674 return E_FAIL;
677 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
679 CString ProjectTopDir;
681 for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)
684 GitStatus svn = GitStatus();
685 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
686 return S_OK; // file/directory not under version control
688 if (svn.status->entry == NULL)
689 return S_OK;
691 if( CTGitPath(I->c_str()).HasAdminDir(&ProjectTopDir))
692 break;
693 else
694 return S_OK;
697 if (files_.empty())
698 return S_OK;
700 LoadLangDll();
701 PROPSHEETPAGE psp;
702 SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
703 HPROPSHEETPAGE hPage;
704 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
706 psp.dwSize = sizeof (psp);
707 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
708 psp.hInstance = g_hResInst;
709 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
710 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
711 psp.pszTitle = _T("Git");
712 psp.pfnDlgProc = (DLGPROC) PageProc;
713 psp.lParam = (LPARAM) sheetpage;
714 psp.pfnCallback = PropPageCallbackProc;
715 psp.pcRefParent = (UINT*)&g_cRefThisDll;
717 hPage = CreatePropertySheetPage (&psp);
719 if (hPage != NULL)
721 if (!lpfnAddPage (hPage, lParam))
723 delete sheetpage;
724 DestroyPropertySheetPage (hPage);
728 return S_OK;
731 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
733 return E_FAIL;