Fix crash for init repository
[TortoiseGit.git] / src / Git / GitStatus.cpp
blobcaf0d1655b2173ff9a7930c2afdc60f129c3752e
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "registry.h"
22 //#include "resource.h"
23 #include "..\TortoiseShell\resource.h"
24 //#include "git_config.h"
25 #include "GitStatus.h"
26 #include "UnicodeUtils.h"
27 //#include "GitGlobal.h"
28 //#include "GitHelpers.h"
29 #ifdef _MFC_VER
30 //# include "Git.h"
31 //# include "MessageBox.h"
32 //# include "registry.h"
33 //# include "TGitPath.h"
34 //# include "PathUtils.h"
35 #endif
36 #include "git.h"
37 #include "gitindex.h"
38 #include "shellcache.h"
40 CGitIndexFileMap g_IndexFileMap;
41 CGitHeadFileMap g_HeadFileMap;
42 CGitIgnoreList g_IgnoreList;
44 GitStatus::GitStatus(bool * /*pbCanceled*/)
45 : status(NULL)
47 #if 0
48 m_pool = git_pool_create (NULL);
50 git_error_clear(git_client_create_context(&ctx, m_pool));
52 if (pbCanceled)
54 ctx->cancel_func = cancel;
55 ctx->cancel_baton = pbCanceled;
58 #ifdef _MFC_VER
59 git_error_clear(git_config_ensure(NULL, m_pool));
61 // set up authentication
62 m_prompt.Init(m_pool, ctx);
64 // set up the configuration
65 m_err = git_config_get_config (&(ctx->config), g_pConfigDir, m_pool);
67 if (m_err)
69 ::MessageBox(NULL, this->GetLastErrorMsg(), _T("TortoiseGit"), MB_ICONERROR);
70 git_error_clear(m_err);
71 git_pool_destroy (m_pool); // free the allocated memory
72 exit(-1);
75 // set up the Git_SSH param
76 CString tgit_ssh = CRegString(_T("Software\\TortoiseGit\\SSH"));
77 if (tgit_ssh.IsEmpty())
78 tgit_ssh = CPathUtils::GetAppDirectory() + _T("TortoisePlink.exe");
79 tgit_ssh.Replace('\\', '/');
80 if (!tgit_ssh.IsEmpty())
82 git_config_t * cfg = (git_config_t *)apr_hash_get ((apr_hash_t *)ctx->config, Git_CONFIG_CATEGORY_CONFIG,
83 APR_HASH_KEY_STRING);
84 git_config_set(cfg, Git_CONFIG_SECTION_TUNNELS, "ssh", CUnicodeUtils::GetUTF8(tgit_ssh));
86 #else
87 git_error_clear(git_config_ensure(NULL, m_pool));
89 // set up the configuration
90 m_err = git_config_get_config (&(ctx->config), g_pConfigDir, m_pool);
92 #endif
93 #endif
96 GitStatus::~GitStatus(void)
98 #if 0
99 git_error_clear(m_err);
100 git_pool_destroy (m_pool); // free the allocated memory
101 #endif
104 void GitStatus::ClearPool()
106 #if 0
107 git_pool_clear(m_pool);
108 #endif
111 #ifdef _MFC_VER
112 CString GitStatus::GetLastErrorMsg() const
114 // return Git::GetErrorString(m_err);
115 return CString("");
117 #else
118 stdstring GitStatus::GetLastErrorMsg() const
121 stdstring msg;
122 #if 0
123 char errbuf[256];
125 if (m_err != NULL)
127 git_error_t * ErrPtr = m_err;
128 if (ErrPtr->message)
130 msg = CUnicodeUtils::StdGetUnicode(ErrPtr->message);
132 else
134 /* Is this a Subversion-specific error code? */
135 if ((ErrPtr->apr_err > APR_OS_START_USEERR)
136 && (ErrPtr->apr_err <= APR_OS_START_CANONERR))
137 msg = CUnicodeUtils::StdGetUnicode(git_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)));
138 /* Otherwise, this must be an APR error code. */
139 else
141 git_error_t *temp_err = NULL;
142 const char * err_string = NULL;
143 temp_err = git_utf_cstring_to_utf8(&err_string, apr_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)-1), ErrPtr->pool);
144 if (temp_err)
146 git_error_clear (temp_err);
147 msg = _T("Can't recode error string from APR");
149 else
151 msg = CUnicodeUtils::StdGetUnicode(err_string);
157 while (ErrPtr->child)
159 ErrPtr = ErrPtr->child;
160 msg += _T("\n");
161 if (ErrPtr->message)
163 msg += CUnicodeUtils::StdGetUnicode(ErrPtr->message);
165 else
167 /* Is this a Subversion-specific error code? */
168 if ((ErrPtr->apr_err > APR_OS_START_USEERR)
169 && (ErrPtr->apr_err <= APR_OS_START_CANONERR))
170 msg += CUnicodeUtils::StdGetUnicode(git_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)));
171 /* Otherwise, this must be an APR error code. */
172 else
174 git_error_t *temp_err = NULL;
175 const char * err_string = NULL;
176 temp_err = git_utf_cstring_to_utf8(&err_string, apr_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)-1), ErrPtr->pool);
177 if (temp_err)
179 git_error_clear (temp_err);
180 msg += _T("Can't recode error string from APR");
182 else
184 msg += CUnicodeUtils::StdGetUnicode(err_string);
190 return msg;
191 } // if (m_err != NULL)
192 #endif
193 return msg;
195 #endif
197 // static method
198 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth)
200 git_wc_status_kind statuskind;
201 // git_client_ctx_t * ctx;
203 // apr_pool_t * pool;
204 // git_error_t * err;
205 BOOL err;
206 BOOL isDir;
207 CString sProjectRoot;
209 isDir = path.IsDirectory();
210 if (!path.HasAdminDir(&sProjectRoot))
211 return git_wc_status_none;
213 // pool = git_pool_create (NULL); // create the memory pool
215 // git_error_clear(git_client_create_context(&ctx, pool));
217 // git_revnum_t youngest = Git_INVALID_REVNUM;
218 // git_opt_revision_t rev;
219 // rev.kind = git_opt_revision_unspecified;
220 statuskind = git_wc_status_none;
222 const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source
224 CString sSubPath;
225 CString s = path.GetWinPathString();
226 if (s.GetLength() > sProjectRoot.GetLength())
228 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == _T(':'))
229 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
230 else
231 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
234 bool isfull = ((DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"),
235 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
237 if(isDir)
239 err = GetDirStatus(sProjectRoot,sSubPath,&statuskind, isfull,bIsRecursive,isfull,NULL, NULL);
241 }else
243 err = GetFileStatus(sProjectRoot,sSubPath,&statuskind,isfull, false,isfull, NULL,NULL);
246 return statuskind;
249 // static method
250 git_wc_status_kind GitStatus::GetAllStatusRecursive(const CTGitPath& path)
252 return GetAllStatus(path, git_depth_infinity);
255 // static method
256 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)
258 if (GetStatusRanking(status1) >= GetStatusRanking(status2))
259 return status1;
260 return status2;
262 // static private method
263 int GitStatus::GetStatusRanking(git_wc_status_kind status)
265 switch (status)
267 case git_wc_status_none:
268 return 0;
269 case git_wc_status_unversioned:
270 return 1;
271 case git_wc_status_ignored:
272 return 2;
273 case git_wc_status_incomplete:
274 return 4;
275 case git_wc_status_normal:
276 case git_wc_status_external:
277 return 5;
278 case git_wc_status_added:
279 return 6;
280 case git_wc_status_missing:
281 return 7;
282 case git_wc_status_deleted:
283 return 8;
284 case git_wc_status_replaced:
285 return 9;
286 case git_wc_status_modified:
287 return 10;
288 case git_wc_status_merged:
289 return 11;
290 case git_wc_status_conflicted:
291 return 12;
292 case git_wc_status_obstructed:
293 return 13;
295 return 0;
298 git_revnum_t GitStatus::GetStatus(const CTGitPath& path, bool update /* = false */, bool noignore /* = false */, bool /*noexternals*/ /* = false */)
300 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
301 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
302 // after the call again
304 // apr_hash_t * statushash;
305 // apr_hash_t * exthash;
306 // apr_array_header_t * statusarray;
307 // const sort_item* item;
309 // git_error_clear(m_err);
310 // statushash = apr_hash_make(m_pool);
311 // exthash = apr_hash_make(m_pool);
312 git_revnum_t youngest = GIT_INVALID_REVNUM;
313 // git_opt_revision_t rev;
314 // rev.kind = git_opt_revision_unspecified;
316 CString sProjectRoot;
317 if ( !path.HasAdminDir(&sProjectRoot) )
318 return youngest;
320 struct hashbaton_t hashbaton;
321 // hashbaton.hash = statushash;
322 // hashbaton.exthash = exthash;
323 hashbaton.pThis = this;
325 bool isfull = ((DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"),
326 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
329 LPCTSTR lpszSubPath = NULL;
330 CString sSubPath;
331 CString s = path.GetWinPathString();
332 if (s.GetLength() > sProjectRoot.GetLength())
334 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
335 lpszSubPath = sSubPath;
336 // skip initial slash if necessary
337 if (*lpszSubPath == _T('\\'))
338 lpszSubPath++;
341 m_status.prop_status = m_status.text_status = git_wc_status_none;
343 if(path.IsDirectory())
345 m_err = GetDirStatus(sProjectRoot,CString(lpszSubPath),&m_status.text_status , isfull, false,!noignore, NULL, NULL);
347 }else
349 m_err = GetFileStatus(sProjectRoot,CString(lpszSubPath),&m_status.text_status ,isfull, false,!noignore, NULL,NULL);
353 // Error present if function is not under version control
354 if (m_err) /*|| (apr_hash_count(statushash) == 0)*/
356 status = NULL;
357 return GIT_INVALID_REVNUM;
360 // Convert the unordered hash to an ordered, sorted array
361 /*statusarray = sort_hash (statushash,
362 sort_compare_items_as_paths,
363 m_pool);*/
365 // only the first entry is needed (no recurse)
366 // item = &APR_ARRAY_IDX (statusarray, 0, const sort_item);
368 // status = (git_wc_status2_t *) item->value;
369 status = &m_status;
371 if (update)
373 // done to match TSVN functionality of this function (not sure if any code uses the reutrn val)
374 // if TGit does not need this, then change the return type of function
375 youngest = g_Git.GetHash(_T("HEAD"));
378 return youngest;
381 git_wc_status2_t * GitStatus::GetFirstFileStatus(const CTGitPath& /*path*/, CTGitPath& /*retPath*/, bool /*update*/, git_depth_t /*depth*/, bool /*bNoIgnore*/ /* = true */, bool /*bNoExternals*/ /* = false */)
383 static git_wc_status2 st;
385 m_fileCache.Reset();
387 m_fileCache.Init( CStringA( path.GetWinPathString().GetString() ) );
388 MessageBox(NULL, path.GetWinPathString(), _T("GetFirstFile"), MB_OK);
389 m_fileCache.m_pFileIter = m_fileCache.m_pFiles;
390 st.text_status = git_wc_status_none;
392 if (m_fileCache.m_pFileIter)
394 switch(m_fileCache.m_pFileIter->nStatus)
396 case WGFS_Normal: st.text_status = git_wc_status_normal; break;
397 case WGFS_Modified: st.text_status = git_wc_status_modified; break;
398 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;
401 //retPath.SetFromGit((const char*)item->key);
403 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;
406 return &st;
408 #if 0
409 const sort_item* item;
411 git_error_clear(m_err);
412 m_statushash = apr_hash_make(m_pool);
413 m_externalhash = apr_hash_make(m_pool);
414 headrev = Git_INVALID_REVNUM;
415 git_opt_revision_t rev;
416 rev.kind = git_opt_revision_unspecified;
417 struct hashbaton_t hashbaton;
418 hashbaton.hash = m_statushash;
419 hashbaton.exthash = m_externalhash;
420 hashbaton.pThis = this;
421 m_statushashindex = 0;
422 m_err = git_client_status4 (&headrev,
423 path.GetGitApiPath(m_pool),
424 &rev,
425 getstatushash,
426 &hashbaton,
427 depth,
428 TRUE, //getall
429 update, //update
430 bNoIgnore, //noignore
431 bNoExternals, //noexternals
432 NULL,
433 ctx,
434 m_pool);
437 // Error present if function is not under version control
438 if ((m_err != NULL) || (apr_hash_count(m_statushash) == 0))
440 return NULL;
443 // Convert the unordered hash to an ordered, sorted array
444 m_statusarray = sort_hash (m_statushash,
445 sort_compare_items_as_paths,
446 m_pool);
448 // only the first entry is needed (no recurse)
449 m_statushashindex = 0;
450 item = &APR_ARRAY_IDX (m_statusarray, m_statushashindex, const sort_item);
451 retPath.SetFromGit((const char*)item->key);
452 return (git_wc_status2_t *) item->value;
453 #endif
455 return 0;
458 unsigned int GitStatus::GetVersionedCount() const
460 // return /**/m_fileCache.GetFileCount();
462 unsigned int count = 0;
463 #if 0
464 const sort_item* item;
465 for (unsigned int i=0; i<apr_hash_count(m_statushash); ++i)
467 item = &APR_ARRAY_IDX(m_statusarray, i, const sort_item);
468 if (item)
470 if (GitStatus::GetMoreImportant(((git_wc_status_t *)item->value)->text_status, git_wc_status_ignored)!=git_wc_status_ignored)
471 count++;
474 #endif
475 return count;
478 git_wc_status2_t * GitStatus::GetNextFileStatus(CTGitPath& /*retPath*/)
480 static git_wc_status2 st;
482 st.text_status = git_wc_status_none;
484 /*if (m_fileCache.m_pFileIter)
486 switch(m_fileCache.m_pFileIter->nStatus)
488 case WGFS_Normal: st.text_status = git_wc_status_normal; break;
489 case WGFS_Modified: st.text_status = git_wc_status_modified; break;
490 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;
493 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;
496 return &st;
498 #if 0
499 const sort_item* item;
501 if ((m_statushashindex+1) >= apr_hash_count(m_statushash))
502 return NULL;
503 m_statushashindex++;
505 item = &APR_ARRAY_IDX (m_statusarray, m_statushashindex, const sort_item);
506 retPath.SetFromGit((const char*)item->key);
507 return (git_wc_status2_t *) item->value;
508 #endif
509 return 0;
512 bool GitStatus::IsExternal(const CTGitPath& /*path*/) const
514 #if 0
515 if (apr_hash_get(m_externalhash, path.GetGitApiPath(m_pool), APR_HASH_KEY_STRING))
516 return true;
517 #endif
518 return false;
521 bool GitStatus::IsInExternal(const CTGitPath& /*path*/) const
523 #if 0
524 if (apr_hash_count(m_statushash) == 0)
525 return false;
527 GitPool localpool(m_pool);
528 apr_hash_index_t *hi;
529 const char* key;
530 for (hi = apr_hash_first(localpool, m_externalhash); hi; hi = apr_hash_next(hi))
532 apr_hash_this(hi, (const void**)&key, NULL, NULL);
533 if (key)
535 if (CTGitPath(CUnicodeUtils::GetUnicode(key)).IsAncestorOf(path))
536 return true;
539 #endif
540 return false;
544 void GitStatus::GetStatusString(git_wc_status_kind status, size_t buflen, TCHAR * string)
546 TCHAR * buf;
547 switch (status)
549 case git_wc_status_none:
550 buf = _T("none\0");
551 break;
552 case git_wc_status_unversioned:
553 buf = _T("unversioned\0");
554 break;
555 case git_wc_status_normal:
556 buf = _T("normal\0");
557 break;
558 case git_wc_status_added:
559 buf = _T("added\0");
560 break;
561 case git_wc_status_missing:
562 buf = _T("missing\0");
563 break;
564 case git_wc_status_deleted:
565 buf = _T("deleted\0");
566 break;
567 case git_wc_status_replaced:
568 buf = _T("replaced\0");
569 break;
570 case git_wc_status_modified:
571 buf = _T("modified\0");
572 break;
573 case git_wc_status_merged:
574 buf = _T("merged\0");
575 break;
576 case git_wc_status_conflicted:
577 buf = _T("conflicted\0");
578 break;
579 case git_wc_status_obstructed:
580 buf = _T("obstructed\0");
581 break;
582 case git_wc_status_ignored:
583 buf = _T("ignored");
584 break;
585 case git_wc_status_external:
586 buf = _T("external");
587 break;
588 case git_wc_status_incomplete:
589 buf = _T("incomplete\0");
590 break;
591 default:
592 buf = _T("\0");
593 break;
595 _stprintf_s(string, buflen, _T("%s"), buf);
598 void GitStatus::GetStatusString(HINSTANCE hInst, git_wc_status_kind status, TCHAR * string, int size, WORD lang)
600 switch (status)
602 case git_wc_status_none:
603 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
604 break;
605 case git_wc_status_unversioned:
606 LoadStringEx(hInst, IDS_STATUSUNVERSIONED, string, size, lang);
607 break;
608 case git_wc_status_normal:
609 LoadStringEx(hInst, IDS_STATUSNORMAL, string, size, lang);
610 break;
611 case git_wc_status_added:
612 LoadStringEx(hInst, IDS_STATUSADDED, string, size, lang);
613 break;
614 case git_wc_status_missing:
615 LoadStringEx(hInst, IDS_STATUSABSENT, string, size, lang);
616 break;
617 case git_wc_status_deleted:
618 LoadStringEx(hInst, IDS_STATUSDELETED, string, size, lang);
619 break;
620 case git_wc_status_replaced:
621 LoadStringEx(hInst, IDS_STATUSREPLACED, string, size, lang);
622 break;
623 case git_wc_status_modified:
624 LoadStringEx(hInst, IDS_STATUSMODIFIED, string, size, lang);
625 break;
626 case git_wc_status_merged:
627 LoadStringEx(hInst, IDS_STATUSMERGED, string, size, lang);
628 break;
629 case git_wc_status_conflicted:
630 LoadStringEx(hInst, IDS_STATUSCONFLICTED, string, size, lang);
631 break;
632 case git_wc_status_ignored:
633 LoadStringEx(hInst, IDS_STATUSIGNORED, string, size, lang);
634 break;
635 case git_wc_status_obstructed:
636 LoadStringEx(hInst, IDS_STATUSOBSTRUCTED, string, size, lang);
637 break;
638 case git_wc_status_external:
639 LoadStringEx(hInst, IDS_STATUSEXTERNAL, string, size, lang);
640 break;
641 case git_wc_status_incomplete:
642 LoadStringEx(hInst, IDS_STATUSINCOMPLETE, string, size, lang);
643 break;
644 default:
645 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
646 break;
650 #ifdef _MFC_VER
651 CString GitStatus::GetDepthString(git_depth_t depth)
653 #if 0
654 CString sDepth;
655 switch (depth)
657 case git_depth_unknown:
658 sDepth.LoadString(IDS_Git_DEPTH_UNKNOWN);
659 break;
660 case git_depth_empty:
661 sDepth.LoadString(IDS_Git_DEPTH_EMPTY);
662 break;
663 case git_depth_files:
664 sDepth.LoadString(IDS_Git_DEPTH_FILES);
665 break;
666 case git_depth_immediates:
667 sDepth.LoadString(IDS_Git_DEPTH_IMMEDIATE);
668 break;
669 case git_depth_infinity:
670 sDepth.LoadString(IDS_Git_DEPTH_INFINITE);
671 break;
673 return sDepth;
674 #endif
675 return CString("");
677 #endif
679 void GitStatus::GetDepthString(HINSTANCE /*hInst*/, git_depth_t /*depth*/, TCHAR * /*string*/, int /*size*/, WORD /*lang*/)
681 #if 0
682 switch (depth)
684 case git_depth_unknown:
685 LoadStringEx(hInst, IDS_SVN_DEPTH_UNKNOWN, string, size, lang);
686 break;
687 case git_depth_empty:
688 LoadStringEx(hInst, IDS_SVN_DEPTH_EMPTY, string, size, lang);
689 break;
690 case git_depth_files:
691 LoadStringEx(hInst, IDS_SVN_DEPTH_FILES, string, size, lang);
692 break;
693 case git_depth_immediates:
694 LoadStringEx(hInst, IDS_SVN_DEPTH_IMMEDIATE, string, size, lang);
695 break;
696 case git_depth_infinity:
697 LoadStringEx(hInst, IDS_SVN_DEPTH_INFINITE, string, size, lang);
698 break;
700 #endif
704 int GitStatus::LoadStringEx(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax, WORD wLanguage)
706 const STRINGRESOURCEIMAGE* pImage;
707 const STRINGRESOURCEIMAGE* pImageEnd;
708 ULONG nResourceSize;
709 HGLOBAL hGlobal;
710 UINT iIndex;
711 int ret;
713 HRSRC hResource = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE(((uID>>4)+1)), wLanguage);
714 if (!hResource)
716 // try the default language before giving up!
717 hResource = FindResource(hInstance, MAKEINTRESOURCE(((uID>>4)+1)), RT_STRING);
718 if (!hResource)
719 return 0;
721 hGlobal = LoadResource(hInstance, hResource);
722 if (!hGlobal)
723 return 0;
724 pImage = (const STRINGRESOURCEIMAGE*)::LockResource(hGlobal);
725 if(!pImage)
726 return 0;
728 nResourceSize = ::SizeofResource(hInstance, hResource);
729 pImageEnd = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+nResourceSize);
730 iIndex = uID&0x000f;
732 while ((iIndex > 0) && (pImage < pImageEnd))
734 pImage = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+(sizeof(STRINGRESOURCEIMAGE)+(pImage->nLength*sizeof(WCHAR))));
735 iIndex--;
737 if (pImage >= pImageEnd)
738 return 0;
739 if (pImage->nLength == 0)
740 return 0;
742 ret = pImage->nLength;
743 if (pImage->nLength > nBufferMax)
745 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength-1);
746 lpBuffer[nBufferMax-1] = 0;
748 else
750 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength);
751 lpBuffer[ret] = 0;
753 return ret;
756 BOOL GitStatus::getallstatus(const struct wgFile_s *pFile, void *pUserData)
758 git_wc_status_kind * s = (git_wc_status_kind *)pUserData;
759 *s = GitStatus::GetMoreImportant(*s, GitStatusFromWingit(pFile->nStatus));
760 return FALSE;
763 BOOL GitStatus::getstatus(const struct wgFile_s *pFile, void *pUserData)
765 git_wc_status2_t * s = (git_wc_status2_t*)pUserData;
766 s->prop_status = s->text_status = GitStatus::GetMoreImportant(s->prop_status, GitStatusFromWingit(pFile->nStatus));
767 return FALSE;
770 #if 0
771 git_error_t * GitStatus::getallstatus(void * baton, const char * /*path*/, git_wc_status2_t * status, apr_pool_t * /*pool*/)
773 git_wc_status_kind * s = (git_wc_status_kind *)baton;
774 *s = GitStatus::GetMoreImportant(*s, status->text_status);
775 *s = GitStatus::GetMoreImportant(*s, status->prop_status);
776 return Git_NO_ERROR;
778 #endif
780 #if 0
781 git_error_t * GitStatus::getstatushash(void * baton, const char * path, git_wc_status2_t * status, apr_pool_t * /*pool*/)
783 hashbaton_t * hash = (hashbaton_t *)baton;
784 const StdStrAVector& filterList = hash->pThis->m_filterFileList;
785 if (status->text_status == git_wc_status_external)
787 apr_hash_set (hash->exthash, apr_pstrdup(hash->pThis->m_pool, path), APR_HASH_KEY_STRING, (const void*)1);
788 return Git_NO_ERROR;
790 if(filterList.size() > 0)
792 // We have a filter active - we're only interested in files which are in
793 // the filter
794 if(!binary_search(filterList.begin(), filterList.end(), path))
796 // This item is not in the filter - don't store it
797 return Git_NO_ERROR;
800 git_wc_status2_t * statuscopy = git_wc_dup_status2 (status, hash->pThis->m_pool);
801 apr_hash_set (hash->hash, apr_pstrdup(hash->pThis->m_pool, path), APR_HASH_KEY_STRING, statuscopy);
802 return Git_NO_ERROR;
805 apr_array_header_t * GitStatus::sort_hash (apr_hash_t *ht,
806 int (*comparison_func) (const GitStatus::sort_item *, const GitStatus::sort_item *),
807 apr_pool_t *pool)
809 apr_hash_index_t *hi;
810 apr_array_header_t *ary;
812 /* allocate an array with only one element to begin with. */
813 ary = apr_array_make (pool, 1, sizeof(sort_item));
815 /* loop over hash table and push all keys into the array */
816 for (hi = apr_hash_first (pool, ht); hi; hi = apr_hash_next (hi))
818 sort_item *item = (sort_item*)apr_array_push (ary);
820 apr_hash_this (hi, &item->key, &item->klen, &item->value);
823 /* now quick sort the array. */
824 qsort (ary->elts, ary->nelts, ary->elt_size,
825 (int (*)(const void *, const void *))comparison_func);
827 return ary;
830 int GitStatus::sort_compare_items_as_paths (const sort_item *a, const sort_item *b)
832 const char *astr, *bstr;
834 astr = (const char*)a->key;
835 bstr = (const char*)b->key;
836 return git_path_compare_paths (astr, bstr);
838 #endif
840 git_error_t* GitStatus::cancel(void * /*baton*/)
842 #if 0
843 volatile bool * canceled = (bool *)baton;
844 if (*canceled)
846 CString temp;
847 temp.LoadString(IDS_Git_USERCANCELLED);
848 return git_error_create(Git_ERR_CANCELLED, NULL, CUnicodeUtils::GetUTF8(temp));
850 return Git_NO_ERROR;
851 #endif
852 return 0;
855 #ifdef _MFC_VER
857 // Set-up a filter to restrict the files which will have their status stored by a get-status
858 void GitStatus::SetFilter(const CTGitPathList& fileList)
860 m_filterFileList.clear();
861 for(int fileIndex = 0; fileIndex < fileList.GetCount(); fileIndex++)
863 // m_filterFileList.push_back(fileList[fileIndex].GetGitApiPath(m_pool));
865 // Sort the list so that we can do binary searches
866 std::sort(m_filterFileList.begin(), m_filterFileList.end());
869 void GitStatus::ClearFilter()
871 m_filterFileList.clear();
874 #endif // _MFC_VER
876 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
878 int GitStatus::GetFileStatus(const CString &gitdir,const CString &pathParam,git_wc_status_kind * status,BOOL IsFull, BOOL /*IsRecursive*/,BOOL IsIgnore, FIll_STATUS_CALLBACK callback,void *pData)
882 CString path = pathParam;
884 TCHAR oldpath[MAX_PATH+1];
885 memset(oldpath,0,MAX_PATH+1);
887 path.Replace(_T('\\'),_T('/'));
889 CString lowcasepath =path;
890 lowcasepath.MakeLower();
892 if(status)
894 git_wc_status_kind st = git_wc_status_none;
895 CGitHash hash;
897 g_IndexFileMap.GetFileStatus(gitdir,path,&st,IsFull,false,callback,pData,&hash);
899 if( st == git_wc_status_conflicted )
901 *status =st;
902 if(callback)
903 callback(gitdir+_T("/")+path,st,false,pData);
904 return 0;
907 if( st == git_wc_status_unversioned )
909 if(!IsIgnore)
911 *status = git_wc_status_unversioned;
912 if(callback)
913 callback(gitdir+_T("/")+path, *status, false,pData);
914 return 0;
917 if( g_IgnoreList.CheckIgnoreChanged(gitdir,path))
919 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
921 if( g_IgnoreList.IsIgnore(path, gitdir) )
923 st = git_wc_status_ignored;
925 *status = st;
926 if(callback)
927 callback(gitdir+_T("/")+path,st, false, pData);
929 return 0;
932 if( st == git_wc_status_normal && IsFull)
935 g_HeadFileMap.CheckHeadUpdate(gitdir);
936 bool b=false;
938 SHARED_TREE_PTR treeptr;
940 treeptr=g_HeadFileMap.SafeGet(gitdir);
942 b = treeptr->m_Head != treeptr->m_TreeHash;
944 if(b)
946 treeptr->ReadHeadHash(gitdir);
948 // Init Repository
949 if( treeptr->m_HeadFile.IsEmpty() )
951 *status =st=git_wc_status_added;
952 if(callback)
953 callback(gitdir+_T("/")+path,st,false,pData);
954 return 0;
956 if(treeptr->ReadTree())
958 treeptr->m_LastModifyTimeHead = 0;
959 //Check if init repository
960 *status = treeptr->m_Head.IsEmpty()? git_wc_status_added: st;
961 if(callback)
962 callback(gitdir+_T("/")+path,*status,false,pData);
963 return 0;
965 g_HeadFileMap.SafeSet(gitdir, treeptr);
968 // Check Head Tree Hash;
970 //add item
972 int start =SearchInSortVector(*treeptr,lowcasepath.GetBuffer(),-1);
974 if(start<0)
976 *status =st=git_wc_status_added;
977 ATLTRACE(_T("File miss in head tree %s"), path);
978 if(callback)
979 callback(gitdir+_T("/")+path,st,false, pData);
980 return 0;
983 //staged and not commit
984 if( treeptr->at(start).m_Hash != hash )
986 *status =st=git_wc_status_modified;
987 if(callback)
988 callback(gitdir+_T("/")+path,st, false, pData);
989 return 0;
994 *status =st;
995 if(callback)
996 callback(gitdir+_T("/")+path,st,false, pData);
997 return 0;
1000 catch(...)
1002 if(status)
1003 *status = git_wc_status_none;
1004 return -1;
1007 return 0;
1011 int GitStatus::GetHeadHash(const CString &gitdir, CGitHash &hash)
1013 return g_HeadFileMap.GetHeadHash(gitdir, hash);
1016 bool GitStatus::IsGitReposChanged(const CString &gitdir,const CString &subpaths, int mode)
1018 if( mode & GIT_MODE_INDEX)
1020 return g_IndexFileMap.CheckAndUpdate(gitdir, true);
1023 if( mode & GIT_MODE_HEAD)
1025 if(g_HeadFileMap.CheckHeadUpdate(gitdir))
1026 return true;
1029 if( mode & GIT_MODE_IGNORE)
1031 if(g_IgnoreList.CheckIgnoreChanged(gitdir,subpaths))
1032 return true;
1034 return false;
1037 int GitStatus::LoadIgnoreFile(const CString &gitdir,const CString &subpaths)
1039 return g_IgnoreList.LoadAllIgnoreFile(gitdir,subpaths);
1041 int GitStatus::IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir,bool *isVersion)
1043 return g_IndexFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion);
1046 __int64 GitStatus::GetIndexFileTime(const CString &gitdir)
1048 SHARED_INDEX_PTR ptr=g_IndexFileMap.SafeGet(gitdir);
1049 if(ptr.get() == NULL)
1050 return 0;
1052 return ptr->m_LastModifyTime;
1055 int GitStatus::GetIgnoreFileChangeTimeList(const CString &dir, std::vector<__int64> &timelist)
1057 return g_IgnoreList.GetIgnoreFileChangeTimeList(dir,timelist);
1059 int GitStatus::IsIgnore(const CString &gitdir, const CString &path, bool *isIgnore)
1061 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,path))
1062 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
1064 *isIgnore = g_IgnoreList.IsIgnore(path,gitdir);
1066 return 0;
1069 static bool SortFileName(CGitFileName &Item1, CGitFileName &Item2)
1071 return Item1.m_FileName.Compare(Item2.m_FileName)<0;
1074 int GitStatus::GetFileList(const CString &gitdir, const CString &subpath, std::vector<CGitFileName> &list)
1076 WIN32_FIND_DATA data;
1077 HANDLE handle=::FindFirstFile(gitdir+_T("\\")+subpath+_T("\\*.*"), &data);
1080 if(_tcscmp(data.cFileName, _T(".git")) == 0)
1081 continue;
1083 if(_tcscmp(data.cFileName, _T(".")) == 0)
1084 continue;
1086 if(_tcscmp(data.cFileName, _T("..")) == 0)
1087 continue;
1089 CGitFileName filename;
1091 filename.m_CaseFileName = filename.m_FileName = data.cFileName;
1092 filename.m_FileName.MakeLower();
1094 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1096 filename.m_FileName+=_T('/');
1099 list.push_back(filename);
1101 }while(::FindNextFile(handle, &data));
1103 std::sort(list.begin(), list.end(), SortFileName);
1104 return 0;
1107 int GitStatus::EnumDirStatus(const CString &gitdir,const CString &subpath,git_wc_status_kind * status,BOOL IsFul, BOOL IsRecursive, BOOL IsIgnore, FIll_STATUS_CALLBACK callback, void *pData)
1111 TCHAR oldpath[MAX_PATH+1];
1112 memset(oldpath,0,MAX_PATH+1);
1114 CString path =subpath;
1116 path.Replace(_T('\\'),_T('/'));
1117 if(!path.IsEmpty())
1118 if(path[path.GetLength()-1] != _T('/'))
1119 path += _T('/'); //Add trail / to show it is directory, not file name.
1121 CString lowcasepath = path;
1122 lowcasepath.MakeLower();
1124 std::vector<CGitFileName> filelist;
1125 GetFileList(gitdir, subpath, filelist);
1127 if(status)
1129 g_IndexFileMap.CheckAndUpdate(gitdir,true);
1131 if(g_HeadFileMap.CheckHeadUpdate(gitdir) || g_HeadFileMap.IsHashChanged(gitdir))
1133 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
1134 treeptr->ReadHeadHash(gitdir);
1135 if(!treeptr->ReadTree())
1137 g_HeadFileMap.SafeSet(gitdir, treeptr);
1141 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
1142 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
1144 if( indexptr.get() == NULL)
1145 return -1;
1147 std::vector<CGitFileName>::iterator it;
1148 CString onepath;
1149 CString casepath;
1150 for(it = filelist.begin(); it<filelist.end();it++)
1152 casepath=onepath = path;
1153 onepath += it->m_FileName;
1154 casepath += it->m_CaseFileName;
1156 int pos = SearchInSortVector(*indexptr, onepath.GetBuffer(), onepath.GetLength());
1157 int posintree = SearchInSortVector(*treeptr, onepath.GetBuffer(), onepath.GetLength());
1159 bool bIsDir =false;
1160 if(onepath.GetLength()>0 && onepath[onepath.GetLength()-1] == _T('/'))
1161 bIsDir =true;
1163 if(pos <0 && posintree<0)
1165 if(onepath.GetLength() ==0)
1166 continue;
1168 if(bIsDir) /*check if it is directory*/
1170 if(::PathFileExists(gitdir+onepath+_T("/.git")))
1171 { /* That is git submodule */
1172 *status = git_wc_status_unknown;
1173 if(callback)
1174 callback(gitdir+_T("/")+casepath, *status, bIsDir, pData);
1175 continue;
1179 if(!IsIgnore)
1181 *status = git_wc_status_unversioned;
1182 if(callback)
1183 callback(gitdir+_T("/")+casepath, *status, bIsDir,pData);
1184 continue;
1187 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,casepath))
1188 g_IgnoreList.LoadAllIgnoreFile(gitdir,casepath);
1190 if(g_IgnoreList.IsIgnore(casepath,gitdir))
1191 *status = git_wc_status_ignored;
1192 else
1193 *status = git_wc_status_unversioned;
1195 if(callback)
1196 callback(gitdir+_T("/")+casepath, *status, bIsDir,pData);
1198 }else if(pos <0 && posintree>=0) /* check if file delete in index */
1200 *status = git_wc_status_deleted;
1201 if(callback)
1202 callback(gitdir+_T("/")+casepath, *status, bIsDir,pData);
1204 }else if(pos >=0 && posintree <0) /* Check if file added */
1206 *status = git_wc_status_added;
1207 if(callback)
1208 callback(gitdir+_T("/")+casepath, *status, bIsDir,pData);
1209 }else
1211 if(onepath.GetLength() ==0)
1212 continue;
1214 if(onepath[onepath.GetLength()-1] == _T('/'))
1216 *status = git_wc_status_normal;
1217 if(callback)
1218 callback(gitdir+_T("/")+casepath, *status, bIsDir,pData);
1220 else
1222 git_wc_status_kind filestatus;
1223 GetFileStatus(gitdir,casepath, &filestatus,IsFul, IsRecursive,IsIgnore, callback,pData);
1227 }/*End of For*/
1229 /* Check deleted file in system */
1230 int start=0, end=0;
1231 int pos=SearchInSortVector(*indexptr, lowcasepath.GetBuffer(), lowcasepath.GetLength());
1232 GetRangeInSortVector(*indexptr,lowcasepath.GetBuffer(),lowcasepath.GetLength(),&start,&end,pos);
1235 CGitIndexList::iterator it;
1236 CString oldstring;
1238 for(it = indexptr->begin()+start; it <= indexptr->begin()+end; it++)
1240 int start = lowcasepath.GetLength();
1241 int index = (*it).m_FileName.Find(_T('/'), start);
1242 if(index<0)
1243 index = (*it).m_FileName.GetLength();
1245 CString filename = (*it).m_FileName.Mid(start, index);
1246 if(oldstring != filename)
1248 oldstring = filename;
1249 if(SearchInSortVector(filelist, filename.GetBuffer(), filename.GetLength())<0)
1251 *status = git_wc_status_deleted;
1252 if(callback)
1253 callback(gitdir+_T("/")+filename, *status, false,pData);
1259 start = end =0;
1261 if(GetRangeInSortVector(*treeptr,lowcasepath.GetBuffer(),lowcasepath.GetLength(),&start,&end,pos) == 0)
1263 CGitHeadFileList::iterator it;
1264 CString oldstring;
1266 for(it = treeptr->begin()+start; it <= treeptr->begin()+end; it++)
1268 int start = lowcasepath.GetLength();
1269 int index = (*it).m_FileName.Find(_T('/'), start);
1270 if(index<0)
1271 index = (*it).m_FileName.GetLength();
1273 CString filename = (*it).m_FileName.Mid(start, index);
1274 if(oldstring != filename)
1276 oldstring = filename;
1277 if(SearchInSortVector(filelist, filename.GetBuffer(), filename.GetLength())<0)
1279 *status = git_wc_status_deleted;
1280 if(callback)
1281 callback(gitdir+_T("/")+filename, *status, false,pData);
1287 }/*End of if status*/
1288 }catch(...)
1290 return -1;
1292 return 0;
1295 int GitStatus::GetDirStatus(const CString &gitdir,const CString &subpath,git_wc_status_kind * status,BOOL IsFul, BOOL IsRecursive,BOOL IsIgnore,FIll_STATUS_CALLBACK callback,void *pData)
1299 TCHAR oldpath[MAX_PATH+1];
1300 memset(oldpath,0,MAX_PATH+1);
1302 CString path =subpath;
1304 path.Replace(_T('\\'),_T('/'));
1305 if(!path.IsEmpty())
1306 if(path[path.GetLength()-1] != _T('/'))
1307 path += _T('/'); //Add trail / to show it is directory, not file name.
1309 CString lowcasepath = path;
1310 lowcasepath.MakeLower();
1312 if(status)
1314 g_IndexFileMap.CheckAndUpdate(gitdir, true);
1316 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
1318 int pos=SearchInSortVector(*indexptr,lowcasepath.GetBuffer(),lowcasepath.GetLength());
1320 if(subpath.IsEmpty() && pos<0)
1321 { // for new init repository
1322 *status = git_wc_status_normal;
1323 if(callback)
1324 callback(gitdir+_T("/")+path, *status, false,pData);
1325 return 0;
1328 //Not In Version Contorl
1329 if(pos<0)
1331 if(!IsIgnore)
1333 *status = git_wc_status_unversioned;
1334 if(callback)
1335 callback(gitdir+_T("/")+path, *status, false,pData);
1336 return 0;
1338 //Check ignore always.
1340 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,path))
1341 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
1343 if(g_IgnoreList.IsIgnore(path,gitdir))
1344 *status = git_wc_status_ignored;
1345 else
1346 *status = git_wc_status_unversioned;
1348 g_HeadFileMap.CheckHeadUpdate(gitdir);
1350 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
1351 //Check init repository
1352 if(treeptr->m_Head.IsEmpty() && path.IsEmpty())
1353 *status = git_wc_status_normal;
1356 }else // In version control
1358 *status = git_wc_status_normal;
1360 int start=0;
1361 int end=0;
1362 if(path.IsEmpty())
1364 start=0;
1365 end=indexptr->size()-1;
1367 GetRangeInSortVector(*indexptr,lowcasepath.GetBuffer(),lowcasepath.GetLength(),&start,&end,pos);
1368 CGitIndexList::iterator it;
1370 it = indexptr->begin()+start;
1372 // Check Conflict;
1373 for(int i=start;i<=end;i++)
1375 if( ((*it).m_Flags & CE_STAGEMASK) !=0)
1377 *status = git_wc_status_conflicted;
1378 if(callback)
1380 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
1381 if(dirpos<0 || IsRecursive)
1382 callback(gitdir+_T("\\")+ it->m_FileName,git_wc_status_conflicted,false,pData);
1383 }else
1384 break;
1386 it++;
1389 if( IsFul && (*status != git_wc_status_conflicted))
1391 *status = git_wc_status_normal;
1393 if(g_HeadFileMap.CheckHeadUpdate(gitdir))
1395 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
1397 treeptr->ReadHeadHash(gitdir);
1399 if(treeptr->ReadTree())
1401 g_HeadFileMap.SafeSet(gitdir, treeptr);
1404 //Check Add
1405 it = indexptr->begin()+start;
1409 //Check if new init repository
1410 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
1412 if( treeptr->size() > 0 || treeptr->m_Head.IsEmpty() )
1414 for(int i=start;i<=end;i++)
1416 pos =SearchInSortVector(*treeptr, (*it).m_FileName.GetBuffer(), -1);
1418 if(pos < 0)
1420 *status = *status = max(git_wc_status_added, *status) ;
1421 if(callback)
1423 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
1424 if(dirpos<0 || IsRecursive)
1425 callback(gitdir+_T("\\")+ it->m_FileName,git_wc_status_added,false, pData);
1427 }else
1428 break;
1431 if( pos>=0 && treeptr->at(pos).m_Hash != (*it).m_IndexHash)
1433 *status = *status = max(git_wc_status_modified, *status) ;
1434 if(callback)
1436 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
1437 if(dirpos<0 || IsRecursive)
1438 callback(gitdir+_T("\\")+ it->m_FileName, git_wc_status_modified,false, pData);
1440 }else
1441 break;
1444 it++;
1447 //Check Delete
1448 if( *status == git_wc_status_normal )
1450 pos = SearchInSortVector(*treeptr, lowcasepath.GetBuffer(), lowcasepath.GetLength());
1451 if(pos <0)
1453 *status = git_wc_status_added;
1455 }else
1457 int hstart,hend;
1458 GetRangeInSortVector(*treeptr,lowcasepath.GetBuffer(),lowcasepath.GetLength(),&hstart,&hend,pos);
1459 CGitHeadFileList::iterator hit;
1460 hit = treeptr->begin()+start;
1461 for(int i=hstart;i<=hend;i++)
1463 if( SearchInSortVector(*indexptr,(*hit).m_FileName.GetBuffer(),-1) < 0)
1465 *status = git_wc_status_deleted;
1466 break;
1468 hit++;
1473 }/* End lock*/
1475 // If define callback, it need update each file status.
1476 // If not define callback, status == git_wc_status_conflicted, needn't check each file status
1477 // because git_wc_status_conflicted is highest.s
1478 if(callback || (*status != git_wc_status_conflicted))
1480 //Check File Time;
1481 //if(IsRecursive)
1483 CString sub, currentPath;
1484 it = indexptr->begin()+start;
1485 for(int i=start;i<=end;i++,it++)
1487 if( !IsRecursive )
1489 //skip child directory
1490 int pos = (*it).m_FileName.Find(_T('/'), path.GetLength());
1492 if( pos > 0)
1494 currentPath = (*it).m_FileName.Left(pos);
1495 if( callback && (sub != currentPath) )
1497 sub = currentPath;
1498 ATLTRACE(_T("index subdir %s\n"),sub);
1499 if(callback) callback(gitdir + _T("\\")+sub,
1500 git_wc_status_normal,true, pData);
1502 continue;
1506 git_wc_status_kind filestatus = git_wc_status_none;
1508 GetFileStatus(gitdir,(*it).m_FileName, &filestatus,IsFul, IsRecursive,IsIgnore, callback,pData);
1510 *status = max(filestatus, *status) ;
1516 if(callback) callback(gitdir+_T("/")+subpath,*status,true, pData);
1519 }catch(...)
1521 if(status)
1522 *status = git_wc_status_none;
1523 return -1;
1526 return 0;
1529 bool GitStatus::IsExistIndexLockFile(const CString &gitdir)
1531 CString sDirName= gitdir;
1533 for (;;)
1535 if(PathFileExists(sDirName + _T("\\.git")))
1537 if(PathFileExists(sDirName + _T("\\.git\\index.lock")))
1538 return true;
1539 else
1540 return false;
1543 int x = sDirName.ReverseFind(_T('\\'));
1544 if (x < 2)
1545 return false;
1547 sDirName = sDirName.Left(x);
1550 return false;