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.
21 //#include "resource.h"
22 #include "..\TortoiseShell\resource.h"
23 //#include "git_config.h"
24 #include "GitStatus.h"
25 #include "UnicodeUtils.h"
26 //#include "GitGlobal.h"
27 //#include "GitHelpers.h"
30 //# include "MessageBox.h"
31 //# include "registry.h"
32 //# include "TGitPath.h"
33 //# include "PathUtils.h"
37 GitStatus::GitStatus(bool * pbCanceled
)
41 m_pool
= git_pool_create (NULL
);
43 git_error_clear(git_client_create_context(&ctx
, m_pool
));
47 ctx
->cancel_func
= cancel
;
48 ctx
->cancel_baton
= pbCanceled
;
52 git_error_clear(git_config_ensure(NULL
, m_pool
));
54 // set up authentication
55 m_prompt
.Init(m_pool
, ctx
);
57 // set up the configuration
58 m_err
= git_config_get_config (&(ctx
->config
), g_pConfigDir
, m_pool
);
62 ::MessageBox(NULL
, this->GetLastErrorMsg(), _T("TortoiseGit"), MB_ICONERROR
);
63 git_error_clear(m_err
);
64 git_pool_destroy (m_pool
); // free the allocated memory
68 // set up the Git_SSH param
69 CString tgit_ssh
= CRegString(_T("Software\\TortoiseGit\\SSH"));
70 if (tgit_ssh
.IsEmpty())
71 tgit_ssh
= CPathUtils::GetAppDirectory() + _T("TortoisePlink.exe");
72 tgit_ssh
.Replace('\\', '/');
73 if (!tgit_ssh
.IsEmpty())
75 git_config_t
* cfg
= (git_config_t
*)apr_hash_get ((apr_hash_t
*)ctx
->config
, Git_CONFIG_CATEGORY_CONFIG
,
77 git_config_set(cfg
, Git_CONFIG_SECTION_TUNNELS
, "ssh", CUnicodeUtils::GetUTF8(tgit_ssh
));
80 git_error_clear(git_config_ensure(NULL
, m_pool
));
82 // set up the configuration
83 m_err
= git_config_get_config (&(ctx
->config
), g_pConfigDir
, m_pool
);
89 GitStatus::~GitStatus(void)
92 git_error_clear(m_err
);
93 git_pool_destroy (m_pool
); // free the allocated memory
97 void GitStatus::ClearPool()
100 git_pool_clear(m_pool
);
105 CString
GitStatus::GetLastErrorMsg() const
107 // return Git::GetErrorString(m_err);
111 stdstring
GitStatus::GetLastErrorMsg() const
120 git_error_t
* ErrPtr
= m_err
;
123 msg
= CUnicodeUtils::StdGetUnicode(ErrPtr
->message
);
127 /* Is this a Subversion-specific error code? */
128 if ((ErrPtr
->apr_err
> APR_OS_START_USEERR
)
129 && (ErrPtr
->apr_err
<= APR_OS_START_CANONERR
))
130 msg
= CUnicodeUtils::StdGetUnicode(git_strerror (ErrPtr
->apr_err
, errbuf
, sizeof (errbuf
)));
131 /* Otherwise, this must be an APR error code. */
134 git_error_t
*temp_err
= NULL
;
135 const char * err_string
= NULL
;
136 temp_err
= git_utf_cstring_to_utf8(&err_string
, apr_strerror (ErrPtr
->apr_err
, errbuf
, sizeof (errbuf
)-1), ErrPtr
->pool
);
139 git_error_clear (temp_err
);
140 msg
= _T("Can't recode error string from APR");
144 msg
= CUnicodeUtils::StdGetUnicode(err_string
);
150 while (ErrPtr
->child
)
152 ErrPtr
= ErrPtr
->child
;
156 msg
+= CUnicodeUtils::StdGetUnicode(ErrPtr
->message
);
160 /* Is this a Subversion-specific error code? */
161 if ((ErrPtr
->apr_err
> APR_OS_START_USEERR
)
162 && (ErrPtr
->apr_err
<= APR_OS_START_CANONERR
))
163 msg
+= CUnicodeUtils::StdGetUnicode(git_strerror (ErrPtr
->apr_err
, errbuf
, sizeof (errbuf
)));
164 /* Otherwise, this must be an APR error code. */
167 git_error_t
*temp_err
= NULL
;
168 const char * err_string
= NULL
;
169 temp_err
= git_utf_cstring_to_utf8(&err_string
, apr_strerror (ErrPtr
->apr_err
, errbuf
, sizeof (errbuf
)-1), ErrPtr
->pool
);
172 git_error_clear (temp_err
);
173 msg
+= _T("Can't recode error string from APR");
177 msg
+= CUnicodeUtils::StdGetUnicode(err_string
);
184 } // if (m_err != NULL)
191 git_wc_status_kind
GitStatus::GetAllStatus(const CTGitPath
& path
, git_depth_t depth
)
193 git_wc_status_kind statuskind
;
194 // git_client_ctx_t * ctx;
196 // apr_pool_t * pool;
197 // git_error_t * err;
200 CString sProjectRoot
;
202 isDir
= path
.IsDirectory();
203 if (!path
.HasAdminDir(&sProjectRoot
))
204 return git_wc_status_none
;
206 // pool = git_pool_create (NULL); // create the memory pool
208 // git_error_clear(git_client_create_context(&ctx, pool));
210 // git_revnum_t youngest = Git_INVALID_REVNUM;
211 // git_opt_revision_t rev;
212 // rev.kind = git_opt_revision_unspecified;
213 statuskind
= git_wc_status_none
;
215 const BOOL bIsRecursive
= (depth
== git_depth_infinity
|| depth
== git_depth_unknown
); // taken from SVN source
217 LPCSTR lpszSubPath
= NULL
;
219 CString s
= path
.GetWinPathString();
220 if (s
.GetLength() > sProjectRoot
.GetLength())
222 sSubPath
= CStringA(s
.Right(s
.GetLength() - sProjectRoot
.GetLength() - 1/*otherwise it gets initial slash*/));
223 lpszSubPath
= sSubPath
;
227 // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here
228 UINT nFlags
= WGEFF_SingleFile
;
230 nFlags
|= WGEFF_NoRecurse
;
232 // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)
233 nFlags
|= WGEFF_EmptyAsNormal
;
235 // enumerate all files, recursively if requested
238 nFlags
|= WGEFF_NoRecurse
;
241 err
= !wgEnumFiles_safe(CStringA(sProjectRoot
), lpszSubPath
, nFlags
, &getallstatus
, &statuskind
);
243 /*err = git_client_status4 (&youngest,
244 path.GetSVNApiPath(pool),
252 FALSE, //ignore externals
260 // git_error_clear(err);
261 // git_pool_destroy (pool); //free allocated memory
262 return git_wc_status_none
;
265 // git_pool_destroy (pool); //free allocated memory
271 git_wc_status_kind
GitStatus::GetAllStatusRecursive(const CTGitPath
& path
)
273 return GetAllStatus(path
, git_depth_infinity
);
277 git_wc_status_kind
GitStatus::GetMoreImportant(git_wc_status_kind status1
, git_wc_status_kind status2
)
279 if (GetStatusRanking(status1
) >= GetStatusRanking(status2
))
283 // static private method
284 int GitStatus::GetStatusRanking(git_wc_status_kind status
)
288 case git_wc_status_none
:
290 case git_wc_status_unversioned
:
292 case git_wc_status_ignored
:
294 case git_wc_status_incomplete
:
296 case git_wc_status_normal
:
297 case git_wc_status_external
:
299 case git_wc_status_added
:
301 case git_wc_status_missing
:
303 case git_wc_status_deleted
:
305 case git_wc_status_replaced
:
307 case git_wc_status_modified
:
309 case git_wc_status_merged
:
311 case git_wc_status_conflicted
:
313 case git_wc_status_obstructed
:
319 git_revnum_t
GitStatus::GetStatus(const CTGitPath
& path
, bool update
/* = false */, bool noignore
/* = false */, bool noexternals
/* = false */)
321 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
322 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
323 // after the call again
325 // apr_hash_t * statushash;
326 // apr_hash_t * exthash;
327 // apr_array_header_t * statusarray;
328 // const sort_item* item;
330 // git_error_clear(m_err);
331 // statushash = apr_hash_make(m_pool);
332 // exthash = apr_hash_make(m_pool);
333 git_revnum_t youngest
= GIT_INVALID_REVNUM
;
334 // git_opt_revision_t rev;
335 // rev.kind = git_opt_revision_unspecified;
337 CString sProjectRoot
;
338 if ( !path
.HasAdminDir(&sProjectRoot
) )
341 struct hashbaton_t hashbaton
;
342 // hashbaton.hash = statushash;
343 // hashbaton.exthash = exthash;
344 hashbaton
.pThis
= this;
346 LPCSTR lpszSubPath
= NULL
;
348 CString s
= path
.GetWinPathString();
349 if (s
.GetLength() > sProjectRoot
.GetLength())
351 sSubPath
= CStringA(s
.Right(s
.GetLength() - sProjectRoot
.GetLength() - 1/*otherwise it gets initial slash*/));
352 lpszSubPath
= sSubPath
;
355 // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here
356 UINT nFlags
= WGEFF_SingleFile
| WGEFF_NoRecurse
;
358 // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)
359 nFlags
|= WGEFF_EmptyAsNormal
;
361 m_status
.prop_status
= m_status
.text_status
= git_wc_status_none
;
363 // NOTE: currently wgEnumFiles_safe_safe_safe will not enumerate file if it isn't versioned (so status will be git_wc_status_none)
364 m_err
= !wgEnumFiles_safe(CStringA(sProjectRoot
), lpszSubPath
, nFlags
, &getstatus
, &m_status
);
366 /*m_err = git_client_status4 (&youngest,
367 path.GetGitApiPath(m_pool),
371 git_depth_empty, //depth
381 // Error present if function is not under version control
382 if ((m_err
!= NULL
) /*|| (apr_hash_count(statushash) == 0)*/)
386 return GIT_INVALID_REVNUM
;
389 // Convert the unordered hash to an ordered, sorted array
390 /*statusarray = sort_hash (statushash,
391 sort_compare_items_as_paths,
394 // only the first entry is needed (no recurse)
395 // item = &APR_ARRAY_IDX (statusarray, 0, const sort_item);
397 // status = (git_wc_status2_t *) item->value;
402 const BYTE
*sha1
= wgGetRevisionID_safe(CStringA(sProjectRoot
), NULL
);
404 youngest
= ConvertHashToRevnum(sha1
);
410 git_wc_status2_t
* GitStatus::GetFirstFileStatus(const CTGitPath
& path
, CTGitPath
& retPath
, bool update
, git_depth_t depth
, bool bNoIgnore
/* = true */, bool bNoExternals
/* = false */)
412 static git_wc_status2 st
;
416 m_fileCache.Init( CStringA( path.GetWinPathString().GetString() ) );
417 MessageBox(NULL, path.GetWinPathString(), _T("GetFirstFile"), MB_OK);
418 m_fileCache.m_pFileIter = m_fileCache.m_pFiles;
419 st.text_status = git_wc_status_none;
421 if (m_fileCache.m_pFileIter)
423 switch(m_fileCache.m_pFileIter->nStatus)
425 case WGFS_Normal: st.text_status = git_wc_status_normal; break;
426 case WGFS_Modified: st.text_status = git_wc_status_modified; break;
427 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;
430 //retPath.SetFromGit((const char*)item->key);
432 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;
438 const sort_item
* item
;
440 git_error_clear(m_err
);
441 m_statushash
= apr_hash_make(m_pool
);
442 m_externalhash
= apr_hash_make(m_pool
);
443 headrev
= Git_INVALID_REVNUM
;
444 git_opt_revision_t rev
;
445 rev
.kind
= git_opt_revision_unspecified
;
446 struct hashbaton_t hashbaton
;
447 hashbaton
.hash
= m_statushash
;
448 hashbaton
.exthash
= m_externalhash
;
449 hashbaton
.pThis
= this;
450 m_statushashindex
= 0;
451 m_err
= git_client_status4 (&headrev
,
452 path
.GetGitApiPath(m_pool
),
459 bNoIgnore
, //noignore
460 bNoExternals
, //noexternals
466 // Error present if function is not under version control
467 if ((m_err
!= NULL
) || (apr_hash_count(m_statushash
) == 0))
472 // Convert the unordered hash to an ordered, sorted array
473 m_statusarray
= sort_hash (m_statushash
,
474 sort_compare_items_as_paths
,
477 // only the first entry is needed (no recurse)
478 m_statushashindex
= 0;
479 item
= &APR_ARRAY_IDX (m_statusarray
, m_statushashindex
, const sort_item
);
480 retPath
.SetFromGit((const char*)item
->key
);
481 return (git_wc_status2_t
*) item
->value
;
487 unsigned int GitStatus::GetVersionedCount() const
489 // return /**/m_fileCache.GetFileCount();
491 unsigned int count
= 0;
493 const sort_item
* item
;
494 for (unsigned int i
=0; i
<apr_hash_count(m_statushash
); ++i
)
496 item
= &APR_ARRAY_IDX(m_statusarray
, i
, const sort_item
);
499 if (GitStatus::GetMoreImportant(((git_wc_status_t
*)item
->value
)->text_status
, git_wc_status_ignored
)!=git_wc_status_ignored
)
507 git_wc_status2_t
* GitStatus::GetNextFileStatus(CTGitPath
& retPath
)
509 static git_wc_status2 st
;
511 st
.text_status
= git_wc_status_none
;
513 /*if (m_fileCache.m_pFileIter)
515 switch(m_fileCache.m_pFileIter->nStatus)
517 case WGFS_Normal: st.text_status = git_wc_status_normal; break;
518 case WGFS_Modified: st.text_status = git_wc_status_modified; break;
519 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;
522 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;
528 const sort_item
* item
;
530 if ((m_statushashindex
+1) >= apr_hash_count(m_statushash
))
534 item
= &APR_ARRAY_IDX (m_statusarray
, m_statushashindex
, const sort_item
);
535 retPath
.SetFromGit((const char*)item
->key
);
536 return (git_wc_status2_t
*) item
->value
;
541 bool GitStatus::IsExternal(const CTGitPath
& path
) const
544 if (apr_hash_get(m_externalhash
, path
.GetGitApiPath(m_pool
), APR_HASH_KEY_STRING
))
550 bool GitStatus::IsInExternal(const CTGitPath
& path
) const
553 if (apr_hash_count(m_statushash
) == 0)
556 GitPool
localpool(m_pool
);
557 apr_hash_index_t
*hi
;
559 for (hi
= apr_hash_first(localpool
, m_externalhash
); hi
; hi
= apr_hash_next(hi
))
561 apr_hash_this(hi
, (const void**)&key
, NULL
, NULL
);
564 if (CTGitPath(CUnicodeUtils::GetUnicode(key
)).IsAncestorOf(path
))
573 void GitStatus::GetStatusString(git_wc_status_kind status
, size_t buflen
, TCHAR
* string
)
578 case git_wc_status_none
:
581 case git_wc_status_unversioned
:
582 buf
= _T("unversioned\0");
584 case git_wc_status_normal
:
585 buf
= _T("normal\0");
587 case git_wc_status_added
:
590 case git_wc_status_missing
:
591 buf
= _T("missing\0");
593 case git_wc_status_deleted
:
594 buf
= _T("deleted\0");
596 case git_wc_status_replaced
:
597 buf
= _T("replaced\0");
599 case git_wc_status_modified
:
600 buf
= _T("modified\0");
602 case git_wc_status_merged
:
603 buf
= _T("merged\0");
605 case git_wc_status_conflicted
:
606 buf
= _T("conflicted\0");
608 case git_wc_status_obstructed
:
609 buf
= _T("obstructed\0");
611 case git_wc_status_ignored
:
614 case git_wc_status_external
:
615 buf
= _T("external");
617 case git_wc_status_incomplete
:
618 buf
= _T("incomplete\0");
624 _stprintf_s(string
, buflen
, _T("%s"), buf
);
627 void GitStatus::GetStatusString(HINSTANCE hInst
, git_wc_status_kind status
, TCHAR
* string
, int size
, WORD lang
)
631 case git_wc_status_none
:
632 LoadStringEx(hInst
, IDS_STATUSNONE
, string
, size
, lang
);
634 case git_wc_status_unversioned
:
635 LoadStringEx(hInst
, IDS_STATUSUNVERSIONED
, string
, size
, lang
);
637 case git_wc_status_normal
:
638 LoadStringEx(hInst
, IDS_STATUSNORMAL
, string
, size
, lang
);
640 case git_wc_status_added
:
641 LoadStringEx(hInst
, IDS_STATUSADDED
, string
, size
, lang
);
643 case git_wc_status_missing
:
644 LoadStringEx(hInst
, IDS_STATUSABSENT
, string
, size
, lang
);
646 case git_wc_status_deleted
:
647 LoadStringEx(hInst
, IDS_STATUSDELETED
, string
, size
, lang
);
649 case git_wc_status_replaced
:
650 LoadStringEx(hInst
, IDS_STATUSREPLACED
, string
, size
, lang
);
652 case git_wc_status_modified
:
653 LoadStringEx(hInst
, IDS_STATUSMODIFIED
, string
, size
, lang
);
655 case git_wc_status_merged
:
656 LoadStringEx(hInst
, IDS_STATUSMERGED
, string
, size
, lang
);
658 case git_wc_status_conflicted
:
659 LoadStringEx(hInst
, IDS_STATUSCONFLICTED
, string
, size
, lang
);
661 case git_wc_status_ignored
:
662 LoadStringEx(hInst
, IDS_STATUSIGNORED
, string
, size
, lang
);
664 case git_wc_status_obstructed
:
665 LoadStringEx(hInst
, IDS_STATUSOBSTRUCTED
, string
, size
, lang
);
667 case git_wc_status_external
:
668 LoadStringEx(hInst
, IDS_STATUSEXTERNAL
, string
, size
, lang
);
670 case git_wc_status_incomplete
:
671 LoadStringEx(hInst
, IDS_STATUSINCOMPLETE
, string
, size
, lang
);
674 LoadStringEx(hInst
, IDS_STATUSNONE
, string
, size
, lang
);
680 CString
GitStatus::GetDepthString(git_depth_t depth
)
686 case git_depth_unknown
:
687 sDepth
.LoadString(IDS_Git_DEPTH_UNKNOWN
);
689 case git_depth_empty
:
690 sDepth
.LoadString(IDS_Git_DEPTH_EMPTY
);
692 case git_depth_files
:
693 sDepth
.LoadString(IDS_Git_DEPTH_FILES
);
695 case git_depth_immediates
:
696 sDepth
.LoadString(IDS_Git_DEPTH_IMMEDIATE
);
698 case git_depth_infinity
:
699 sDepth
.LoadString(IDS_Git_DEPTH_INFINITE
);
708 void GitStatus::GetDepthString(HINSTANCE hInst
, git_depth_t depth
, TCHAR
* string
, int size
, WORD lang
)
713 case git_depth_unknown
:
714 LoadStringEx(hInst
, IDS_SVN_DEPTH_UNKNOWN
, string
, size
, lang
);
716 case git_depth_empty
:
717 LoadStringEx(hInst
, IDS_SVN_DEPTH_EMPTY
, string
, size
, lang
);
719 case git_depth_files
:
720 LoadStringEx(hInst
, IDS_SVN_DEPTH_FILES
, string
, size
, lang
);
722 case git_depth_immediates
:
723 LoadStringEx(hInst
, IDS_SVN_DEPTH_IMMEDIATE
, string
, size
, lang
);
725 case git_depth_infinity
:
726 LoadStringEx(hInst
, IDS_SVN_DEPTH_INFINITE
, string
, size
, lang
);
733 int GitStatus::LoadStringEx(HINSTANCE hInstance
, UINT uID
, LPTSTR lpBuffer
, int nBufferMax
, WORD wLanguage
)
735 const STRINGRESOURCEIMAGE
* pImage
;
736 const STRINGRESOURCEIMAGE
* pImageEnd
;
742 HRSRC hResource
= FindResourceEx(hInstance
, RT_STRING
, MAKEINTRESOURCE(((uID
>>4)+1)), wLanguage
);
745 // try the default language before giving up!
746 hResource
= FindResource(hInstance
, MAKEINTRESOURCE(((uID
>>4)+1)), RT_STRING
);
750 hGlobal
= LoadResource(hInstance
, hResource
);
753 pImage
= (const STRINGRESOURCEIMAGE
*)::LockResource(hGlobal
);
757 nResourceSize
= ::SizeofResource(hInstance
, hResource
);
758 pImageEnd
= (const STRINGRESOURCEIMAGE
*)(LPBYTE(pImage
)+nResourceSize
);
761 while ((iIndex
> 0) && (pImage
< pImageEnd
))
763 pImage
= (const STRINGRESOURCEIMAGE
*)(LPBYTE(pImage
)+(sizeof(STRINGRESOURCEIMAGE
)+(pImage
->nLength
*sizeof(WCHAR
))));
766 if (pImage
>= pImageEnd
)
768 if (pImage
->nLength
== 0)
771 ret
= pImage
->nLength
;
772 if (pImage
->nLength
> nBufferMax
)
774 wcsncpy_s(lpBuffer
, nBufferMax
, pImage
->achString
, pImage
->nLength
-1);
775 lpBuffer
[nBufferMax
-1] = 0;
779 wcsncpy_s(lpBuffer
, nBufferMax
, pImage
->achString
, pImage
->nLength
);
785 BOOL
GitStatus::getallstatus(const struct wgFile_s
*pFile
, void *pUserData
)
787 git_wc_status_kind
* s
= (git_wc_status_kind
*)pUserData
;
788 *s
= GitStatus::GetMoreImportant(*s
, GitStatusFromWingit(pFile
->nStatus
));
792 BOOL
GitStatus::getstatus(const struct wgFile_s
*pFile
, void *pUserData
)
794 git_wc_status2_t
* s
= (git_wc_status2_t
*)pUserData
;
795 s
->prop_status
= s
->text_status
= GitStatus::GetMoreImportant(s
->prop_status
, GitStatusFromWingit(pFile
->nStatus
));
800 git_error_t
* GitStatus::getallstatus(void * baton
, const char * /*path*/, git_wc_status2_t
* status
, apr_pool_t
* /*pool*/)
802 git_wc_status_kind
* s
= (git_wc_status_kind
*)baton
;
803 *s
= GitStatus::GetMoreImportant(*s
, status
->text_status
);
804 *s
= GitStatus::GetMoreImportant(*s
, status
->prop_status
);
810 git_error_t
* GitStatus::getstatushash(void * baton
, const char * path
, git_wc_status2_t
* status
, apr_pool_t
* /*pool*/)
812 hashbaton_t
* hash
= (hashbaton_t
*)baton
;
813 const StdStrAVector
& filterList
= hash
->pThis
->m_filterFileList
;
814 if (status
->text_status
== git_wc_status_external
)
816 apr_hash_set (hash
->exthash
, apr_pstrdup(hash
->pThis
->m_pool
, path
), APR_HASH_KEY_STRING
, (const void*)1);
819 if(filterList
.size() > 0)
821 // We have a filter active - we're only interested in files which are in
823 if(!binary_search(filterList
.begin(), filterList
.end(), path
))
825 // This item is not in the filter - don't store it
829 git_wc_status2_t
* statuscopy
= git_wc_dup_status2 (status
, hash
->pThis
->m_pool
);
830 apr_hash_set (hash
->hash
, apr_pstrdup(hash
->pThis
->m_pool
, path
), APR_HASH_KEY_STRING
, statuscopy
);
834 apr_array_header_t
* GitStatus::sort_hash (apr_hash_t
*ht
,
835 int (*comparison_func
) (const GitStatus::sort_item
*, const GitStatus::sort_item
*),
838 apr_hash_index_t
*hi
;
839 apr_array_header_t
*ary
;
841 /* allocate an array with only one element to begin with. */
842 ary
= apr_array_make (pool
, 1, sizeof(sort_item
));
844 /* loop over hash table and push all keys into the array */
845 for (hi
= apr_hash_first (pool
, ht
); hi
; hi
= apr_hash_next (hi
))
847 sort_item
*item
= (sort_item
*)apr_array_push (ary
);
849 apr_hash_this (hi
, &item
->key
, &item
->klen
, &item
->value
);
852 /* now quick sort the array. */
853 qsort (ary
->elts
, ary
->nelts
, ary
->elt_size
,
854 (int (*)(const void *, const void *))comparison_func
);
859 int GitStatus::sort_compare_items_as_paths (const sort_item
*a
, const sort_item
*b
)
861 const char *astr
, *bstr
;
863 astr
= (const char*)a
->key
;
864 bstr
= (const char*)b
->key
;
865 return git_path_compare_paths (astr
, bstr
);
869 git_error_t
* GitStatus::cancel(void *baton
)
872 volatile bool * canceled
= (bool *)baton
;
876 temp
.LoadString(IDS_Git_USERCANCELLED
);
877 return git_error_create(Git_ERR_CANCELLED
, NULL
, CUnicodeUtils::GetUTF8(temp
));
886 // Set-up a filter to restrict the files which will have their status stored by a get-status
887 void GitStatus::SetFilter(const CTGitPathList
& fileList
)
889 m_filterFileList
.clear();
890 for(int fileIndex
= 0; fileIndex
< fileList
.GetCount(); fileIndex
++)
892 // m_filterFileList.push_back(fileList[fileIndex].GetGitApiPath(m_pool));
894 // Sort the list so that we can do binary searches
895 std::sort(m_filterFileList
.begin(), m_filterFileList
.end());
898 void GitStatus::ClearFilter()
900 m_filterFileList
.clear();