OSDN Git Service

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