OSDN Git Service

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