1 // TortoiseGit - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseGit
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
21 #ifdef _TORTOISESHELL
\r
22 #include "ShellExt.h"
\r
24 #include "registry.h"
\r
26 //#include "resource.h"
\r
27 #include "..\TortoiseShell\resource.h"
\r
28 //#include "git_config.h"
\r
29 #include "GitStatus.h"
\r
30 #include "UnicodeUtils.h"
\r
31 //#include "GitGlobal.h"
\r
32 //#include "GitHelpers.h"
\r
35 //# include "MessageBox.h"
\r
36 //# include "registry.h"
\r
37 //# include "TGitPath.h"
\r
38 //# include "PathUtils.h"
\r
41 #include "gitindex.h"
\r
43 CGitIndexFileMap g_IndexFileMap;
\r
45 GitStatus::GitStatus(bool * pbCanceled)
\r
49 m_pool = git_pool_create (NULL);
\r
51 git_error_clear(git_client_create_context(&ctx, m_pool));
\r
55 ctx->cancel_func = cancel;
\r
56 ctx->cancel_baton = pbCanceled;
\r
60 git_error_clear(git_config_ensure(NULL, m_pool));
\r
62 // set up authentication
\r
63 m_prompt.Init(m_pool, ctx);
\r
65 // set up the configuration
\r
66 m_err = git_config_get_config (&(ctx->config), g_pConfigDir, m_pool);
\r
70 ::MessageBox(NULL, this->GetLastErrorMsg(), _T("TortoiseGit"), MB_ICONERROR);
\r
71 git_error_clear(m_err);
\r
72 git_pool_destroy (m_pool); // free the allocated memory
\r
76 // set up the Git_SSH param
\r
77 CString tgit_ssh = CRegString(_T("Software\\TortoiseGit\\SSH"));
\r
78 if (tgit_ssh.IsEmpty())
\r
79 tgit_ssh = CPathUtils::GetAppDirectory() + _T("TortoisePlink.exe");
\r
80 tgit_ssh.Replace('\\', '/');
\r
81 if (!tgit_ssh.IsEmpty())
\r
83 git_config_t * cfg = (git_config_t *)apr_hash_get ((apr_hash_t *)ctx->config, Git_CONFIG_CATEGORY_CONFIG,
\r
84 APR_HASH_KEY_STRING);
\r
85 git_config_set(cfg, Git_CONFIG_SECTION_TUNNELS, "ssh", CUnicodeUtils::GetUTF8(tgit_ssh));
\r
88 git_error_clear(git_config_ensure(NULL, m_pool));
\r
90 // set up the configuration
\r
91 m_err = git_config_get_config (&(ctx->config), g_pConfigDir, m_pool);
\r
97 GitStatus::~GitStatus(void)
\r
100 git_error_clear(m_err);
\r
101 git_pool_destroy (m_pool); // free the allocated memory
\r
105 void GitStatus::ClearPool()
\r
108 git_pool_clear(m_pool);
\r
113 CString GitStatus::GetLastErrorMsg() const
\r
115 // return Git::GetErrorString(m_err);
\r
116 return CString("");
\r
119 stdstring GitStatus::GetLastErrorMsg() const
\r
128 git_error_t * ErrPtr = m_err;
\r
129 if (ErrPtr->message)
\r
131 msg = CUnicodeUtils::StdGetUnicode(ErrPtr->message);
\r
135 /* Is this a Subversion-specific error code? */
\r
136 if ((ErrPtr->apr_err > APR_OS_START_USEERR)
\r
137 && (ErrPtr->apr_err <= APR_OS_START_CANONERR))
\r
138 msg = CUnicodeUtils::StdGetUnicode(git_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)));
\r
139 /* Otherwise, this must be an APR error code. */
\r
142 git_error_t *temp_err = NULL;
\r
143 const char * err_string = NULL;
\r
144 temp_err = git_utf_cstring_to_utf8(&err_string, apr_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)-1), ErrPtr->pool);
\r
147 git_error_clear (temp_err);
\r
148 msg = _T("Can't recode error string from APR");
\r
152 msg = CUnicodeUtils::StdGetUnicode(err_string);
\r
158 while (ErrPtr->child)
\r
160 ErrPtr = ErrPtr->child;
\r
162 if (ErrPtr->message)
\r
164 msg += CUnicodeUtils::StdGetUnicode(ErrPtr->message);
\r
168 /* Is this a Subversion-specific error code? */
\r
169 if ((ErrPtr->apr_err > APR_OS_START_USEERR)
\r
170 && (ErrPtr->apr_err <= APR_OS_START_CANONERR))
\r
171 msg += CUnicodeUtils::StdGetUnicode(git_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)));
\r
172 /* Otherwise, this must be an APR error code. */
\r
175 git_error_t *temp_err = NULL;
\r
176 const char * err_string = NULL;
\r
177 temp_err = git_utf_cstring_to_utf8(&err_string, apr_strerror (ErrPtr->apr_err, errbuf, sizeof (errbuf)-1), ErrPtr->pool);
\r
180 git_error_clear (temp_err);
\r
181 msg += _T("Can't recode error string from APR");
\r
185 msg += CUnicodeUtils::StdGetUnicode(err_string);
\r
192 } // if (m_err != NULL)
\r
199 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth)
\r
201 git_wc_status_kind statuskind;
\r
202 // git_client_ctx_t * ctx;
\r
204 // apr_pool_t * pool;
\r
205 // git_error_t * err;
\r
208 CString sProjectRoot;
\r
210 isDir = path.IsDirectory();
\r
211 if (!path.HasAdminDir(&sProjectRoot))
\r
212 return git_wc_status_none;
\r
214 // pool = git_pool_create (NULL); // create the memory pool
\r
216 // git_error_clear(git_client_create_context(&ctx, pool));
\r
218 // git_revnum_t youngest = Git_INVALID_REVNUM;
\r
219 // git_opt_revision_t rev;
\r
220 // rev.kind = git_opt_revision_unspecified;
\r
221 statuskind = git_wc_status_none;
\r
223 const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source
\r
225 #ifdef _TORTOISESHELL
\r
226 if (g_ShellCache.GetCacheType() == ShellCache::dll)
\r
228 if ((DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION) ? 2 : 1) == 2)
\r
231 // gitindex.h based status
\r
234 CString s = path.GetWinPathString();
\r
235 if (s.GetLength() > sProjectRoot.GetLength())
\r
237 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == _T(':'))
\r
238 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
\r
240 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
\r
243 err = g_IndexFileMap.GetFileStatus(sProjectRoot,sSubPath,&statuskind);
\r
247 LPCTSTR lpszSubPath = NULL;
\r
249 CString s = path.GetWinPathString();
\r
250 if (s.GetLength() > sProjectRoot.GetLength())
\r
252 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
\r
253 lpszSubPath = sSubPath;
\r
254 // skip initial slash if necessary
\r
255 if (*lpszSubPath == _T('\\'))
\r
260 // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here
\r
261 UINT nFlags = WGEFF_SingleFile;
\r
263 nFlags |= WGEFF_NoRecurse;
\r
265 // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)
\r
266 nFlags |= WGEFF_EmptyAsNormal;
\r
268 // enumerate all files, recursively if requested
\r
271 nFlags |= WGEFF_NoRecurse;
\r
274 err = !wgEnumFiles(sProjectRoot, lpszSubPath, nFlags, &getallstatus, &statuskind);
\r
276 /*err = git_client_status4 (&youngest,
\r
277 path.GetSVNApiPath(pool),
\r
285 FALSE, //ignore externals
\r
294 // git_error_clear(err);
\r
295 // git_pool_destroy (pool); //free allocated memory
\r
296 return git_wc_status_none;
\r
299 // git_pool_destroy (pool); //free allocated memory
\r
305 git_wc_status_kind GitStatus::GetAllStatusRecursive(const CTGitPath& path)
\r
307 return GetAllStatus(path, git_depth_infinity);
\r
311 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)
\r
313 if (GetStatusRanking(status1) >= GetStatusRanking(status2))
\r
317 // static private method
\r
318 int GitStatus::GetStatusRanking(git_wc_status_kind status)
\r
322 case git_wc_status_none:
\r
324 case git_wc_status_unversioned:
\r
326 case git_wc_status_ignored:
\r
328 case git_wc_status_incomplete:
\r
330 case git_wc_status_normal:
\r
331 case git_wc_status_external:
\r
333 case git_wc_status_added:
\r
335 case git_wc_status_missing:
\r
337 case git_wc_status_deleted:
\r
339 case git_wc_status_replaced:
\r
341 case git_wc_status_modified:
\r
343 case git_wc_status_merged:
\r
345 case git_wc_status_conflicted:
\r
347 case git_wc_status_obstructed:
\r
353 git_revnum_t GitStatus::GetStatus(const CTGitPath& path, bool update /* = false */, bool noignore /* = false */, bool noexternals /* = false */)
\r
355 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
\r
356 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
\r
357 // after the call again
\r
359 // apr_hash_t * statushash;
\r
360 // apr_hash_t * exthash;
\r
361 // apr_array_header_t * statusarray;
\r
362 // const sort_item* item;
\r
364 // git_error_clear(m_err);
\r
365 // statushash = apr_hash_make(m_pool);
\r
366 // exthash = apr_hash_make(m_pool);
\r
367 git_revnum_t youngest = GIT_INVALID_REVNUM;
\r
368 // git_opt_revision_t rev;
\r
369 // rev.kind = git_opt_revision_unspecified;
\r
371 CString sProjectRoot;
\r
372 if ( !path.HasAdminDir(&sProjectRoot) )
\r
375 struct hashbaton_t hashbaton;
\r
376 // hashbaton.hash = statushash;
\r
377 // hashbaton.exthash = exthash;
\r
378 hashbaton.pThis = this;
\r
380 #ifdef _TORTOISESHELL
\r
381 if (g_ShellCache.GetCacheType() == ShellCache::dll)
\r
383 if ((DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION) ? 2 : 1) == 2)
\r
386 // gitindex.h based status
\r
389 CString s = path.GetWinPathString();
\r
390 if (s.GetLength() > sProjectRoot.GetLength())
\r
392 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == _T(':'))
\r
393 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
\r
395 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
\r
398 m_status.prop_status = m_status.text_status = git_wc_status_none;
\r
400 m_err = g_IndexFileMap.GetFileStatus(sProjectRoot,sSubPath,&m_status.text_status);
\r
404 LPCTSTR lpszSubPath = NULL;
\r
406 CString s = path.GetWinPathString();
\r
407 if (s.GetLength() > sProjectRoot.GetLength())
\r
409 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
\r
410 lpszSubPath = sSubPath;
\r
411 // skip initial slash if necessary
\r
412 if (*lpszSubPath == _T('\\'))
\r
416 // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here
\r
417 UINT nFlags = WGEFF_SingleFile | WGEFF_NoRecurse;
\r
419 // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)
\r
420 nFlags |= WGEFF_EmptyAsNormal;
\r
422 m_status.prop_status = m_status.text_status = git_wc_status_none;
\r
424 // NOTE: currently wgEnumFiles will not enumerate file if it isn't versioned (so status will be git_wc_status_none)
\r
425 m_err = !wgEnumFiles(sProjectRoot, lpszSubPath, nFlags, &getstatus, &m_status);
\r
427 /*m_err = git_client_status4 (&youngest,
\r
428 path.GetGitApiPath(m_pool),
\r
432 git_depth_empty, //depth
\r
435 noignore, //noignore
\r
442 // Error present if function is not under version control
\r
443 if (m_err) /*|| (apr_hash_count(statushash) == 0)*/
\r
447 return GIT_INVALID_REVNUM;
\r
450 // Convert the unordered hash to an ordered, sorted array
\r
451 /*statusarray = sort_hash (statushash,
\r
452 sort_compare_items_as_paths,
\r
455 // only the first entry is needed (no recurse)
\r
456 // item = &APR_ARRAY_IDX (statusarray, 0, const sort_item);
\r
458 // status = (git_wc_status2_t *) item->value;
\r
459 status = &m_status;
\r
463 // done to match TSVN functionality of this function (not sure if any code uses the reutrn val)
\r
464 // if TGit does not need this, then change the return type of function
\r
465 youngest = g_Git.GetHash(CString(_T("HEAD")));
\r
471 git_wc_status2_t * GitStatus::GetFirstFileStatus(const CTGitPath& path, CTGitPath& retPath, bool update, git_depth_t depth, bool bNoIgnore /* = true */, bool bNoExternals /* = false */)
\r
473 static git_wc_status2 st;
\r
475 m_fileCache.Reset();
\r
477 m_fileCache.Init( CStringA( path.GetWinPathString().GetString() ) );
\r
478 MessageBox(NULL, path.GetWinPathString(), _T("GetFirstFile"), MB_OK);
\r
479 m_fileCache.m_pFileIter = m_fileCache.m_pFiles;
\r
480 st.text_status = git_wc_status_none;
\r
482 if (m_fileCache.m_pFileIter)
\r
484 switch(m_fileCache.m_pFileIter->nStatus)
\r
486 case WGFS_Normal: st.text_status = git_wc_status_normal; break;
\r
487 case WGFS_Modified: st.text_status = git_wc_status_modified; break;
\r
488 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;
\r
491 //retPath.SetFromGit((const char*)item->key);
\r
493 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;
\r
499 const sort_item* item;
\r
501 git_error_clear(m_err);
\r
502 m_statushash = apr_hash_make(m_pool);
\r
503 m_externalhash = apr_hash_make(m_pool);
\r
504 headrev = Git_INVALID_REVNUM;
\r
505 git_opt_revision_t rev;
\r
506 rev.kind = git_opt_revision_unspecified;
\r
507 struct hashbaton_t hashbaton;
\r
508 hashbaton.hash = m_statushash;
\r
509 hashbaton.exthash = m_externalhash;
\r
510 hashbaton.pThis = this;
\r
511 m_statushashindex = 0;
\r
512 m_err = git_client_status4 (&headrev,
\r
513 path.GetGitApiPath(m_pool),
\r
520 bNoIgnore, //noignore
\r
521 bNoExternals, //noexternals
\r
527 // Error present if function is not under version control
\r
528 if ((m_err != NULL) || (apr_hash_count(m_statushash) == 0))
\r
533 // Convert the unordered hash to an ordered, sorted array
\r
534 m_statusarray = sort_hash (m_statushash,
\r
535 sort_compare_items_as_paths,
\r
538 // only the first entry is needed (no recurse)
\r
539 m_statushashindex = 0;
\r
540 item = &APR_ARRAY_IDX (m_statusarray, m_statushashindex, const sort_item);
\r
541 retPath.SetFromGit((const char*)item->key);
\r
542 return (git_wc_status2_t *) item->value;
\r
548 unsigned int GitStatus::GetVersionedCount() const
\r
550 // return /**/m_fileCache.GetFileCount();
\r
552 unsigned int count = 0;
\r
554 const sort_item* item;
\r
555 for (unsigned int i=0; i<apr_hash_count(m_statushash); ++i)
\r
557 item = &APR_ARRAY_IDX(m_statusarray, i, const sort_item);
\r
560 if (GitStatus::GetMoreImportant(((git_wc_status_t *)item->value)->text_status, git_wc_status_ignored)!=git_wc_status_ignored)
\r
568 git_wc_status2_t * GitStatus::GetNextFileStatus(CTGitPath& retPath)
\r
570 static git_wc_status2 st;
\r
572 st.text_status = git_wc_status_none;
\r
574 /*if (m_fileCache.m_pFileIter)
\r
576 switch(m_fileCache.m_pFileIter->nStatus)
\r
578 case WGFS_Normal: st.text_status = git_wc_status_normal; break;
\r
579 case WGFS_Modified: st.text_status = git_wc_status_modified; break;
\r
580 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;
\r
583 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;
\r
589 const sort_item* item;
\r
591 if ((m_statushashindex+1) >= apr_hash_count(m_statushash))
\r
593 m_statushashindex++;
\r
595 item = &APR_ARRAY_IDX (m_statusarray, m_statushashindex, const sort_item);
\r
596 retPath.SetFromGit((const char*)item->key);
\r
597 return (git_wc_status2_t *) item->value;
\r
602 bool GitStatus::IsExternal(const CTGitPath& path) const
\r
605 if (apr_hash_get(m_externalhash, path.GetGitApiPath(m_pool), APR_HASH_KEY_STRING))
\r
611 bool GitStatus::IsInExternal(const CTGitPath& path) const
\r
614 if (apr_hash_count(m_statushash) == 0)
\r
617 GitPool localpool(m_pool);
\r
618 apr_hash_index_t *hi;
\r
620 for (hi = apr_hash_first(localpool, m_externalhash); hi; hi = apr_hash_next(hi))
\r
622 apr_hash_this(hi, (const void**)&key, NULL, NULL);
\r
625 if (CTGitPath(CUnicodeUtils::GetUnicode(key)).IsAncestorOf(path))
\r
634 void GitStatus::GetStatusString(git_wc_status_kind status, size_t buflen, TCHAR * string)
\r
639 case git_wc_status_none:
\r
640 buf = _T("none\0");
\r
642 case git_wc_status_unversioned:
\r
643 buf = _T("unversioned\0");
\r
645 case git_wc_status_normal:
\r
646 buf = _T("normal\0");
\r
648 case git_wc_status_added:
\r
649 buf = _T("added\0");
\r
651 case git_wc_status_missing:
\r
652 buf = _T("missing\0");
\r
654 case git_wc_status_deleted:
\r
655 buf = _T("deleted\0");
\r
657 case git_wc_status_replaced:
\r
658 buf = _T("replaced\0");
\r
660 case git_wc_status_modified:
\r
661 buf = _T("modified\0");
\r
663 case git_wc_status_merged:
\r
664 buf = _T("merged\0");
\r
666 case git_wc_status_conflicted:
\r
667 buf = _T("conflicted\0");
\r
669 case git_wc_status_obstructed:
\r
670 buf = _T("obstructed\0");
\r
672 case git_wc_status_ignored:
\r
673 buf = _T("ignored");
\r
675 case git_wc_status_external:
\r
676 buf = _T("external");
\r
678 case git_wc_status_incomplete:
\r
679 buf = _T("incomplete\0");
\r
685 _stprintf_s(string, buflen, _T("%s"), buf);
\r
688 void GitStatus::GetStatusString(HINSTANCE hInst, git_wc_status_kind status, TCHAR * string, int size, WORD lang)
\r
692 case git_wc_status_none:
\r
693 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
\r
695 case git_wc_status_unversioned:
\r
696 LoadStringEx(hInst, IDS_STATUSUNVERSIONED, string, size, lang);
\r
698 case git_wc_status_normal:
\r
699 LoadStringEx(hInst, IDS_STATUSNORMAL, string, size, lang);
\r
701 case git_wc_status_added:
\r
702 LoadStringEx(hInst, IDS_STATUSADDED, string, size, lang);
\r
704 case git_wc_status_missing:
\r
705 LoadStringEx(hInst, IDS_STATUSABSENT, string, size, lang);
\r
707 case git_wc_status_deleted:
\r
708 LoadStringEx(hInst, IDS_STATUSDELETED, string, size, lang);
\r
710 case git_wc_status_replaced:
\r
711 LoadStringEx(hInst, IDS_STATUSREPLACED, string, size, lang);
\r
713 case git_wc_status_modified:
\r
714 LoadStringEx(hInst, IDS_STATUSMODIFIED, string, size, lang);
\r
716 case git_wc_status_merged:
\r
717 LoadStringEx(hInst, IDS_STATUSMERGED, string, size, lang);
\r
719 case git_wc_status_conflicted:
\r
720 LoadStringEx(hInst, IDS_STATUSCONFLICTED, string, size, lang);
\r
722 case git_wc_status_ignored:
\r
723 LoadStringEx(hInst, IDS_STATUSIGNORED, string, size, lang);
\r
725 case git_wc_status_obstructed:
\r
726 LoadStringEx(hInst, IDS_STATUSOBSTRUCTED, string, size, lang);
\r
728 case git_wc_status_external:
\r
729 LoadStringEx(hInst, IDS_STATUSEXTERNAL, string, size, lang);
\r
731 case git_wc_status_incomplete:
\r
732 LoadStringEx(hInst, IDS_STATUSINCOMPLETE, string, size, lang);
\r
735 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
\r
741 CString GitStatus::GetDepthString(git_depth_t depth)
\r
747 case git_depth_unknown:
\r
748 sDepth.LoadString(IDS_Git_DEPTH_UNKNOWN);
\r
750 case git_depth_empty:
\r
751 sDepth.LoadString(IDS_Git_DEPTH_EMPTY);
\r
753 case git_depth_files:
\r
754 sDepth.LoadString(IDS_Git_DEPTH_FILES);
\r
756 case git_depth_immediates:
\r
757 sDepth.LoadString(IDS_Git_DEPTH_IMMEDIATE);
\r
759 case git_depth_infinity:
\r
760 sDepth.LoadString(IDS_Git_DEPTH_INFINITE);
\r
765 return CString("");
\r
769 void GitStatus::GetDepthString(HINSTANCE hInst, git_depth_t depth, TCHAR * string, int size, WORD lang)
\r
774 case git_depth_unknown:
\r
775 LoadStringEx(hInst, IDS_SVN_DEPTH_UNKNOWN, string, size, lang);
\r
777 case git_depth_empty:
\r
778 LoadStringEx(hInst, IDS_SVN_DEPTH_EMPTY, string, size, lang);
\r
780 case git_depth_files:
\r
781 LoadStringEx(hInst, IDS_SVN_DEPTH_FILES, string, size, lang);
\r
783 case git_depth_immediates:
\r
784 LoadStringEx(hInst, IDS_SVN_DEPTH_IMMEDIATE, string, size, lang);
\r
786 case git_depth_infinity:
\r
787 LoadStringEx(hInst, IDS_SVN_DEPTH_INFINITE, string, size, lang);
\r
794 int GitStatus::LoadStringEx(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax, WORD wLanguage)
\r
796 const STRINGRESOURCEIMAGE* pImage;
\r
797 const STRINGRESOURCEIMAGE* pImageEnd;
\r
798 ULONG nResourceSize;
\r
803 HRSRC hResource = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE(((uID>>4)+1)), wLanguage);
\r
806 // try the default language before giving up!
\r
807 hResource = FindResource(hInstance, MAKEINTRESOURCE(((uID>>4)+1)), RT_STRING);
\r
811 hGlobal = LoadResource(hInstance, hResource);
\r
814 pImage = (const STRINGRESOURCEIMAGE*)::LockResource(hGlobal);
\r
818 nResourceSize = ::SizeofResource(hInstance, hResource);
\r
819 pImageEnd = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+nResourceSize);
\r
820 iIndex = uID&0x000f;
\r
822 while ((iIndex > 0) && (pImage < pImageEnd))
\r
824 pImage = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+(sizeof(STRINGRESOURCEIMAGE)+(pImage->nLength*sizeof(WCHAR))));
\r
827 if (pImage >= pImageEnd)
\r
829 if (pImage->nLength == 0)
\r
832 ret = pImage->nLength;
\r
833 if (pImage->nLength > nBufferMax)
\r
835 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength-1);
\r
836 lpBuffer[nBufferMax-1] = 0;
\r
840 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength);
\r
846 BOOL GitStatus::getallstatus(const struct wgFile_s *pFile, void *pUserData)
\r
848 git_wc_status_kind * s = (git_wc_status_kind *)pUserData;
\r
849 *s = GitStatus::GetMoreImportant(*s, GitStatusFromWingit(pFile->nStatus));
\r
853 BOOL GitStatus::getstatus(const struct wgFile_s *pFile, void *pUserData)
\r
855 git_wc_status2_t * s = (git_wc_status2_t*)pUserData;
\r
856 s->prop_status = s->text_status = GitStatus::GetMoreImportant(s->prop_status, GitStatusFromWingit(pFile->nStatus));
\r
861 git_error_t * GitStatus::getallstatus(void * baton, const char * /*path*/, git_wc_status2_t * status, apr_pool_t * /*pool*/)
\r
863 git_wc_status_kind * s = (git_wc_status_kind *)baton;
\r
864 *s = GitStatus::GetMoreImportant(*s, status->text_status);
\r
865 *s = GitStatus::GetMoreImportant(*s, status->prop_status);
\r
866 return Git_NO_ERROR;
\r
871 git_error_t * GitStatus::getstatushash(void * baton, const char * path, git_wc_status2_t * status, apr_pool_t * /*pool*/)
\r
873 hashbaton_t * hash = (hashbaton_t *)baton;
\r
874 const StdStrAVector& filterList = hash->pThis->m_filterFileList;
\r
875 if (status->text_status == git_wc_status_external)
\r
877 apr_hash_set (hash->exthash, apr_pstrdup(hash->pThis->m_pool, path), APR_HASH_KEY_STRING, (const void*)1);
\r
878 return Git_NO_ERROR;
\r
880 if(filterList.size() > 0)
\r
882 // We have a filter active - we're only interested in files which are in
\r
884 if(!binary_search(filterList.begin(), filterList.end(), path))
\r
886 // This item is not in the filter - don't store it
\r
887 return Git_NO_ERROR;
\r
890 git_wc_status2_t * statuscopy = git_wc_dup_status2 (status, hash->pThis->m_pool);
\r
891 apr_hash_set (hash->hash, apr_pstrdup(hash->pThis->m_pool, path), APR_HASH_KEY_STRING, statuscopy);
\r
892 return Git_NO_ERROR;
\r
895 apr_array_header_t * GitStatus::sort_hash (apr_hash_t *ht,
\r
896 int (*comparison_func) (const GitStatus::sort_item *, const GitStatus::sort_item *),
\r
899 apr_hash_index_t *hi;
\r
900 apr_array_header_t *ary;
\r
902 /* allocate an array with only one element to begin with. */
\r
903 ary = apr_array_make (pool, 1, sizeof(sort_item));
\r
905 /* loop over hash table and push all keys into the array */
\r
906 for (hi = apr_hash_first (pool, ht); hi; hi = apr_hash_next (hi))
\r
908 sort_item *item = (sort_item*)apr_array_push (ary);
\r
910 apr_hash_this (hi, &item->key, &item->klen, &item->value);
\r
913 /* now quick sort the array. */
\r
914 qsort (ary->elts, ary->nelts, ary->elt_size,
\r
915 (int (*)(const void *, const void *))comparison_func);
\r
920 int GitStatus::sort_compare_items_as_paths (const sort_item *a, const sort_item *b)
\r
922 const char *astr, *bstr;
\r
924 astr = (const char*)a->key;
\r
925 bstr = (const char*)b->key;
\r
926 return git_path_compare_paths (astr, bstr);
\r
930 git_error_t* GitStatus::cancel(void *baton)
\r
933 volatile bool * canceled = (bool *)baton;
\r
937 temp.LoadString(IDS_Git_USERCANCELLED);
\r
938 return git_error_create(Git_ERR_CANCELLED, NULL, CUnicodeUtils::GetUTF8(temp));
\r
940 return Git_NO_ERROR;
\r
947 // Set-up a filter to restrict the files which will have their status stored by a get-status
\r
948 void GitStatus::SetFilter(const CTGitPathList& fileList)
\r
950 m_filterFileList.clear();
\r
951 for(int fileIndex = 0; fileIndex < fileList.GetCount(); fileIndex++)
\r
953 // m_filterFileList.push_back(fileList[fileIndex].GetGitApiPath(m_pool));
\r
955 // Sort the list so that we can do binary searches
\r
956 std::sort(m_filterFileList.begin(), m_filterFileList.end());
\r
959 void GitStatus::ClearFilter()
\r
961 m_filterFileList.clear();
\r