OSDN Git Service

Suppressed compiler warning
[tortoisegit/TortoiseGitJp.git] / src / Git / GitStatus.cpp
1 // TortoiseGit - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseGit\r
4 \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
9 \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
14 \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
18 //\r
19 \r
20 #include "stdafx.h"\r
21 #ifdef _TORTOISESHELL\r
22 #include "ShellExt.h"\r
23 #else\r
24 #include "registry.h"\r
25 #endif\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
33 #ifdef _MFC_VER\r
34 //#     include "Git.h"\r
35 //#     include "MessageBox.h"\r
36 //#     include "registry.h"\r
37 //#     include "TGitPath.h"\r
38 //#     include "PathUtils.h"\r
39 #endif\r
40 #include "git.h"\r
41 #include "gitindex.h"\r
42 \r
43 CGitIndexFileMap g_IndexFileMap;\r
44 \r
45 GitStatus::GitStatus(bool * pbCanceled)\r
46         : status(NULL)\r
47 {\r
48 #if 0\r
49         m_pool = git_pool_create (NULL);\r
50         \r
51         git_error_clear(git_client_create_context(&ctx, m_pool));\r
52         \r
53         if (pbCanceled)\r
54         {\r
55                 ctx->cancel_func = cancel;\r
56                 ctx->cancel_baton = pbCanceled;\r
57         }\r
58 \r
59 #ifdef _MFC_VER\r
60         git_error_clear(git_config_ensure(NULL, m_pool));\r
61         \r
62         // set up authentication\r
63         m_prompt.Init(m_pool, ctx);\r
64 \r
65         // set up the configuration\r
66         m_err = git_config_get_config (&(ctx->config), g_pConfigDir, m_pool);\r
67 \r
68         if (m_err)\r
69         {\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
73                 exit(-1);\r
74         }\r
75 \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
82         {\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
86         }\r
87 #else\r
88         git_error_clear(git_config_ensure(NULL, m_pool));\r
89 \r
90         // set up the configuration\r
91         m_err = git_config_get_config (&(ctx->config), g_pConfigDir, m_pool);\r
92 \r
93 #endif\r
94 #endif\r
95 }\r
96 \r
97 GitStatus::~GitStatus(void)\r
98 {\r
99 #if 0\r
100         git_error_clear(m_err);\r
101         git_pool_destroy (m_pool);                                      // free the allocated memory\r
102 #endif\r
103 }\r
104 \r
105 void GitStatus::ClearPool()\r
106 {\r
107 #if 0\r
108         git_pool_clear(m_pool);\r
109 #endif\r
110 }\r
111 \r
112 #ifdef _MFC_VER\r
113 CString GitStatus::GetLastErrorMsg() const\r
114 {\r
115 //      return Git::GetErrorString(m_err);\r
116         return CString("");\r
117 }\r
118 #else\r
119 stdstring GitStatus::GetLastErrorMsg() const\r
120 {\r
121 \r
122         stdstring msg;\r
123 #if 0\r
124         char errbuf[256];\r
125 \r
126         if (m_err != NULL)\r
127         {\r
128                 git_error_t * ErrPtr = m_err;\r
129                 if (ErrPtr->message)\r
130                 {\r
131                         msg = CUnicodeUtils::StdGetUnicode(ErrPtr->message);\r
132                 }\r
133                 else\r
134                 {\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
140                         else\r
141                         {\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
145                                 if (temp_err)\r
146                                 {\r
147                                         git_error_clear (temp_err);\r
148                                         msg = _T("Can't recode error string from APR");\r
149                                 }\r
150                                 else\r
151                                 {\r
152                                         msg = CUnicodeUtils::StdGetUnicode(err_string);\r
153                                 }\r
154                         }\r
155 \r
156                 }\r
157 \r
158                 while (ErrPtr->child)\r
159                 {\r
160                         ErrPtr = ErrPtr->child;\r
161                         msg += _T("\n");\r
162                         if (ErrPtr->message)\r
163                         {\r
164                                 msg += CUnicodeUtils::StdGetUnicode(ErrPtr->message);\r
165                         }\r
166                         else\r
167                         {\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
173                                 else\r
174                                 {\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
178                                         if (temp_err)\r
179                                         {\r
180                                                 git_error_clear (temp_err);\r
181                                                 msg += _T("Can't recode error string from APR");\r
182                                         }\r
183                                         else\r
184                                         {\r
185                                                 msg += CUnicodeUtils::StdGetUnicode(err_string);\r
186                                         }\r
187                                 }\r
188 \r
189                         }\r
190                 }\r
191                 return msg;\r
192         } // if (m_err != NULL)\r
193 #endif\r
194         return msg;\r
195 }\r
196 #endif\r
197 \r
198 // static method\r
199 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth)\r
200 {\r
201         git_wc_status_kind                      statuskind;\r
202 //      git_client_ctx_t *                      ctx;\r
203         \r
204 //      apr_pool_t *                            pool;\r
205 //      git_error_t *                           err;\r
206         BOOL                                            err;\r
207         BOOL                                            isDir;\r
208         CString                                         sProjectRoot;\r
209 \r
210         isDir = path.IsDirectory();\r
211         if (!path.HasAdminDir(&sProjectRoot))\r
212                 return git_wc_status_none;\r
213 \r
214 //      pool = git_pool_create (NULL);                          // create the memory pool\r
215 \r
216 //      git_error_clear(git_client_create_context(&ctx, pool));\r
217 \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
222 \r
223         const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source\r
224 \r
225 #ifdef _TORTOISESHELL\r
226         if (g_ShellCache.GetCacheType() == ShellCache::dll)\r
227 #else\r
228         if ((DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION) ? 2 : 1) == 2)\r
229 #endif\r
230         {\r
231                 // gitindex.h based status\r
232 \r
233                 CString sSubPath;\r
234                 CString s = path.GetWinPathString();\r
235                 if (s.GetLength() > sProjectRoot.GetLength())\r
236                 {\r
237                         sSubPath = CStringA(s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/));\r
238                 }\r
239 \r
240                 err = g_IndexFileMap.GetFileStatus(sProjectRoot,sSubPath,&statuskind);\r
241         }\r
242         else\r
243         {\r
244                 LPCTSTR lpszSubPath = NULL;\r
245                 CString sSubPath;\r
246                 CString s = path.GetWinPathString();\r
247                 if (s.GetLength() > sProjectRoot.GetLength())\r
248                 {\r
249                         sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);\r
250                         lpszSubPath = sSubPath;\r
251                 }\r
252 \r
253 #if 1\r
254                 // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here\r
255                 UINT nFlags = WGEFF_SingleFile;\r
256                 if (!bIsRecursive)\r
257                         nFlags |= WGEFF_NoRecurse;\r
258                 if (!lpszSubPath)\r
259                         // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)\r
260                         nFlags |= WGEFF_EmptyAsNormal;\r
261 #else\r
262                 // enumerate all files, recursively if requested\r
263                 UINT nFlags = 0;\r
264                 if (!bIsRecursive)\r
265                         nFlags |= WGEFF_NoRecurse;\r
266 #endif\r
267 \r
268                 err = !wgEnumFiles(sProjectRoot, lpszSubPath, nFlags, &getallstatus, &statuskind);\r
269 \r
270                 /*err = git_client_status4 (&youngest,\r
271                                                         path.GetSVNApiPath(pool),\r
272                                                         &rev,\r
273                                                         getallstatus,\r
274                                                         &statuskind,\r
275                                                         depth,\r
276                                                         TRUE,           //getall\r
277                                                         FALSE,          //update\r
278                                                         TRUE,           //noignore\r
279                                                         FALSE,          //ignore externals\r
280                                                         NULL,\r
281                                                         ctx,\r
282                                                         pool);*/\r
283         }\r
284 \r
285         // Error present\r
286         if (err != NULL)\r
287         {\r
288 //              git_error_clear(err);\r
289 //              git_pool_destroy (pool);                                //free allocated memory\r
290                 return git_wc_status_none;      \r
291         }\r
292 \r
293 //      git_pool_destroy (pool);                                //free allocated memory\r
294 \r
295         return statuskind;\r
296 }\r
297 \r
298 // static method\r
299 git_wc_status_kind GitStatus::GetAllStatusRecursive(const CTGitPath& path)\r
300 {\r
301         return GetAllStatus(path, git_depth_infinity);\r
302 }\r
303 \r
304 // static method\r
305 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)\r
306 {\r
307         if (GetStatusRanking(status1) >= GetStatusRanking(status2))\r
308                 return status1;\r
309         return status2;\r
310 }\r
311 // static private method\r
312 int GitStatus::GetStatusRanking(git_wc_status_kind status)\r
313 {\r
314         switch (status)\r
315         {\r
316                 case git_wc_status_none:\r
317                         return 0;\r
318                 case git_wc_status_unversioned:\r
319                         return 1;\r
320                 case git_wc_status_ignored:\r
321                         return 2;\r
322                 case git_wc_status_incomplete:\r
323                         return 4;\r
324                 case git_wc_status_normal:\r
325                 case git_wc_status_external:\r
326                         return 5;\r
327                 case git_wc_status_added:\r
328                         return 6;\r
329                 case git_wc_status_missing:\r
330                         return 7;\r
331                 case git_wc_status_deleted:\r
332                         return 8;\r
333                 case git_wc_status_replaced:\r
334                         return 9;\r
335                 case git_wc_status_modified:\r
336                         return 10;\r
337                 case git_wc_status_merged:\r
338                         return 11;\r
339                 case git_wc_status_conflicted:\r
340                         return 12;\r
341                 case git_wc_status_obstructed:\r
342                         return 13;\r
343         }\r
344         return 0;\r
345 }\r
346 \r
347 git_revnum_t GitStatus::GetStatus(const CTGitPath& path, bool update /* = false */, bool noignore /* = false */, bool noexternals /* = false */)\r
348 {\r
349         // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of\r
350         //       Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right\r
351         //       after the call again\r
352 \r
353 //      apr_hash_t *                            statushash;\r
354 //      apr_hash_t *                            exthash;\r
355 //      apr_array_header_t *            statusarray;\r
356 //      const sort_item*                        item;\r
357         \r
358 //      git_error_clear(m_err);\r
359 //      statushash = apr_hash_make(m_pool);\r
360 //      exthash = apr_hash_make(m_pool);\r
361         git_revnum_t youngest = GIT_INVALID_REVNUM;\r
362 //      git_opt_revision_t rev;\r
363 //      rev.kind = git_opt_revision_unspecified;\r
364 \r
365         CString sProjectRoot;\r
366         if ( !path.HasAdminDir(&sProjectRoot) )\r
367                 return youngest;\r
368 \r
369         struct hashbaton_t hashbaton;\r
370 //      hashbaton.hash = statushash;\r
371 //      hashbaton.exthash = exthash;\r
372         hashbaton.pThis = this;\r
373 \r
374 #ifdef _TORTOISESHELL\r
375         if (g_ShellCache.GetCacheType() == ShellCache::dll)\r
376 #else\r
377         if ((DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION) ? 2 : 1) == 2)\r
378 #endif\r
379         {\r
380                 // gitindex.h based status\r
381 \r
382                 CString sSubPath;\r
383                 CString s = path.GetWinPathString();\r
384                 if (s.GetLength() > sProjectRoot.GetLength())\r
385                 {\r
386                         sSubPath = CString(s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/));\r
387                 }\r
388 \r
389                 m_status.prop_status = m_status.text_status = git_wc_status_none;\r
390 \r
391                 m_err = g_IndexFileMap.GetFileStatus(sProjectRoot,sSubPath,&m_status.text_status);\r
392         }\r
393         else\r
394         {\r
395                 LPCTSTR lpszSubPath = NULL;\r
396                 CString sSubPath;\r
397                 CString s = path.GetWinPathString();\r
398                 if (s.GetLength() > sProjectRoot.GetLength())\r
399                 {\r
400                         sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);\r
401                         lpszSubPath = sSubPath;\r
402                 }\r
403 \r
404                 // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here\r
405                 UINT nFlags = WGEFF_SingleFile | WGEFF_NoRecurse;\r
406                 if (!lpszSubPath)\r
407                         // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)\r
408                         nFlags |= WGEFF_EmptyAsNormal;\r
409 \r
410                 m_status.prop_status = m_status.text_status = git_wc_status_none;\r
411 \r
412                 // NOTE: currently wgEnumFiles will not enumerate file if it isn't versioned (so status will be git_wc_status_none)\r
413                 m_err = !wgEnumFiles(sProjectRoot, lpszSubPath, nFlags, &getstatus, &m_status);\r
414 \r
415                 /*m_err = git_client_status4 (&youngest,\r
416                                                         path.GetGitApiPath(m_pool),\r
417                                                         &rev,\r
418                                                         getstatushash,\r
419                                                         &hashbaton,\r
420                                                         git_depth_empty,                //depth\r
421                                                         TRUE,           //getall\r
422                                                         update,         //update\r
423                                                         noignore,               //noignore\r
424                                                         noexternals,\r
425                                                         NULL,\r
426                                                         ctx,\r
427                                                         m_pool);*/\r
428         }\r
429 \r
430         // Error present if function is not under version control\r
431         if (m_err) /*|| (apr_hash_count(statushash) == 0)*/\r
432         {\r
433                 status = NULL;\r
434 //              return -2;      \r
435                 return GIT_INVALID_REVNUM;\r
436         }\r
437 \r
438         // Convert the unordered hash to an ordered, sorted array\r
439         /*statusarray = sort_hash (statushash,\r
440                                                           sort_compare_items_as_paths,\r
441                                                           m_pool);*/\r
442 \r
443         // only the first entry is needed (no recurse)\r
444 //      item = &APR_ARRAY_IDX (statusarray, 0, const sort_item);\r
445         \r
446 //      status = (git_wc_status2_t *) item->value;\r
447         status = &m_status;\r
448 \r
449         if (update)\r
450         {\r
451                 // done to match TSVN functionality of this function (not sure if any code uses the reutrn val)\r
452                 // if TGit does not need this, then change the return type of function\r
453                 youngest = g_Git.GetHash(CString(_T("HEAD")));\r
454         }\r
455 \r
456         return youngest;\r
457 }\r
458 \r
459 git_wc_status2_t * GitStatus::GetFirstFileStatus(const CTGitPath& path, CTGitPath& retPath, bool update, git_depth_t depth, bool bNoIgnore /* = true */, bool bNoExternals /* = false */)\r
460 {\r
461         static git_wc_status2 st;\r
462 /*\r
463         m_fileCache.Reset();\r
464 \r
465         m_fileCache.Init( CStringA( path.GetWinPathString().GetString() ) );\r
466 MessageBox(NULL, path.GetWinPathString(), _T("GetFirstFile"), MB_OK);\r
467         m_fileCache.m_pFileIter = m_fileCache.m_pFiles;\r
468         st.text_status = git_wc_status_none;\r
469 \r
470         if (m_fileCache.m_pFileIter)\r
471         {\r
472                 switch(m_fileCache.m_pFileIter->nStatus)\r
473                 {\r
474                 case WGFS_Normal: st.text_status = git_wc_status_normal; break;\r
475                 case WGFS_Modified: st.text_status = git_wc_status_modified; break;\r
476                 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;\r
477                 }\r
478 \r
479                 //retPath.SetFromGit((const char*)item->key);\r
480 \r
481                 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;\r
482         }\r
483 \r
484         return &st;\r
485 */\r
486 #if 0\r
487         const sort_item*                        item;\r
488 \r
489         git_error_clear(m_err);\r
490         m_statushash = apr_hash_make(m_pool);\r
491         m_externalhash = apr_hash_make(m_pool);\r
492         headrev = Git_INVALID_REVNUM;\r
493         git_opt_revision_t rev;\r
494         rev.kind = git_opt_revision_unspecified;\r
495         struct hashbaton_t hashbaton;\r
496         hashbaton.hash = m_statushash;\r
497         hashbaton.exthash = m_externalhash;\r
498         hashbaton.pThis = this;\r
499         m_statushashindex = 0;\r
500         m_err = git_client_status4 (&headrev,\r
501                                                         path.GetGitApiPath(m_pool),\r
502                                                         &rev,\r
503                                                         getstatushash,\r
504                                                         &hashbaton,\r
505                                                         depth,\r
506                                                         TRUE,           //getall\r
507                                                         update,         //update\r
508                                                         bNoIgnore,      //noignore\r
509                                                         bNoExternals,           //noexternals\r
510                                                         NULL,\r
511                                                         ctx,\r
512                                                         m_pool);\r
513 \r
514 \r
515         // Error present if function is not under version control\r
516         if ((m_err != NULL) || (apr_hash_count(m_statushash) == 0))\r
517         {\r
518                 return NULL;    \r
519         }\r
520 \r
521         // Convert the unordered hash to an ordered, sorted array\r
522         m_statusarray = sort_hash (m_statushash,\r
523                                                                 sort_compare_items_as_paths,\r
524                                                                 m_pool);\r
525 \r
526         // only the first entry is needed (no recurse)\r
527         m_statushashindex = 0;\r
528         item = &APR_ARRAY_IDX (m_statusarray, m_statushashindex, const sort_item);\r
529         retPath.SetFromGit((const char*)item->key);\r
530         return (git_wc_status2_t *) item->value;\r
531 #endif\r
532 \r
533         return 0;\r
534 }\r
535 \r
536 unsigned int GitStatus::GetVersionedCount() const\r
537 {\r
538 //      return /**/m_fileCache.GetFileCount();\r
539 \r
540         unsigned int count = 0;\r
541 #if 0\r
542         const sort_item* item;\r
543         for (unsigned int i=0; i<apr_hash_count(m_statushash); ++i)\r
544         {\r
545                 item = &APR_ARRAY_IDX(m_statusarray, i, const sort_item);\r
546                 if (item)\r
547                 {\r
548                         if (GitStatus::GetMoreImportant(((git_wc_status_t *)item->value)->text_status, git_wc_status_ignored)!=git_wc_status_ignored)\r
549                                 count++;                                \r
550                 }\r
551         }\r
552 #endif\r
553         return count;\r
554 }\r
555 \r
556 git_wc_status2_t * GitStatus::GetNextFileStatus(CTGitPath& retPath)\r
557 {\r
558         static git_wc_status2 st;\r
559 \r
560         st.text_status = git_wc_status_none;\r
561 \r
562         /*if (m_fileCache.m_pFileIter)\r
563         {\r
564                 switch(m_fileCache.m_pFileIter->nStatus)\r
565                 {\r
566                 case WGFS_Normal: st.text_status = git_wc_status_normal; break;\r
567                 case WGFS_Modified: st.text_status = git_wc_status_modified; break;\r
568                 case WGFS_Deleted: st.text_status = git_wc_status_deleted; break;\r
569                 }\r
570 \r
571                 m_fileCache.m_pFileIter = m_fileCache.m_pFileIter->pNext;\r
572         }*/\r
573 \r
574         return &st;\r
575 \r
576 #if 0\r
577         const sort_item*                        item;\r
578 \r
579         if ((m_statushashindex+1) >= apr_hash_count(m_statushash))\r
580                 return NULL;\r
581         m_statushashindex++;\r
582 \r
583         item = &APR_ARRAY_IDX (m_statusarray, m_statushashindex, const sort_item);\r
584         retPath.SetFromGit((const char*)item->key);\r
585         return (git_wc_status2_t *) item->value;\r
586 #endif\r
587         return 0;\r
588 }\r
589 \r
590 bool GitStatus::IsExternal(const CTGitPath& path) const\r
591 {\r
592 #if 0\r
593         if (apr_hash_get(m_externalhash, path.GetGitApiPath(m_pool), APR_HASH_KEY_STRING))\r
594                 return true;\r
595 #endif\r
596         return false;\r
597 }\r
598 \r
599 bool GitStatus::IsInExternal(const CTGitPath& path) const\r
600 {\r
601 #if 0\r
602         if (apr_hash_count(m_statushash) == 0)\r
603                 return false;\r
604 \r
605         GitPool localpool(m_pool);\r
606         apr_hash_index_t *hi;\r
607         const char* key;\r
608         for (hi = apr_hash_first(localpool, m_externalhash); hi; hi = apr_hash_next(hi)) \r
609         {\r
610                 apr_hash_this(hi, (const void**)&key, NULL, NULL);\r
611                 if (key)\r
612                 {\r
613                         if (CTGitPath(CUnicodeUtils::GetUnicode(key)).IsAncestorOf(path))\r
614                                 return true;\r
615                 }\r
616         }\r
617 #endif\r
618         return false;\r
619 }\r
620 \r
621 \r
622 void GitStatus::GetStatusString(git_wc_status_kind status, size_t buflen, TCHAR * string)\r
623 {\r
624         TCHAR * buf;\r
625         switch (status)\r
626         {\r
627                 case git_wc_status_none:\r
628                         buf = _T("none\0");\r
629                         break;\r
630                 case git_wc_status_unversioned:\r
631                         buf = _T("unversioned\0");\r
632                         break;\r
633                 case git_wc_status_normal:\r
634                         buf = _T("normal\0");\r
635                         break;\r
636                 case git_wc_status_added:\r
637                         buf = _T("added\0");\r
638                         break;\r
639                 case git_wc_status_missing:\r
640                         buf = _T("missing\0");\r
641                         break;\r
642                 case git_wc_status_deleted:\r
643                         buf = _T("deleted\0");\r
644                         break;\r
645                 case git_wc_status_replaced:\r
646                         buf = _T("replaced\0");\r
647                         break;\r
648                 case git_wc_status_modified:\r
649                         buf = _T("modified\0");\r
650                         break;\r
651                 case git_wc_status_merged:\r
652                         buf = _T("merged\0");\r
653                         break;\r
654                 case git_wc_status_conflicted:\r
655                         buf = _T("conflicted\0");\r
656                         break;\r
657                 case git_wc_status_obstructed:\r
658                         buf = _T("obstructed\0");\r
659                         break;\r
660                 case git_wc_status_ignored:\r
661                         buf = _T("ignored");\r
662                         break;\r
663                 case git_wc_status_external:\r
664                         buf = _T("external");\r
665                         break;\r
666                 case git_wc_status_incomplete:\r
667                         buf = _T("incomplete\0");\r
668                         break;\r
669                 default:\r
670                         buf = _T("\0");\r
671                         break;\r
672         }\r
673         _stprintf_s(string, buflen, _T("%s"), buf);\r
674 }\r
675 \r
676 void GitStatus::GetStatusString(HINSTANCE hInst, git_wc_status_kind status, TCHAR * string, int size, WORD lang)\r
677 {\r
678         switch (status)\r
679         {\r
680                 case git_wc_status_none:\r
681                         LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);\r
682                         break;\r
683                 case git_wc_status_unversioned:\r
684                         LoadStringEx(hInst, IDS_STATUSUNVERSIONED, string, size, lang);\r
685                         break;\r
686                 case git_wc_status_normal:\r
687                         LoadStringEx(hInst, IDS_STATUSNORMAL, string, size, lang);\r
688                         break;\r
689                 case git_wc_status_added:\r
690                         LoadStringEx(hInst, IDS_STATUSADDED, string, size, lang);\r
691                         break;\r
692                 case git_wc_status_missing:\r
693                         LoadStringEx(hInst, IDS_STATUSABSENT, string, size, lang);\r
694                         break;\r
695                 case git_wc_status_deleted:\r
696                         LoadStringEx(hInst, IDS_STATUSDELETED, string, size, lang);\r
697                         break;\r
698                 case git_wc_status_replaced:\r
699                         LoadStringEx(hInst, IDS_STATUSREPLACED, string, size, lang);\r
700                         break;\r
701                 case git_wc_status_modified:\r
702                         LoadStringEx(hInst, IDS_STATUSMODIFIED, string, size, lang);\r
703                         break;\r
704                 case git_wc_status_merged:\r
705                         LoadStringEx(hInst, IDS_STATUSMERGED, string, size, lang);\r
706                         break;\r
707                 case git_wc_status_conflicted:\r
708                         LoadStringEx(hInst, IDS_STATUSCONFLICTED, string, size, lang);\r
709                         break;\r
710                 case git_wc_status_ignored:\r
711                         LoadStringEx(hInst, IDS_STATUSIGNORED, string, size, lang);\r
712                         break;\r
713                 case git_wc_status_obstructed:\r
714                         LoadStringEx(hInst, IDS_STATUSOBSTRUCTED, string, size, lang);\r
715                         break;\r
716                 case git_wc_status_external:\r
717                         LoadStringEx(hInst, IDS_STATUSEXTERNAL, string, size, lang);\r
718                         break;\r
719                 case git_wc_status_incomplete:\r
720                         LoadStringEx(hInst, IDS_STATUSINCOMPLETE, string, size, lang);\r
721                         break;\r
722                 default:\r
723                         LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);\r
724                         break;\r
725         }\r
726 }\r
727 \r
728 #ifdef _MFC_VER\r
729 CString GitStatus::GetDepthString(git_depth_t depth)\r
730 {\r
731 #if 0\r
732         CString sDepth;\r
733         switch (depth)\r
734         {\r
735         case git_depth_unknown:\r
736                 sDepth.LoadString(IDS_Git_DEPTH_UNKNOWN);\r
737                 break;\r
738         case git_depth_empty:\r
739                 sDepth.LoadString(IDS_Git_DEPTH_EMPTY);\r
740                 break;\r
741         case git_depth_files:\r
742                 sDepth.LoadString(IDS_Git_DEPTH_FILES);\r
743                 break;\r
744         case git_depth_immediates:\r
745                 sDepth.LoadString(IDS_Git_DEPTH_IMMEDIATE);\r
746                 break;\r
747         case git_depth_infinity:\r
748                 sDepth.LoadString(IDS_Git_DEPTH_INFINITE);\r
749                 break;\r
750         }\r
751         return sDepth;\r
752 #endif\r
753         return CString("");\r
754 }\r
755 #endif\r
756 \r
757 void GitStatus::GetDepthString(HINSTANCE hInst, git_depth_t depth, TCHAR * string, int size, WORD lang)\r
758 {\r
759 #if 0\r
760         switch (depth)\r
761         {\r
762         case git_depth_unknown:\r
763                 LoadStringEx(hInst, IDS_SVN_DEPTH_UNKNOWN, string, size, lang);\r
764                 break;\r
765         case git_depth_empty:\r
766                 LoadStringEx(hInst, IDS_SVN_DEPTH_EMPTY, string, size, lang);\r
767                 break;\r
768         case git_depth_files:\r
769                 LoadStringEx(hInst, IDS_SVN_DEPTH_FILES, string, size, lang);\r
770                 break;\r
771         case git_depth_immediates:\r
772                 LoadStringEx(hInst, IDS_SVN_DEPTH_IMMEDIATE, string, size, lang);\r
773                 break;\r
774         case git_depth_infinity:\r
775                 LoadStringEx(hInst, IDS_SVN_DEPTH_INFINITE, string, size, lang);\r
776                 break;\r
777         }\r
778 #endif\r
779 }\r
780 \r
781 \r
782 int GitStatus::LoadStringEx(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax, WORD wLanguage)\r
783 {\r
784         const STRINGRESOURCEIMAGE* pImage;\r
785         const STRINGRESOURCEIMAGE* pImageEnd;\r
786         ULONG nResourceSize;\r
787         HGLOBAL hGlobal;\r
788         UINT iIndex;\r
789         int ret;\r
790 \r
791         HRSRC hResource =  FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE(((uID>>4)+1)), wLanguage);\r
792         if (!hResource)\r
793         {\r
794                 // try the default language before giving up!\r
795                 hResource = FindResource(hInstance, MAKEINTRESOURCE(((uID>>4)+1)), RT_STRING);\r
796                 if (!hResource)\r
797                         return 0;\r
798         }\r
799         hGlobal = LoadResource(hInstance, hResource);\r
800         if (!hGlobal)\r
801                 return 0;\r
802         pImage = (const STRINGRESOURCEIMAGE*)::LockResource(hGlobal);\r
803         if(!pImage)\r
804                 return 0;\r
805 \r
806         nResourceSize = ::SizeofResource(hInstance, hResource);\r
807         pImageEnd = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+nResourceSize);\r
808         iIndex = uID&0x000f;\r
809 \r
810         while ((iIndex > 0) && (pImage < pImageEnd))\r
811         {\r
812                 pImage = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+(sizeof(STRINGRESOURCEIMAGE)+(pImage->nLength*sizeof(WCHAR))));\r
813                 iIndex--;\r
814         }\r
815         if (pImage >= pImageEnd)\r
816                 return 0;\r
817         if (pImage->nLength == 0)\r
818                 return 0;\r
819 \r
820         ret = pImage->nLength;\r
821         if (pImage->nLength > nBufferMax)\r
822         {\r
823                 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength-1);\r
824                 lpBuffer[nBufferMax-1] = 0;\r
825         }\r
826         else\r
827         {\r
828                 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength);\r
829                 lpBuffer[ret] = 0;\r
830         }\r
831         return ret;\r
832 }\r
833 \r
834 BOOL GitStatus::getallstatus(const struct wgFile_s *pFile, void *pUserData)\r
835 {\r
836         git_wc_status_kind * s = (git_wc_status_kind *)pUserData;\r
837         *s = GitStatus::GetMoreImportant(*s, GitStatusFromWingit(pFile->nStatus));\r
838         return FALSE;\r
839 }\r
840 \r
841 BOOL GitStatus::getstatus(const struct wgFile_s *pFile, void *pUserData)\r
842 {\r
843         git_wc_status2_t * s = (git_wc_status2_t*)pUserData;\r
844         s->prop_status = s->text_status = GitStatus::GetMoreImportant(s->prop_status, GitStatusFromWingit(pFile->nStatus));\r
845         return FALSE;\r
846 }\r
847 \r
848 #if 0\r
849 git_error_t * GitStatus::getallstatus(void * baton, const char * /*path*/, git_wc_status2_t * status, apr_pool_t * /*pool*/)\r
850 {\r
851         git_wc_status_kind * s = (git_wc_status_kind *)baton;\r
852         *s = GitStatus::GetMoreImportant(*s, status->text_status);\r
853         *s = GitStatus::GetMoreImportant(*s, status->prop_status);\r
854         return Git_NO_ERROR;\r
855 }\r
856 #endif\r
857 \r
858 #if 0\r
859 git_error_t * GitStatus::getstatushash(void * baton, const char * path, git_wc_status2_t * status, apr_pool_t * /*pool*/)\r
860 {\r
861         hashbaton_t * hash = (hashbaton_t *)baton;\r
862         const StdStrAVector& filterList = hash->pThis->m_filterFileList;\r
863         if (status->text_status == git_wc_status_external)\r
864         {\r
865                 apr_hash_set (hash->exthash, apr_pstrdup(hash->pThis->m_pool, path), APR_HASH_KEY_STRING, (const void*)1);\r
866                 return Git_NO_ERROR;\r
867         }\r
868         if(filterList.size() > 0)\r
869         {\r
870                 // We have a filter active - we're only interested in files which are in \r
871                 // the filter  \r
872                 if(!binary_search(filterList.begin(), filterList.end(), path))\r
873                 {\r
874                         // This item is not in the filter - don't store it\r
875                         return Git_NO_ERROR;\r
876                 }\r
877         }\r
878         git_wc_status2_t * statuscopy = git_wc_dup_status2 (status, hash->pThis->m_pool);\r
879         apr_hash_set (hash->hash, apr_pstrdup(hash->pThis->m_pool, path), APR_HASH_KEY_STRING, statuscopy);\r
880         return Git_NO_ERROR;\r
881 }\r
882 \r
883 apr_array_header_t * GitStatus::sort_hash (apr_hash_t *ht,\r
884                                                                                 int (*comparison_func) (const GitStatus::sort_item *, const GitStatus::sort_item *),\r
885                                                                                 apr_pool_t *pool)\r
886 {\r
887         apr_hash_index_t *hi;\r
888         apr_array_header_t *ary;\r
889 \r
890         /* allocate an array with only one element to begin with. */\r
891         ary = apr_array_make (pool, 1, sizeof(sort_item));\r
892 \r
893         /* loop over hash table and push all keys into the array */\r
894         for (hi = apr_hash_first (pool, ht); hi; hi = apr_hash_next (hi))\r
895         {\r
896                 sort_item *item = (sort_item*)apr_array_push (ary);\r
897 \r
898                 apr_hash_this (hi, &item->key, &item->klen, &item->value);\r
899         }\r
900 \r
901         /* now quick sort the array.  */\r
902         qsort (ary->elts, ary->nelts, ary->elt_size,\r
903                 (int (*)(const void *, const void *))comparison_func);\r
904 \r
905         return ary;\r
906 }\r
907 \r
908 int GitStatus::sort_compare_items_as_paths (const sort_item *a, const sort_item *b)\r
909 {\r
910         const char *astr, *bstr;\r
911 \r
912         astr = (const char*)a->key;\r
913         bstr = (const char*)b->key;\r
914         return git_path_compare_paths (astr, bstr);\r
915 }\r
916 #endif\r
917 \r
918 git_error_t* GitStatus::cancel(void *baton)\r
919 {\r
920 #if 0\r
921         volatile bool * canceled = (bool *)baton;\r
922         if (*canceled)\r
923         {\r
924                 CString temp;\r
925                 temp.LoadString(IDS_Git_USERCANCELLED);\r
926                 return git_error_create(Git_ERR_CANCELLED, NULL, CUnicodeUtils::GetUTF8(temp));\r
927         }\r
928         return Git_NO_ERROR;\r
929 #endif \r
930         return 0;\r
931 }\r
932 \r
933 #ifdef _MFC_VER\r
934 \r
935 // Set-up a filter to restrict the files which will have their status stored by a get-status\r
936 void GitStatus::SetFilter(const CTGitPathList& fileList)\r
937 {\r
938         m_filterFileList.clear();\r
939         for(int fileIndex = 0; fileIndex < fileList.GetCount(); fileIndex++)\r
940         {\r
941 //              m_filterFileList.push_back(fileList[fileIndex].GetGitApiPath(m_pool));\r
942         }\r
943         // Sort the list so that we can do binary searches\r
944         std::sort(m_filterFileList.begin(), m_filterFileList.end());\r
945 }\r
946 \r
947 void GitStatus::ClearFilter()\r
948 {\r
949         m_filterFileList.clear();\r
950 }\r
951 \r
952 #endif // _MFC_VER\r
953 \r