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.
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"
35 //# include "MessageBox.h"
36 //# include "registry.h"
37 //# include "TGitPath.h"
38 //# include "PathUtils.h"
43 CGitIndexFileMap g_IndexFileMap
;
44 CGitHeadFileMap g_HeadFileMap
;
45 CGitIgnoreList g_IgnoreList
;
47 GitStatus::GitStatus(bool * pbCanceled
)
51 m_pool
= git_pool_create (NULL
);
53 git_error_clear(git_client_create_context(&ctx
, m_pool
));
57 ctx
->cancel_func
= cancel
;
58 ctx
->cancel_baton
= pbCanceled
;
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
);
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
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
,
87 git_config_set(cfg
, Git_CONFIG_SECTION_TUNNELS
, "ssh", CUnicodeUtils::GetUTF8(tgit_ssh
));
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
);
99 GitStatus::~GitStatus(void)
102 git_error_clear(m_err
);
103 git_pool_destroy (m_pool
); // free the allocated memory
107 void GitStatus::ClearPool()
110 git_pool_clear(m_pool
);
115 CString
GitStatus::GetLastErrorMsg() const
117 // return Git::GetErrorString(m_err);
121 stdstring
GitStatus::GetLastErrorMsg() const
130 git_error_t
* ErrPtr
= m_err
;
133 msg
= CUnicodeUtils::StdGetUnicode(ErrPtr
->message
);
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. */
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
);
149 git_error_clear (temp_err
);
150 msg
= _T("Can't recode error string from APR");
154 msg
= CUnicodeUtils::StdGetUnicode(err_string
);
160 while (ErrPtr
->child
)
162 ErrPtr
= ErrPtr
->child
;
166 msg
+= CUnicodeUtils::StdGetUnicode(ErrPtr
->message
);
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. */
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
);
182 git_error_clear (temp_err
);
183 msg
+= _T("Can't recode error string from APR");
187 msg
+= CUnicodeUtils::StdGetUnicode(err_string
);
194 } // if (m_err != NULL)
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;
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
)
230 if ((DWORD
)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION
) ? 2 : 1) == 2)
233 // gitindex.h based status
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());
242 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength() - 1/*otherwise it gets initial slash*/);
245 err
= g_IndexFileMap
.GetFileStatus(sProjectRoot
,sSubPath
,&statuskind
);
249 LPCTSTR lpszSubPath
= NULL
;
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('\\'))
262 // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here
263 UINT nFlags
= WGEFF_SingleFile
;
265 nFlags
|= WGEFF_NoRecurse
;
267 // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)
268 nFlags
|= WGEFF_EmptyAsNormal
;
270 // enumerate all files, recursively if requested
273 nFlags
|= WGEFF_NoRecurse
;
276 //err = !wgEnumFiles(sProjectRoot, lpszSubPath, nFlags, &getallstatus, &statuskind);
280 err
= GetDirStatus(sProjectRoot
,CString(lpszSubPath
),&statuskind
, true,bIsRecursive
,NULL
, NULL
);
284 err
= GetFileStatus(sProjectRoot
,CString(lpszSubPath
),&statuskind
,true, false,NULL
,NULL
);
288 /*err = git_client_status4 (&youngest,
289 path.GetSVNApiPath(pool),
297 FALSE, //ignore externals
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
317 git_wc_status_kind
GitStatus::GetAllStatusRecursive(const CTGitPath
& path
)
319 return GetAllStatus(path
, git_depth_infinity
);
323 git_wc_status_kind
GitStatus::GetMoreImportant(git_wc_status_kind status1
, git_wc_status_kind status2
)
325 if (GetStatusRanking(status1
) >= GetStatusRanking(status2
))
329 // static private method
330 int GitStatus::GetStatusRanking(git_wc_status_kind status
)
334 case git_wc_status_none
:
336 case git_wc_status_unversioned
:
338 case git_wc_status_ignored
:
340 case git_wc_status_incomplete
:
342 case git_wc_status_normal
:
343 case git_wc_status_external
:
345 case git_wc_status_added
:
347 case git_wc_status_missing
:
349 case git_wc_status_deleted
:
351 case git_wc_status_replaced
:
353 case git_wc_status_modified
:
355 case git_wc_status_merged
:
357 case git_wc_status_conflicted
:
359 case git_wc_status_obstructed
:
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
) )
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
)
395 if ((DWORD
)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION
) ? 2 : 1) == 2)
398 // gitindex.h based status
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());
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
);
416 LPCTSTR lpszSubPath
= NULL
;
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('\\'))
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
;
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
);
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),
452 git_depth_empty, //depth
462 // Error present if function is not under version control
463 if (m_err
) /*|| (apr_hash_count(statushash) == 0)*/
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,
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;
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")));
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
;
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;
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
),
540 bNoIgnore
, //noignore
541 bNoExternals
, //noexternals
547 // Error present if function is not under version control
548 if ((m_err
!= NULL
) || (apr_hash_count(m_statushash
) == 0))
553 // Convert the unordered hash to an ordered, sorted array
554 m_statusarray
= sort_hash (m_statushash
,
555 sort_compare_items_as_paths
,
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
;
568 unsigned int GitStatus::GetVersionedCount() const
570 // return /**/m_fileCache.GetFileCount();
572 unsigned int count
= 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
);
580 if (GitStatus::GetMoreImportant(((git_wc_status_t
*)item
->value
)->text_status
, git_wc_status_ignored
)!=git_wc_status_ignored
)
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;
609 const sort_item
* item
;
611 if ((m_statushashindex
+1) >= apr_hash_count(m_statushash
))
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
;
622 bool GitStatus::IsExternal(const CTGitPath
& path
) const
625 if (apr_hash_get(m_externalhash
, path
.GetGitApiPath(m_pool
), APR_HASH_KEY_STRING
))
631 bool GitStatus::IsInExternal(const CTGitPath
& path
) const
634 if (apr_hash_count(m_statushash
) == 0)
637 GitPool
localpool(m_pool
);
638 apr_hash_index_t
*hi
;
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
);
645 if (CTGitPath(CUnicodeUtils::GetUnicode(key
)).IsAncestorOf(path
))
654 void GitStatus::GetStatusString(git_wc_status_kind status
, size_t buflen
, TCHAR
* string
)
659 case git_wc_status_none
:
662 case git_wc_status_unversioned
:
663 buf
= _T("unversioned\0");
665 case git_wc_status_normal
:
666 buf
= _T("normal\0");
668 case git_wc_status_added
:
671 case git_wc_status_missing
:
672 buf
= _T("missing\0");
674 case git_wc_status_deleted
:
675 buf
= _T("deleted\0");
677 case git_wc_status_replaced
:
678 buf
= _T("replaced\0");
680 case git_wc_status_modified
:
681 buf
= _T("modified\0");
683 case git_wc_status_merged
:
684 buf
= _T("merged\0");
686 case git_wc_status_conflicted
:
687 buf
= _T("conflicted\0");
689 case git_wc_status_obstructed
:
690 buf
= _T("obstructed\0");
692 case git_wc_status_ignored
:
695 case git_wc_status_external
:
696 buf
= _T("external");
698 case git_wc_status_incomplete
:
699 buf
= _T("incomplete\0");
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
)
712 case git_wc_status_none
:
713 LoadStringEx(hInst
, IDS_STATUSNONE
, string
, size
, lang
);
715 case git_wc_status_unversioned
:
716 LoadStringEx(hInst
, IDS_STATUSUNVERSIONED
, string
, size
, lang
);
718 case git_wc_status_normal
:
719 LoadStringEx(hInst
, IDS_STATUSNORMAL
, string
, size
, lang
);
721 case git_wc_status_added
:
722 LoadStringEx(hInst
, IDS_STATUSADDED
, string
, size
, lang
);
724 case git_wc_status_missing
:
725 LoadStringEx(hInst
, IDS_STATUSABSENT
, string
, size
, lang
);
727 case git_wc_status_deleted
:
728 LoadStringEx(hInst
, IDS_STATUSDELETED
, string
, size
, lang
);
730 case git_wc_status_replaced
:
731 LoadStringEx(hInst
, IDS_STATUSREPLACED
, string
, size
, lang
);
733 case git_wc_status_modified
:
734 LoadStringEx(hInst
, IDS_STATUSMODIFIED
, string
, size
, lang
);
736 case git_wc_status_merged
:
737 LoadStringEx(hInst
, IDS_STATUSMERGED
, string
, size
, lang
);
739 case git_wc_status_conflicted
:
740 LoadStringEx(hInst
, IDS_STATUSCONFLICTED
, string
, size
, lang
);
742 case git_wc_status_ignored
:
743 LoadStringEx(hInst
, IDS_STATUSIGNORED
, string
, size
, lang
);
745 case git_wc_status_obstructed
:
746 LoadStringEx(hInst
, IDS_STATUSOBSTRUCTED
, string
, size
, lang
);
748 case git_wc_status_external
:
749 LoadStringEx(hInst
, IDS_STATUSEXTERNAL
, string
, size
, lang
);
751 case git_wc_status_incomplete
:
752 LoadStringEx(hInst
, IDS_STATUSINCOMPLETE
, string
, size
, lang
);
755 LoadStringEx(hInst
, IDS_STATUSNONE
, string
, size
, lang
);
761 CString
GitStatus::GetDepthString(git_depth_t depth
)
767 case git_depth_unknown
:
768 sDepth
.LoadString(IDS_Git_DEPTH_UNKNOWN
);
770 case git_depth_empty
:
771 sDepth
.LoadString(IDS_Git_DEPTH_EMPTY
);
773 case git_depth_files
:
774 sDepth
.LoadString(IDS_Git_DEPTH_FILES
);
776 case git_depth_immediates
:
777 sDepth
.LoadString(IDS_Git_DEPTH_IMMEDIATE
);
779 case git_depth_infinity
:
780 sDepth
.LoadString(IDS_Git_DEPTH_INFINITE
);
789 void GitStatus::GetDepthString(HINSTANCE hInst
, git_depth_t depth
, TCHAR
* string
, int size
, WORD lang
)
794 case git_depth_unknown
:
795 LoadStringEx(hInst
, IDS_SVN_DEPTH_UNKNOWN
, string
, size
, lang
);
797 case git_depth_empty
:
798 LoadStringEx(hInst
, IDS_SVN_DEPTH_EMPTY
, string
, size
, lang
);
800 case git_depth_files
:
801 LoadStringEx(hInst
, IDS_SVN_DEPTH_FILES
, string
, size
, lang
);
803 case git_depth_immediates
:
804 LoadStringEx(hInst
, IDS_SVN_DEPTH_IMMEDIATE
, string
, size
, lang
);
806 case git_depth_infinity
:
807 LoadStringEx(hInst
, IDS_SVN_DEPTH_INFINITE
, string
, size
, lang
);
814 int GitStatus::LoadStringEx(HINSTANCE hInstance
, UINT uID
, LPTSTR lpBuffer
, int nBufferMax
, WORD wLanguage
)
816 const STRINGRESOURCEIMAGE
* pImage
;
817 const STRINGRESOURCEIMAGE
* pImageEnd
;
823 HRSRC hResource
= FindResourceEx(hInstance
, RT_STRING
, MAKEINTRESOURCE(((uID
>>4)+1)), wLanguage
);
826 // try the default language before giving up!
827 hResource
= FindResource(hInstance
, MAKEINTRESOURCE(((uID
>>4)+1)), RT_STRING
);
831 hGlobal
= LoadResource(hInstance
, hResource
);
834 pImage
= (const STRINGRESOURCEIMAGE
*)::LockResource(hGlobal
);
838 nResourceSize
= ::SizeofResource(hInstance
, hResource
);
839 pImageEnd
= (const STRINGRESOURCEIMAGE
*)(LPBYTE(pImage
)+nResourceSize
);
842 while ((iIndex
> 0) && (pImage
< pImageEnd
))
844 pImage
= (const STRINGRESOURCEIMAGE
*)(LPBYTE(pImage
)+(sizeof(STRINGRESOURCEIMAGE
)+(pImage
->nLength
*sizeof(WCHAR
))));
847 if (pImage
>= pImageEnd
)
849 if (pImage
->nLength
== 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;
860 wcsncpy_s(lpBuffer
, nBufferMax
, pImage
->achString
, pImage
->nLength
);
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
));
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
));
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
);
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);
900 if(filterList
.size() > 0)
902 // We have a filter active - we're only interested in files which are in
904 if(!binary_search(filterList
.begin(), filterList
.end(), path
))
906 // This item is not in the filter - don't store it
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
);
915 apr_array_header_t
* GitStatus::sort_hash (apr_hash_t
*ht
,
916 int (*comparison_func
) (const GitStatus::sort_item
*, const GitStatus::sort_item
*),
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
);
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
);
950 git_error_t
* GitStatus::cancel(void *baton
)
953 volatile bool * canceled
= (bool *)baton
;
957 temp
.LoadString(IDS_Git_USERCANCELLED
);
958 return git_error_create(Git_ERR_CANCELLED
, NULL
, CUnicodeUtils::GetUTF8(temp
));
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();
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
);
1008 git_wc_status_kind st
= git_wc_status_none
;
1011 g_IndexFileMap
.GetFileStatus(gitdir
,path
,&st
,IsFull
,false, NULL
,NULL
,&hash
);
1013 if( st
== git_wc_status_conflicted
)
1017 callback(path
,st
,pData
);
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
;
1033 callback(path
,st
,pData
);
1037 if( st
== git_wc_status_normal
)
1039 if(g_HeadFileMap
[gitdir
].CheckHeadUpdate())
1041 g_HeadFileMap
[gitdir
].ReadHeadHash(gitdir
);
1043 if( g_HeadFileMap
[gitdir
].m_HeadFile
.IsEmpty() )
1045 *status
=st
=git_wc_status_added
;
1047 callback(path
,st
,pData
);
1050 if(g_HeadFileMap
[gitdir
].ReadTree())
1052 g_HeadFileMap
[gitdir
].m_LastModifyTimeHead
= 0;
1055 callback(path
,st
,pData
);
1060 // Check Head Tree Hash;
1064 if(g_HeadFileMap
[gitdir
].m_Map
.find(path
)
1065 == g_HeadFileMap
[gitdir
].m_Map
.end())
1067 *status
=st
=git_wc_status_added
;
1069 callback(path
,st
,pData
);
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
;
1080 callback(path
,st
,pData
);
1088 callback(path
,st
,pData
);
1093 ::SetCurrentDirectory(oldpath
);
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
);
1117 g_IndexFileMap
.CheckAndUpdateIndex(gitdir
);
1118 int pos
=SearchInSortVector(g_IndexFileMap
[gitdir
],path
.GetBuffer(),path
.GetLength());
1120 //Not In Version Contorl
1125 *status
= git_wc_status_unversioned
;
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
;
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
;
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
;
1161 for(int i
=start
;i
<=end
;i
++)
1163 if( ((*it
).m_Flags
& CE_STAGEMASK
) !=0)
1165 *status
= git_wc_status_conflicted
;
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())
1181 g_Git
.SetCurrentDir(gitdir
);
1182 ::GetCurrentDirectory(MAX_PATH
,oldpath
);
1183 ::SetCurrentDirectory(g_Git
.m_CurrentDir
);
1185 g_HeadFileMap
[gitdir
].ReadTree();
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())
1200 pos
= g_HeadFileMap
[gitdir
].m_Map
[(*it
).m_FileName
];
1204 *status
= git_wc_status_added
;
1208 if( g_HeadFileMap
[gitdir
][pos
].m_Hash
!= (*it
).m_IndexHash
)
1210 *status
== git_wc_status_modified
;
1218 if( *status
== git_wc_status_normal
)
1220 pos
= SearchInSortVector(g_HeadFileMap
[gitdir
], path
.GetBuffer(), path
.GetLength());
1223 *status
= git_wc_status_added
;
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
;
1246 it
= g_IndexFileMap
[gitdir
].begin()+start
;
1247 for(int i
=start
;i
<=end
;i
++)
1250 CString fullpath
=gitdir
;
1251 fullpath
+= _T("\\");
1252 fullpath
+= (*it
).m_FileName
;
1255 //skip child directory
1256 if((*it
).m_FileName
.Find(_T('/'), path
.GetLength()) + 1>0)
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
;
1274 if(callback
) callback(path
,*status
,pData
);
1278 ::SetCurrentDirectory(oldpath
);
1280 g_Git
.m_critGitDllSec
.Unlock();