TGitCache test application basic work.
[TortoiseGit.git] / src / Git / GitStatus.cpp
blob1b626535cca9b359037fe29e05339b1eb9c6a405
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - 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 #ifdef _TORTOISESHELL
22 #include "ShellExt.h"
23 #else
24 #include "registry.h"
25 #endif
26 //#include "resource.h"
27 #include "..\TortoiseShell\resource.h"
28 //#include "git_config.h"
29 #include "GitStatus.h"
30 #include "UnicodeUtils.h"
31 //#include "GitGlobal.h"
32 //#include "GitHelpers.h"
33 #ifdef _MFC_VER
34 //# include "Git.h"
35 //# include "MessageBox.h"
36 //# include "registry.h"
37 //# include "TGitPath.h"
38 //# include "PathUtils.h"
39 #endif
40 #include "git.h"
41 #include "gitindex.h"
43 CGitIndexFileMap g_IndexFileMap;
44 CGitHeadFileMap g_HeadFileMap;
45 CGitIgnoreList g_IgnoreList;
47 GitStatus::GitStatus(bool * pbCanceled)
48 : status(NULL)
50 #if 0
51 m_pool = git_pool_create (NULL);
53 git_error_clear(git_client_create_context(&ctx, m_pool));
55 if (pbCanceled)
57 ctx->cancel_func = cancel;
58 ctx->cancel_baton = pbCanceled;
61 #ifdef _MFC_VER
62 git_error_clear(git_config_ensure(NULL, m_pool));
64 // set up authentication
65 m_prompt.Init(m_pool, ctx);
67 // set up the configuration
68 m_err = git_config_get_config (&(ctx->config), g_pConfigDir, m_pool);
70 if (m_err)
72 ::MessageBox(NULL, this->GetLastErrorMsg(), _T("TortoiseGit"), MB_ICONERROR);
73 git_error_clear(m_err);
74 git_pool_destroy (m_pool); // free the allocated memory
75 exit(-1);
78 // set up the Git_SSH param
79 CString tgit_ssh = CRegString(_T("Software\\TortoiseGit\\SSH"));
80 if (tgit_ssh.IsEmpty())
81 tgit_ssh = CPathUtils::GetAppDirectory() + _T("TortoisePlink.exe");
82 tgit_ssh.Replace('\\', '/');
83 if (!tgit_ssh.IsEmpty())
85 git_config_t * cfg = (git_config_t *)apr_hash_get ((apr_hash_t *)ctx->config, Git_CONFIG_CATEGORY_CONFIG,
86 APR_HASH_KEY_STRING);
87 git_config_set(cfg, Git_CONFIG_SECTION_TUNNELS, "ssh", CUnicodeUtils::GetUTF8(tgit_ssh));
89 #else
90 git_error_clear(git_config_ensure(NULL, m_pool));
92 // set up the configuration
93 m_err = git_config_get_config (&(ctx->config), g_pConfigDir, m_pool);
95 #endif
96 #endif
99 GitStatus::~GitStatus(void)
101 #if 0
102 git_error_clear(m_err);
103 git_pool_destroy (m_pool); // free the allocated memory
104 #endif
107 void GitStatus::ClearPool()
109 #if 0
110 git_pool_clear(m_pool);
111 #endif
114 #ifdef _MFC_VER
115 CString GitStatus::GetLastErrorMsg() const
117 // return Git::GetErrorString(m_err);
118 return CString("");
120 #else
121 stdstring GitStatus::GetLastErrorMsg() const
124 stdstring msg;
125 #if 0
126 char errbuf[256];
128 if (m_err != NULL)
130 git_error_t * ErrPtr = m_err;
131 if (ErrPtr->message)
133 msg = CUnicodeUtils::StdGetUnicode(ErrPtr->message);
135 else
137 /* Is this a Subversion-specific error code? */
138 if ((ErrPtr->apr_err > APR_OS_START_USEERR)
139 && (ErrPtr->apr_err <= APR_OS_START_CANONERR))
140 msg = CUnicodeUtils::StdGetUnicode(git_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)));
141 /* Otherwise, this must be an APR error code. */
142 else
144 git_error_t *temp_err = NULL;
145 const char * err_string = NULL;
146 temp_err = git_utf_cstring_to_utf8(&err_string, apr_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)-1), ErrPtr->pool);
147 if (temp_err)
149 git_error_clear (temp_err);
150 msg = _T("Can't recode error string from APR");
152 else
154 msg = CUnicodeUtils::StdGetUnicode(err_string);
160 while (ErrPtr->child)
162 ErrPtr = ErrPtr->child;
163 msg += _T("\n");
164 if (ErrPtr->message)
166 msg += CUnicodeUtils::StdGetUnicode(ErrPtr->message);
168 else
170 /* Is this a Subversion-specific error code? */
171 if ((ErrPtr->apr_err > APR_OS_START_USEERR)
172 && (ErrPtr->apr_err <= APR_OS_START_CANONERR))
173 msg += CUnicodeUtils::StdGetUnicode(git_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)));
174 /* Otherwise, this must be an APR error code. */
175 else
177 git_error_t *temp_err = NULL;
178 const char * err_string = NULL;
179 temp_err = git_utf_cstring_to_utf8(&err_string, apr_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)-1), ErrPtr->pool);
180 if (temp_err)
182 git_error_clear (temp_err);
183 msg += _T("Can't recode error string from APR");
185 else
187 msg += CUnicodeUtils::StdGetUnicode(err_string);
193 return msg;
194 } // if (m_err != NULL)
195 #endif
196 return msg;
198 #endif
200 // static method
201 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth)
203 git_wc_status_kind statuskind;
204 // git_client_ctx_t * ctx;
206 // apr_pool_t * pool;
207 // git_error_t * err;
208 BOOL err;
209 BOOL isDir;
210 CString sProjectRoot;
212 isDir = path.IsDirectory();
213 if (!path.HasAdminDir(&sProjectRoot))
214 return git_wc_status_none;
216 // pool = git_pool_create (NULL); // create the memory pool
218 // git_error_clear(git_client_create_context(&ctx, pool));
220 // git_revnum_t youngest = Git_INVALID_REVNUM;
221 // git_opt_revision_t rev;
222 // rev.kind = git_opt_revision_unspecified;
223 statuskind = git_wc_status_none;
225 const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source
227 #ifdef _TORTOISESHELL
228 if (g_ShellCache.GetCacheType() == ShellCache::dll)
229 #else
230 if ((DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION) ? 2 : 1) == 2)
231 #endif
233 // gitindex.h based status
235 CString sSubPath;
236 CString s = path.GetWinPathString();
237 if (s.GetLength() > sProjectRoot.GetLength())
239 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == _T(':'))
240 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
241 else
242 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
245 err = g_IndexFileMap.GetFileStatus(sProjectRoot,sSubPath,&statuskind);
247 else
249 LPCTSTR lpszSubPath = NULL;
250 CString sSubPath;
251 CString s = path.GetWinPathString();
252 if (s.GetLength() > sProjectRoot.GetLength())
254 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
255 lpszSubPath = sSubPath;
256 // skip initial slash if necessary
257 if (*lpszSubPath == _T('\\'))
258 lpszSubPath++;
261 #if 1
262 // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here
263 UINT nFlags = WGEFF_SingleFile;
264 if (!bIsRecursive)
265 nFlags |= WGEFF_NoRecurse;
266 if (!lpszSubPath)
267 // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)
268 nFlags |= WGEFF_EmptyAsNormal;
269 #else
270 // enumerate all files, recursively if requested
271 UINT nFlags = 0;
272 if (!bIsRecursive)
273 nFlags |= WGEFF_NoRecurse;
274 #endif
276 //err = !wgEnumFiles(sProjectRoot, lpszSubPath, nFlags, &getallstatus, &statuskind);
278 if(isDir)
280 err = GetDirStatus(sProjectRoot,CString(lpszSubPath),&statuskind, true,bIsRecursive,NULL, NULL);
282 }else
284 err = GetFileStatus(sProjectRoot,CString(lpszSubPath),&statuskind,true, false,NULL,NULL);
288 /*err = git_client_status4 (&youngest,
289 path.GetSVNApiPath(pool),
290 &rev,
291 getallstatus,
292 &statuskind,
293 depth,
294 TRUE, //getall
295 FALSE, //update
296 TRUE, //noignore
297 FALSE, //ignore externals
298 NULL,
299 ctx,
300 pool);*/
303 // Error present
304 if (err != NULL)
306 // git_error_clear(err);
307 // git_pool_destroy (pool); //free allocated memory
308 return git_wc_status_none;
311 // git_pool_destroy (pool); //free allocated memory
313 return statuskind;
316 // static method
317 git_wc_status_kind GitStatus::GetAllStatusRecursive(const CTGitPath& path)
319 return GetAllStatus(path, git_depth_infinity);
322 // static method
323 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)
325 if (GetStatusRanking(status1) >= GetStatusRanking(status2))
326 return status1;
327 return status2;
329 // static private method
330 int GitStatus::GetStatusRanking(git_wc_status_kind status)
332 switch (status)
334 case git_wc_status_none:
335 return 0;
336 case git_wc_status_unversioned:
337 return 1;
338 case git_wc_status_ignored:
339 return 2;
340 case git_wc_status_incomplete:
341 return 4;
342 case git_wc_status_normal:
343 case git_wc_status_external:
344 return 5;
345 case git_wc_status_added:
346 return 6;
347 case git_wc_status_missing:
348 return 7;
349 case git_wc_status_deleted:
350 return 8;
351 case git_wc_status_replaced:
352 return 9;
353 case git_wc_status_modified:
354 return 10;
355 case git_wc_status_merged:
356 return 11;
357 case git_wc_status_conflicted:
358 return 12;
359 case git_wc_status_obstructed:
360 return 13;
362 return 0;
365 git_revnum_t GitStatus::GetStatus(const CTGitPath& path, bool update /* = false */, bool noignore /* = false */, bool noexternals /* = false */)
367 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
368 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
369 // after the call again
371 // apr_hash_t * statushash;
372 // apr_hash_t * exthash;
373 // apr_array_header_t * statusarray;
374 // const sort_item* item;
376 // git_error_clear(m_err);
377 // statushash = apr_hash_make(m_pool);
378 // exthash = apr_hash_make(m_pool);
379 git_revnum_t youngest = GIT_INVALID_REVNUM;
380 // git_opt_revision_t rev;
381 // rev.kind = git_opt_revision_unspecified;
383 CString sProjectRoot;
384 if ( !path.HasAdminDir(&sProjectRoot) )
385 return youngest;
387 struct hashbaton_t hashbaton;
388 // hashbaton.hash = statushash;
389 // hashbaton.exthash = exthash;
390 hashbaton.pThis = this;
392 #ifdef _TORTOISESHELL
393 if (g_ShellCache.GetCacheType() == ShellCache::dll)
394 #else
395 if ((DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION) ? 2 : 1) == 2)
396 #endif
398 // gitindex.h based status
400 CString sSubPath;
401 CString s = path.GetWinPathString();
402 if (s.GetLength() > sProjectRoot.GetLength())
404 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == _T(':'))
405 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
406 else
407 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
410 m_status.prop_status = m_status.text_status = git_wc_status_none;
412 m_err = g_IndexFileMap.GetFileStatus(sProjectRoot,sSubPath,&m_status.text_status);
414 else
416 LPCTSTR lpszSubPath = NULL;
417 CString sSubPath;
418 CString s = path.GetWinPathString();
419 if (s.GetLength() > sProjectRoot.GetLength())
421 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
422 lpszSubPath = sSubPath;
423 // skip initial slash if necessary
424 if (*lpszSubPath == _T('\\'))
425 lpszSubPath++;
428 // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here
429 UINT nFlags = WGEFF_SingleFile | WGEFF_NoRecurse;
430 if (!lpszSubPath)
431 // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)
432 nFlags |= WGEFF_EmptyAsNormal;
434 m_status.prop_status = m_status.text_status = git_wc_status_none;
436 // NOTE: currently wgEnumFiles will not enumerate file if it isn't versioned (so status will be git_wc_status_none)
437 //m_err = !wgEnumFiles(sProjectRoot, lpszSubPath, nFlags, &getstatus, &m_status);
438 if(path.IsDirectory())
440 m_err = GetDirStatus(sProjectRoot,CString(lpszSubPath),&m_status.text_status , false,false,NULL, NULL);
442 }else
444 m_err = GetFileStatus(sProjectRoot,CString(lpszSubPath),&m_status.text_status ,false, false,NULL,NULL);
447 /*m_err = git_client_status4 (&youngest,
448 path.GetGitApiPath(m_pool),
449 &rev,
450 getstatushash,
451 &hashbaton,
452 git_depth_empty, //depth
453 TRUE, //getall
454 update, //update
455 noignore, //noignore
456 noexternals,
457 NULL,
458 ctx,
459 m_pool);*/
462 // Error present if function is not under version control
463 if (m_err) /*|| (apr_hash_count(statushash) == 0)*/
465 status = NULL;
466 // return -2;
467 return GIT_INVALID_REVNUM;
470 // Convert the unordered hash to an ordered, sorted array
471 /*statusarray = sort_hash (statushash,
472 sort_compare_items_as_paths,
473 m_pool);*/
475 // only the first entry is needed (no recurse)
476 // item = &APR_ARRAY_IDX (statusarray, 0, const sort_item);
478 // status = (git_wc_status2_t *) item->value;
479 status = &m_status;
481 if (update)
483 // done to match TSVN functionality of this function (not sure if any code uses the reutrn val)
484 // if TGit does not need this, then change the return type of function
485 youngest = g_Git.GetHash(CString(_T("HEAD")));
488 return youngest;
491 git_wc_status2_t * GitStatus::GetFirstFileStatus(const CTGitPath& path, CTGitPath& retPath, bool update, git_depth_t depth, bool bNoIgnore /* = true */, bool bNoExternals /* = false */)
493 static git_wc_status2 st;
495 m_fileCache.Reset();
497 m_fileCache.Init( CStringA( path.GetWinPathString().GetString() ) );
498 MessageBox(NULL, path.GetWinPathString(), _T("GetFirstFile"), MB_OK);
499 m_fileCache.m_pFileIter = m_fileCache.m_pFiles;
500 st.text_status = git_wc_status_none;
502 if (m_fileCache.m_pFileIter)
504 switch(m_fileCache.m_pFileIter->nStatus)
506 case WGFS_Normal: st.text_status = git_wc_status_normal; break;
507 case WGFS_Modified: st.text_status = git_wc_status_modified; break;
508 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;
511 //retPath.SetFromGit((const char*)item->key);
513 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;
516 return &st;
518 #if 0
519 const sort_item* item;
521 git_error_clear(m_err);
522 m_statushash = apr_hash_make(m_pool);
523 m_externalhash = apr_hash_make(m_pool);
524 headrev = Git_INVALID_REVNUM;
525 git_opt_revision_t rev;
526 rev.kind = git_opt_revision_unspecified;
527 struct hashbaton_t hashbaton;
528 hashbaton.hash = m_statushash;
529 hashbaton.exthash = m_externalhash;
530 hashbaton.pThis = this;
531 m_statushashindex = 0;
532 m_err = git_client_status4 (&headrev,
533 path.GetGitApiPath(m_pool),
534 &rev,
535 getstatushash,
536 &hashbaton,
537 depth,
538 TRUE, //getall
539 update, //update
540 bNoIgnore, //noignore
541 bNoExternals, //noexternals
542 NULL,
543 ctx,
544 m_pool);
547 // Error present if function is not under version control
548 if ((m_err != NULL) || (apr_hash_count(m_statushash) == 0))
550 return NULL;
553 // Convert the unordered hash to an ordered, sorted array
554 m_statusarray = sort_hash (m_statushash,
555 sort_compare_items_as_paths,
556 m_pool);
558 // only the first entry is needed (no recurse)
559 m_statushashindex = 0;
560 item = &APR_ARRAY_IDX (m_statusarray, m_statushashindex, const sort_item);
561 retPath.SetFromGit((const char*)item->key);
562 return (git_wc_status2_t *) item->value;
563 #endif
565 return 0;
568 unsigned int GitStatus::GetVersionedCount() const
570 // return /**/m_fileCache.GetFileCount();
572 unsigned int count = 0;
573 #if 0
574 const sort_item* item;
575 for (unsigned int i=0; i<apr_hash_count(m_statushash); ++i)
577 item = &APR_ARRAY_IDX(m_statusarray, i, const sort_item);
578 if (item)
580 if (GitStatus::GetMoreImportant(((git_wc_status_t *)item->value)->text_status, git_wc_status_ignored)!=git_wc_status_ignored)
581 count++;
584 #endif
585 return count;
588 git_wc_status2_t * GitStatus::GetNextFileStatus(CTGitPath& retPath)
590 static git_wc_status2 st;
592 st.text_status = git_wc_status_none;
594 /*if (m_fileCache.m_pFileIter)
596 switch(m_fileCache.m_pFileIter->nStatus)
598 case WGFS_Normal: st.text_status = git_wc_status_normal; break;
599 case WGFS_Modified: st.text_status = git_wc_status_modified; break;
600 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;
603 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;
606 return &st;
608 #if 0
609 const sort_item* item;
611 if ((m_statushashindex+1) >= apr_hash_count(m_statushash))
612 return NULL;
613 m_statushashindex++;
615 item = &APR_ARRAY_IDX (m_statusarray, m_statushashindex, const sort_item);
616 retPath.SetFromGit((const char*)item->key);
617 return (git_wc_status2_t *) item->value;
618 #endif
619 return 0;
622 bool GitStatus::IsExternal(const CTGitPath& path) const
624 #if 0
625 if (apr_hash_get(m_externalhash, path.GetGitApiPath(m_pool), APR_HASH_KEY_STRING))
626 return true;
627 #endif
628 return false;
631 bool GitStatus::IsInExternal(const CTGitPath& path) const
633 #if 0
634 if (apr_hash_count(m_statushash) == 0)
635 return false;
637 GitPool localpool(m_pool);
638 apr_hash_index_t *hi;
639 const char* key;
640 for (hi = apr_hash_first(localpool, m_externalhash); hi; hi = apr_hash_next(hi))
642 apr_hash_this(hi, (const void**)&key, NULL, NULL);
643 if (key)
645 if (CTGitPath(CUnicodeUtils::GetUnicode(key)).IsAncestorOf(path))
646 return true;
649 #endif
650 return false;
654 void GitStatus::GetStatusString(git_wc_status_kind status, size_t buflen, TCHAR * string)
656 TCHAR * buf;
657 switch (status)
659 case git_wc_status_none:
660 buf = _T("none\0");
661 break;
662 case git_wc_status_unversioned:
663 buf = _T("unversioned\0");
664 break;
665 case git_wc_status_normal:
666 buf = _T("normal\0");
667 break;
668 case git_wc_status_added:
669 buf = _T("added\0");
670 break;
671 case git_wc_status_missing:
672 buf = _T("missing\0");
673 break;
674 case git_wc_status_deleted:
675 buf = _T("deleted\0");
676 break;
677 case git_wc_status_replaced:
678 buf = _T("replaced\0");
679 break;
680 case git_wc_status_modified:
681 buf = _T("modified\0");
682 break;
683 case git_wc_status_merged:
684 buf = _T("merged\0");
685 break;
686 case git_wc_status_conflicted:
687 buf = _T("conflicted\0");
688 break;
689 case git_wc_status_obstructed:
690 buf = _T("obstructed\0");
691 break;
692 case git_wc_status_ignored:
693 buf = _T("ignored");
694 break;
695 case git_wc_status_external:
696 buf = _T("external");
697 break;
698 case git_wc_status_incomplete:
699 buf = _T("incomplete\0");
700 break;
701 default:
702 buf = _T("\0");
703 break;
705 _stprintf_s(string, buflen, _T("%s"), buf);
708 void GitStatus::GetStatusString(HINSTANCE hInst, git_wc_status_kind status, TCHAR * string, int size, WORD lang)
710 switch (status)
712 case git_wc_status_none:
713 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
714 break;
715 case git_wc_status_unversioned:
716 LoadStringEx(hInst, IDS_STATUSUNVERSIONED, string, size, lang);
717 break;
718 case git_wc_status_normal:
719 LoadStringEx(hInst, IDS_STATUSNORMAL, string, size, lang);
720 break;
721 case git_wc_status_added:
722 LoadStringEx(hInst, IDS_STATUSADDED, string, size, lang);
723 break;
724 case git_wc_status_missing:
725 LoadStringEx(hInst, IDS_STATUSABSENT, string, size, lang);
726 break;
727 case git_wc_status_deleted:
728 LoadStringEx(hInst, IDS_STATUSDELETED, string, size, lang);
729 break;
730 case git_wc_status_replaced:
731 LoadStringEx(hInst, IDS_STATUSREPLACED, string, size, lang);
732 break;
733 case git_wc_status_modified:
734 LoadStringEx(hInst, IDS_STATUSMODIFIED, string, size, lang);
735 break;
736 case git_wc_status_merged:
737 LoadStringEx(hInst, IDS_STATUSMERGED, string, size, lang);
738 break;
739 case git_wc_status_conflicted:
740 LoadStringEx(hInst, IDS_STATUSCONFLICTED, string, size, lang);
741 break;
742 case git_wc_status_ignored:
743 LoadStringEx(hInst, IDS_STATUSIGNORED, string, size, lang);
744 break;
745 case git_wc_status_obstructed:
746 LoadStringEx(hInst, IDS_STATUSOBSTRUCTED, string, size, lang);
747 break;
748 case git_wc_status_external:
749 LoadStringEx(hInst, IDS_STATUSEXTERNAL, string, size, lang);
750 break;
751 case git_wc_status_incomplete:
752 LoadStringEx(hInst, IDS_STATUSINCOMPLETE, string, size, lang);
753 break;
754 default:
755 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
756 break;
760 #ifdef _MFC_VER
761 CString GitStatus::GetDepthString(git_depth_t depth)
763 #if 0
764 CString sDepth;
765 switch (depth)
767 case git_depth_unknown:
768 sDepth.LoadString(IDS_Git_DEPTH_UNKNOWN);
769 break;
770 case git_depth_empty:
771 sDepth.LoadString(IDS_Git_DEPTH_EMPTY);
772 break;
773 case git_depth_files:
774 sDepth.LoadString(IDS_Git_DEPTH_FILES);
775 break;
776 case git_depth_immediates:
777 sDepth.LoadString(IDS_Git_DEPTH_IMMEDIATE);
778 break;
779 case git_depth_infinity:
780 sDepth.LoadString(IDS_Git_DEPTH_INFINITE);
781 break;
783 return sDepth;
784 #endif
785 return CString("");
787 #endif
789 void GitStatus::GetDepthString(HINSTANCE hInst, git_depth_t depth, TCHAR * string, int size, WORD lang)
791 #if 0
792 switch (depth)
794 case git_depth_unknown:
795 LoadStringEx(hInst, IDS_SVN_DEPTH_UNKNOWN, string, size, lang);
796 break;
797 case git_depth_empty:
798 LoadStringEx(hInst, IDS_SVN_DEPTH_EMPTY, string, size, lang);
799 break;
800 case git_depth_files:
801 LoadStringEx(hInst, IDS_SVN_DEPTH_FILES, string, size, lang);
802 break;
803 case git_depth_immediates:
804 LoadStringEx(hInst, IDS_SVN_DEPTH_IMMEDIATE, string, size, lang);
805 break;
806 case git_depth_infinity:
807 LoadStringEx(hInst, IDS_SVN_DEPTH_INFINITE, string, size, lang);
808 break;
810 #endif
814 int GitStatus::LoadStringEx(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax, WORD wLanguage)
816 const STRINGRESOURCEIMAGE* pImage;
817 const STRINGRESOURCEIMAGE* pImageEnd;
818 ULONG nResourceSize;
819 HGLOBAL hGlobal;
820 UINT iIndex;
821 int ret;
823 HRSRC hResource = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE(((uID>>4)+1)), wLanguage);
824 if (!hResource)
826 // try the default language before giving up!
827 hResource = FindResource(hInstance, MAKEINTRESOURCE(((uID>>4)+1)), RT_STRING);
828 if (!hResource)
829 return 0;
831 hGlobal = LoadResource(hInstance, hResource);
832 if (!hGlobal)
833 return 0;
834 pImage = (const STRINGRESOURCEIMAGE*)::LockResource(hGlobal);
835 if(!pImage)
836 return 0;
838 nResourceSize = ::SizeofResource(hInstance, hResource);
839 pImageEnd = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+nResourceSize);
840 iIndex = uID&0x000f;
842 while ((iIndex > 0) && (pImage < pImageEnd))
844 pImage = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+(sizeof(STRINGRESOURCEIMAGE)+(pImage->nLength*sizeof(WCHAR))));
845 iIndex--;
847 if (pImage >= pImageEnd)
848 return 0;
849 if (pImage->nLength == 0)
850 return 0;
852 ret = pImage->nLength;
853 if (pImage->nLength > nBufferMax)
855 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength-1);
856 lpBuffer[nBufferMax-1] = 0;
858 else
860 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength);
861 lpBuffer[ret] = 0;
863 return ret;
866 BOOL GitStatus::getallstatus(const struct wgFile_s *pFile, void *pUserData)
868 git_wc_status_kind * s = (git_wc_status_kind *)pUserData;
869 *s = GitStatus::GetMoreImportant(*s, GitStatusFromWingit(pFile->nStatus));
870 return FALSE;
873 BOOL GitStatus::getstatus(const struct wgFile_s *pFile, void *pUserData)
875 git_wc_status2_t * s = (git_wc_status2_t*)pUserData;
876 s->prop_status = s->text_status = GitStatus::GetMoreImportant(s->prop_status, GitStatusFromWingit(pFile->nStatus));
877 return FALSE;
880 #if 0
881 git_error_t * GitStatus::getallstatus(void * baton, const char * /*path*/, git_wc_status2_t * status, apr_pool_t * /*pool*/)
883 git_wc_status_kind * s = (git_wc_status_kind *)baton;
884 *s = GitStatus::GetMoreImportant(*s, status->text_status);
885 *s = GitStatus::GetMoreImportant(*s, status->prop_status);
886 return Git_NO_ERROR;
888 #endif
890 #if 0
891 git_error_t * GitStatus::getstatushash(void * baton, const char * path, git_wc_status2_t * status, apr_pool_t * /*pool*/)
893 hashbaton_t * hash = (hashbaton_t *)baton;
894 const StdStrAVector& filterList = hash->pThis->m_filterFileList;
895 if (status->text_status == git_wc_status_external)
897 apr_hash_set (hash->exthash, apr_pstrdup(hash->pThis->m_pool, path), APR_HASH_KEY_STRING, (const void*)1);
898 return Git_NO_ERROR;
900 if(filterList.size() > 0)
902 // We have a filter active - we're only interested in files which are in
903 // the filter
904 if(!binary_search(filterList.begin(), filterList.end(), path))
906 // This item is not in the filter - don't store it
907 return Git_NO_ERROR;
910 git_wc_status2_t * statuscopy = git_wc_dup_status2 (status, hash->pThis->m_pool);
911 apr_hash_set (hash->hash, apr_pstrdup(hash->pThis->m_pool, path), APR_HASH_KEY_STRING, statuscopy);
912 return Git_NO_ERROR;
915 apr_array_header_t * GitStatus::sort_hash (apr_hash_t *ht,
916 int (*comparison_func) (const GitStatus::sort_item *, const GitStatus::sort_item *),
917 apr_pool_t *pool)
919 apr_hash_index_t *hi;
920 apr_array_header_t *ary;
922 /* allocate an array with only one element to begin with. */
923 ary = apr_array_make (pool, 1, sizeof(sort_item));
925 /* loop over hash table and push all keys into the array */
926 for (hi = apr_hash_first (pool, ht); hi; hi = apr_hash_next (hi))
928 sort_item *item = (sort_item*)apr_array_push (ary);
930 apr_hash_this (hi, &item->key, &item->klen, &item->value);
933 /* now quick sort the array. */
934 qsort (ary->elts, ary->nelts, ary->elt_size,
935 (int (*)(const void *, const void *))comparison_func);
937 return ary;
940 int GitStatus::sort_compare_items_as_paths (const sort_item *a, const sort_item *b)
942 const char *astr, *bstr;
944 astr = (const char*)a->key;
945 bstr = (const char*)b->key;
946 return git_path_compare_paths (astr, bstr);
948 #endif
950 git_error_t* GitStatus::cancel(void *baton)
952 #if 0
953 volatile bool * canceled = (bool *)baton;
954 if (*canceled)
956 CString temp;
957 temp.LoadString(IDS_Git_USERCANCELLED);
958 return git_error_create(Git_ERR_CANCELLED, NULL, CUnicodeUtils::GetUTF8(temp));
960 return Git_NO_ERROR;
961 #endif
962 return 0;
965 #ifdef _MFC_VER
967 // Set-up a filter to restrict the files which will have their status stored by a get-status
968 void GitStatus::SetFilter(const CTGitPathList& fileList)
970 m_filterFileList.clear();
971 for(int fileIndex = 0; fileIndex < fileList.GetCount(); fileIndex++)
973 // m_filterFileList.push_back(fileList[fileIndex].GetGitApiPath(m_pool));
975 // Sort the list so that we can do binary searches
976 std::sort(m_filterFileList.begin(), m_filterFileList.end());
979 void GitStatus::ClearFilter()
981 m_filterFileList.clear();
984 #endif // _MFC_VER
986 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
988 int GitStatus::GetFileStatus(CString &gitdir,CString &path,git_wc_status_kind * status,BOOL IsFull, BOOL IsRecursive,FIll_STATUS_CALLBACK callback,void *pData)
991 TCHAR oldpath[MAX_PATH+1];
992 memset(oldpath,0,MAX_PATH+1);
994 CAutoLocker lock(g_Git.m_critGitDllSec);
996 path.Replace(_T('\\'),_T('/'));
998 if(gitdir != g_Git.m_CurrentDir)
1000 g_Git.SetCurrentDir(gitdir);
1001 ::GetCurrentDirectory(MAX_PATH,oldpath);
1002 ::SetCurrentDirectory(g_Git.m_CurrentDir);
1003 git_init();
1006 if(status)
1008 git_wc_status_kind st = git_wc_status_none;
1009 CGitHash hash;
1011 g_IndexFileMap.GetFileStatus(gitdir,path,&st,IsFull,false, NULL,NULL,&hash);
1013 if( st == git_wc_status_conflicted )
1015 *status =st;
1016 if(callback)
1017 callback(path,st,pData);
1018 return 0;
1021 if( st == git_wc_status_unversioned )
1023 if( g_IgnoreList.CheckIgnoreChanged(gitdir,path))
1025 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
1027 if( g_IgnoreList.IsIgnore(path, gitdir) )
1029 st = git_wc_status_ignored;
1031 *status = st;
1032 if(callback)
1033 callback(path,st,pData);
1034 return 0;
1037 if( st == git_wc_status_normal )
1039 if(g_HeadFileMap[gitdir].CheckHeadUpdate())
1041 g_HeadFileMap[gitdir].ReadHeadHash(gitdir);
1042 // Init Repository
1043 if( g_HeadFileMap[gitdir].m_HeadFile.IsEmpty() )
1045 *status =st=git_wc_status_added;
1046 if(callback)
1047 callback(path,st,pData);
1048 return 0;
1050 if(g_HeadFileMap[gitdir].ReadTree())
1052 g_HeadFileMap[gitdir].m_LastModifyTimeHead = 0;
1053 *status = st;
1054 if(callback)
1055 callback(path,st,pData);
1056 return 0;
1060 // Check Head Tree Hash;
1062 //add item
1064 if(g_HeadFileMap[gitdir].m_Map.find(path)
1065 == g_HeadFileMap[gitdir].m_Map.end())
1067 *status =st=git_wc_status_added;
1068 if(callback)
1069 callback(path,st,pData);
1070 return 0;
1073 //staged and not commit
1074 int index=g_HeadFileMap[gitdir].m_Map[path];
1075 if( g_HeadFileMap[gitdir].at(index).m_Hash != hash )
1077 *status = st= git_wc_status_modified;
1078 *status =st=git_wc_status_added;
1079 if(callback)
1080 callback(path,st,pData);
1081 return 0;
1086 *status =st;
1087 if(callback)
1088 callback(path,st,pData);
1089 return 0;
1092 if(*oldpath!=0)
1093 ::SetCurrentDirectory(oldpath);
1095 return 0;
1099 int GitStatus::GetDirStatus(CString &gitdir,CString &path,git_wc_status_kind * status,BOOL IsFul, BOOL IsRecursive,FIll_STATUS_CALLBACK callback,void *pData)
1101 g_Git.m_critGitDllSec.Lock();
1102 TCHAR oldpath[MAX_PATH+1];
1103 memset(oldpath,0,MAX_PATH+1);
1105 path.Replace(_T('\\'),_T('/'));
1107 if(gitdir != g_Git.m_CurrentDir)
1109 g_Git.SetCurrentDir(gitdir);
1110 ::GetCurrentDirectory(MAX_PATH,oldpath);
1111 ::SetCurrentDirectory(g_Git.m_CurrentDir);
1112 git_init();
1115 if(status)
1117 g_IndexFileMap.CheckAndUpdateIndex(gitdir);
1118 int pos=SearchInSortVector(g_IndexFileMap[gitdir],path.GetBuffer(),path.GetLength());
1120 //Not In Version Contorl
1121 if(pos<0)
1123 if(!IsFul)
1125 *status = git_wc_status_unversioned;
1127 }else
1129 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,path))
1130 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
1132 if(g_IgnoreList.IsIgnore(path,gitdir))
1133 *status = git_wc_status_ignored;
1134 else
1135 *status = git_wc_status_unversioned;
1137 g_HeadFileMap[gitdir].CheckHeadUpdate();
1139 //Check init repository
1140 if(g_HeadFileMap[gitdir].m_Head.IsEmpty())
1141 *status = git_wc_status_normal;
1144 }else // In version control
1146 *status = git_wc_status_normal;
1148 int start=0;
1149 int end=0;
1150 if(path.IsEmpty())
1152 start=0;
1153 end=g_IndexFileMap[gitdir].size()-1;
1155 GetRangeInSortVector(g_IndexFileMap[gitdir],path.GetBuffer(),path.GetLength(),&start,&end,pos);
1156 CGitIndexList::iterator it;
1158 it = g_IndexFileMap[gitdir].begin()+start;
1160 // Check Conflict;
1161 for(int i=start;i<=end;i++)
1163 if( ((*it).m_Flags & CE_STAGEMASK) !=0)
1165 *status = git_wc_status_conflicted;
1166 break;
1168 it++;
1171 if( IsFul && (*status != git_wc_status_conflicted))
1173 *status = git_wc_status_normal;
1175 if(g_HeadFileMap[gitdir].CheckHeadUpdate())
1177 g_HeadFileMap[gitdir].ReadHeadHash(gitdir);
1178 if(g_HeadFileMap[gitdir].ReadTree())
1180 //try again
1181 g_Git.SetCurrentDir(gitdir);
1182 ::GetCurrentDirectory(MAX_PATH,oldpath);
1183 ::SetCurrentDirectory(g_Git.m_CurrentDir);
1184 git_init();
1185 g_HeadFileMap[gitdir].ReadTree();
1188 //Check Add
1189 it = ::g_IndexFileMap[gitdir].begin()+start;
1191 //Check if new init repository
1192 if( g_HeadFileMap[gitdir].size() > 0 || g_HeadFileMap[gitdir].m_Head.IsEmpty() )
1194 for(int i=start;i<=end;i++)
1196 if( g_HeadFileMap[gitdir].m_Map.find((*it).m_FileName) == g_HeadFileMap[gitdir].m_Map.end())
1198 pos = -1;
1199 }else
1200 pos = g_HeadFileMap[gitdir].m_Map[(*it).m_FileName];
1202 if(pos < 0)
1204 *status = git_wc_status_added;
1205 break;
1208 if( g_HeadFileMap[gitdir][pos].m_Hash != (*it).m_IndexHash)
1210 *status == git_wc_status_modified;
1211 break;
1214 it++;
1217 //Check Delete
1218 if( *status == git_wc_status_normal )
1220 pos = SearchInSortVector(g_HeadFileMap[gitdir], path.GetBuffer(), path.GetLength());
1221 if(pos <0)
1223 *status = git_wc_status_added;
1225 }else
1227 int hstart,hend;
1228 GetRangeInSortVector(g_HeadFileMap[gitdir],path.GetBuffer(),path.GetLength(),&hstart,&hend,pos);
1229 CGitHeadFileList::iterator hit;
1230 hit = g_HeadFileMap[gitdir].begin()+start;
1231 for(int i=hstart;i<=hend;i++)
1233 if( ::g_IndexFileMap[gitdir].m_Map.find((*hit).m_FileName) == g_IndexFileMap[gitdir].m_Map.end())
1235 *status = git_wc_status_deleted;
1236 break;
1238 hit++;
1243 //Check File Time;
1244 //if(IsRecursive)
1246 it = g_IndexFileMap[gitdir].begin()+start;
1247 for(int i=start;i<=end;i++)
1249 __int64 time;
1250 CString fullpath=gitdir;
1251 fullpath += _T("\\");
1252 fullpath += (*it).m_FileName;
1253 if( !IsRecursive )
1255 //skip child directory
1256 if((*it).m_FileName.Find(_T('/'), path.GetLength()) + 1>0)
1257 continue;
1259 if( g_Git.GetFileModifyTime(fullpath,&time) )
1261 *status = git_wc_status_deleted;
1263 if( (*it).m_ModifyTime != time)
1265 *status = git_wc_status_modified;
1266 break;
1268 it++;
1274 if(callback) callback(path,*status,pData);
1277 if(*oldpath!=0)
1278 ::SetCurrentDirectory(oldpath);
1280 g_Git.m_critGitDllSec.Unlock();
1281 return 0;