1 // TortoiseGit - a Windows shell extension for easy version control
3 // External Cache Copyright (C) 2005 - 2006,2010 - Will Dean, Stefan Kueng
4 // Copyright (C) 2008-2012 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "TGITCache.h"
24 #include "GitStatusCache.h"
25 #include "CacheInterface.h"
28 #include "CrashReport.h"
29 #include "GitAdminDir.h"
33 #include "..\version.h"
34 //#include "svn_dso.h"
35 #include "SmartHandle.h"
40 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
43 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
47 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
49 CCrashReportTGit
crasher(L
"TGitCache " _T(APP_X64_STRING
));
51 DWORD WINAPI
InstanceThread(LPVOID
);
52 DWORD WINAPI
PipeThread(LPVOID
);
53 DWORD WINAPI
CommandWaitThread(LPVOID
);
54 DWORD WINAPI
CommandThread(LPVOID
);
55 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
57 NOTIFYICONDATA niData
;
60 TCHAR szCurrentCrawledPath
[MAX_CRAWLEDPATHS
][MAX_CRAWLEDPATHSLEN
];
61 int nCurrentCrawledpathIndex
= 0;
62 CComAutoCriticalSection critSec
;
64 volatile LONG nThreadCount
= 0;
66 #define PACKVERSION(major,minor) MAKELONG(minor,major)
68 void DebugOutputLastError()
72 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
73 FORMAT_MESSAGE_FROM_SYSTEM
|
74 FORMAT_MESSAGE_IGNORE_INSERTS
,
77 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
85 // Display the string.
86 OutputDebugStringA("TGitCache GetLastError(): ");
87 OutputDebugString((LPCTSTR
)lpMsgBuf
);
88 OutputDebugStringA("\n");
91 LocalFree( lpMsgBuf
);
94 int __stdcall
WinMain(HINSTANCE hInstance
, HINSTANCE
/*hPrevInstance*/, LPSTR
/*lpCmdLine*/, int /*cmdShow*/)
97 CAutoGeneralHandle hReloadProtection
= ::CreateMutex(NULL
, FALSE
, GetCacheMutexName());
99 if ((!hReloadProtection
) || (GetLastError() == ERROR_ALREADY_EXISTS
))
101 // An instance of TGitCache is already running
102 ATLTRACE("TGitCache ignoring restart\n");
107 // svn_dso_initialize2();
108 g_GitAdminDir
.Init();
109 CGitStatusCache::Create();
110 CGitStatusCache::Instance().Init();
112 SecureZeroMemory(szCurrentCrawledPath
, sizeof(szCurrentCrawledPath
));
116 TCHAR szWindowClass
[] = {TGIT_CACHE_WINDOW_NAME
};
118 // create a hidden window to receive window messages.
120 wcex
.cbSize
= sizeof(WNDCLASSEX
);
121 wcex
.style
= CS_HREDRAW
| CS_VREDRAW
;
122 wcex
.lpfnWndProc
= (WNDPROC
)WndProc
;
125 wcex
.hInstance
= hInstance
;
128 wcex
.hbrBackground
= 0;
129 wcex
.lpszMenuName
= NULL
;
130 wcex
.lpszClassName
= szWindowClass
;
132 RegisterClassEx(&wcex
);
133 hWnd
= CreateWindow(TGIT_CACHE_WINDOW_NAME
, TGIT_CACHE_WINDOW_NAME
, WS_CAPTION
, 0, 0, 800, 300, NULL
, 0, hInstance
, 0);
139 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE
)==TRUE
)
141 SecureZeroMemory(&niData
,sizeof(NOTIFYICONDATA
));
145 AtlGetShellVersion(&dwMajor
, &dwMinor
);
146 DWORD dwVersion
= PACKVERSION(dwMajor
, dwMinor
);
147 if (dwVersion
>= PACKVERSION(6,0))
148 niData
.cbSize
= sizeof(NOTIFYICONDATA
);
149 else if (dwVersion
>= PACKVERSION(5,0))
150 niData
.cbSize
= NOTIFYICONDATA_V2_SIZE
;
152 niData
.cbSize
= NOTIFYICONDATA_V1_SIZE
;
154 niData
.uID
= TRAY_ID
; // own tray icon ID
156 niData
.uFlags
= NIF_ICON
|NIF_MESSAGE
;
160 (HICON
)LoadImage(hInstance
,
161 MAKEINTRESOURCE(IDI_TGITCACHE
),
163 GetSystemMetrics(SM_CXSMICON
),
164 GetSystemMetrics(SM_CYSMICON
),
167 // set the message to send
168 // note: the message value should be in the
169 // range of WM_APP through 0xBFFF
170 niData
.uCallbackMessage
= TRAY_CALLBACK
;
171 Shell_NotifyIcon(NIM_ADD
,&niData
);
173 if(niData
.hIcon
&& DestroyIcon(niData
.hIcon
))
177 // Create a thread which waits for incoming pipe connections
178 CAutoGeneralHandle hPipeThread
= CreateThread(
179 NULL
, // no security attribute
180 0, // default stack size
182 (LPVOID
) &bRun
, // thread parameter
184 &dwThreadId
); // returns thread ID
188 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");
189 //DebugOutputLastError();
192 else hPipeThread
.CloseHandle();
194 // Create a thread which waits for incoming pipe connections
195 CAutoGeneralHandle hCommandWaitThread
= CreateThread(
196 NULL
, // no security attribute
197 0, // default stack size
199 (LPVOID
) &bRun
, // thread parameter
201 &dwThreadId
); // returns thread ID
203 if (!hCommandWaitThread
)
205 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");
206 //DebugOutputLastError();
211 // loop to handle window messages.
215 bLoopRet
= GetMessage(&msg
, NULL
, 0, 0);
216 if ((bLoopRet
!= -1)&&(bLoopRet
!= 0))
218 DispatchMessage(&msg
);
224 Shell_NotifyIcon(NIM_DELETE
,&niData
);
225 CGitStatusCache::Destroy();
226 g_GitAdminDir
.Close();
232 LRESULT CALLBACK
WndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
240 case WM_LBUTTONDBLCLK
:
241 if (IsWindowVisible(hWnd
))
242 ShowWindow(hWnd
, SW_HIDE
);
244 ShowWindow(hWnd
, SW_RESTORE
);
249 NOTIFYICONDATA SystemTray
;
250 sInfoTip
.Format(_T("Cached Directories : %ld\nWatched paths : %ld"),
251 CGitStatusCache::Instance().GetCacheSize(),
252 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
254 SystemTray
.cbSize
= sizeof(NOTIFYICONDATA
);
255 SystemTray
.hWnd
= hTrayWnd
;
256 SystemTray
.uID
= TRAY_ID
;
257 SystemTray
.uFlags
= NIF_TIP
;
258 _tcscpy_s(SystemTray
.szTip
, sInfoTip
);
259 Shell_NotifyIcon(NIM_MODIFY
, &SystemTray
);
266 DWORD ptW
= GetMessagePos();
267 pt
.x
= GET_X_LPARAM(ptW
);
268 pt
.y
= GET_Y_LPARAM(ptW
);
269 HMENU hMenu
= CreatePopupMenu();
272 InsertMenu(hMenu
, (UINT
)-1, MF_BYPOSITION
, TRAYPOP_EXIT
, _T("Exit"));
273 SetForegroundWindow(hWnd
);
274 TrackPopupMenu(hMenu
, TPM_BOTTOMALIGN
, pt
.x
, pt
.y
, 0, hWnd
, NULL
);
285 HDC hdc
= BeginPaint(hWnd
, &ps
);
287 GetClientRect(hWnd
, &rect
);
288 // clear the background
289 HBRUSH background
= CreateSolidBrush(::GetSysColor(COLOR_WINDOW
));
290 HGDIOBJ oldbrush
= SelectObject(hdc
, background
);
291 FillRect(hdc
, &rect
, background
);
295 AutoLocker
print(critSec
);
296 GetTextExtentPoint32( hdc
, szCurrentCrawledPath
[0], (int)_tcslen(szCurrentCrawledPath
[0]), &fontsize
);
297 for (int i
=nCurrentCrawledpathIndex
; i
<MAX_CRAWLEDPATHS
; ++i
)
299 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
302 for (int i
=0; i
<nCurrentCrawledpathIndex
; ++i
)
304 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
309 SelectObject(hdc
,oldbrush
);
311 DeleteObject(background
);
317 WORD wmId
= LOWORD(wParam
);
327 case WM_QUERYENDSESSION
:
329 ATLTRACE("WM_QUERYENDSESSION\n");
330 if (CGitStatusCache::Instance().WaitToWrite(200))
332 CGitStatusCache::Instance().Stop();
333 CGitStatusCache::Instance().Done();
343 ATLTRACE("WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
344 CGitStatusCache::Instance().WaitToWrite();
345 CGitStatusCache::Instance().Stop();
346 CGitStatusCache::Instance().SaveCache();
347 if (message
!= WM_QUIT
)
353 case WM_DEVICECHANGE
:
355 DEV_BROADCAST_HDR
* phdr
= (DEV_BROADCAST_HDR
*)lParam
;
358 case DBT_CUSTOMEVENT
:
360 ATLTRACE("WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
361 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
363 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
364 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_DISMOUNT
))
366 ATLTRACE("Device to be dismounted\n");
367 CGitStatusCache::Instance().WaitToWrite();
368 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
369 CGitStatusCache::Instance().Done();
371 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_LOCK
))
373 ATLTRACE("Device lock event\n");
374 CGitStatusCache::Instance().WaitToWrite();
375 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
376 CGitStatusCache::Instance().Done();
381 case DBT_DEVICEREMOVEPENDING
:
382 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING\n");
383 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
385 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
386 CGitStatusCache::Instance().WaitToWrite();
387 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
388 CGitStatusCache::Instance().Done();
392 CGitStatusCache::Instance().WaitToWrite();
393 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
394 CGitStatusCache::Instance().Done();
397 case DBT_DEVICEQUERYREMOVE
:
398 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEQUERYREMOVE\n");
399 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
401 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
402 CGitStatusCache::Instance().WaitToWrite();
403 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
404 CGitStatusCache::Instance().Done();
408 CGitStatusCache::Instance().WaitToWrite();
409 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
410 CGitStatusCache::Instance().Done();
413 case DBT_DEVICEREMOVECOMPLETE
:
414 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVECOMPLETE\n");
415 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
417 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
418 CGitStatusCache::Instance().WaitToWrite();
419 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
420 CGitStatusCache::Instance().Done();
424 CGitStatusCache::Instance().WaitToWrite();
425 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
426 CGitStatusCache::Instance().Done();
435 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
438 //////////////////////////////////////////////////////////////////////////
440 VOID
GetAnswerToRequest(const TGITCacheRequest
* pRequest
, TGITCacheResponse
* pReply
, DWORD
* pResponseLength
)
443 *pResponseLength
= 0;
444 if(pRequest
->flags
& TGITCACHE_FLAGS_FOLDERISKNOWN
)
446 path
.SetFromWin(pRequest
->path
, !!(pRequest
->flags
& TGITCACHE_FLAGS_ISFOLDER
));
450 path
.SetFromWin(pRequest
->path
);
453 if (CGitStatusCache::Instance().WaitToRead(2000))
455 CGitStatusCache::Instance().GetStatusForPath(path
, pRequest
->flags
, false).BuildCacheResponse(*pReply
, *pResponseLength
);
456 CGitStatusCache::Instance().Done();
460 CStatusCacheEntry entry
;
461 entry
.BuildCacheResponse(*pReply
, *pResponseLength
);
465 DWORD WINAPI
PipeThread(LPVOID lpvParam
)
467 ATLTRACE("PipeThread started\n");
468 bool * bRun
= (bool *)lpvParam
;
469 // The main loop creates an instance of the named pipe and
470 // then waits for a client to connect to it. When the client
471 // connects, a thread is created to handle communications
472 // with that client, and the loop is repeated.
479 hPipe
= CreateNamedPipe(
481 PIPE_ACCESS_DUPLEX
, // read/write access
482 PIPE_TYPE_MESSAGE
| // message type pipe
483 PIPE_READMODE_MESSAGE
| // message-read mode
484 PIPE_WAIT
, // blocking mode
485 PIPE_UNLIMITED_INSTANCES
, // max. instances
486 BUFSIZE
, // output buffer size
487 BUFSIZE
, // input buffer size
488 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
493 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
494 //DebugOutputLastError();
497 continue; // never leave the thread!
500 // Wait for the client to connect; if it succeeds,
501 // the function returns a nonzero value. If the function returns
502 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
503 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
506 // Create a thread for this client.
507 CAutoGeneralHandle hInstanceThread
= CreateThread(
508 NULL
, // no security attribute
509 0, // default stack size
511 (HANDLE
) hPipe
, // thread parameter
513 &dwThreadId
); // returns thread ID
515 if (!hInstanceThread
)
517 //OutputDebugStringA("TSVNCache: Could not create Instance thread\n");
518 //DebugOutputLastError();
519 DisconnectNamedPipe(hPipe
);
520 // since we're now closing this thread, we also have to close the whole application!
521 // otherwise the thread is dead, but the app is still running, refusing new instances
522 // but no pipe will be available anymore.
523 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
526 // detach the handle, since we passed it to the thread
531 // The client could not connect, so close the pipe.
532 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
533 //DebugOutputLastError();
537 continue; // don't end the thread!
540 ATLTRACE("Pipe thread exited\n");
544 DWORD WINAPI
CommandWaitThread(LPVOID lpvParam
)
546 ATLTRACE("CommandWaitThread started\n");
547 bool * bRun
= (bool *)lpvParam
;
548 // The main loop creates an instance of the named pipe and
549 // then waits for a client to connect to it. When the client
550 // connects, a thread is created to handle communications
551 // with that client, and the loop is repeated.
558 hPipe
= CreateNamedPipe(
559 GetCacheCommandPipeName(),
560 PIPE_ACCESS_DUPLEX
, // read/write access
561 PIPE_TYPE_MESSAGE
| // message type pipe
562 PIPE_READMODE_MESSAGE
| // message-read mode
563 PIPE_WAIT
, // blocking mode
564 PIPE_UNLIMITED_INSTANCES
, // max. instances
565 BUFSIZE
, // output buffer size
566 BUFSIZE
, // input buffer size
567 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
572 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
573 //DebugOutputLastError();
576 continue; // never leave the thread!
579 // Wait for the client to connect; if it succeeds,
580 // the function returns a nonzero value. If the function returns
581 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
582 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
585 // Create a thread for this client.
586 CAutoGeneralHandle hCommandThread
= CreateThread(
587 NULL
, // no security attribute
588 0, // default stack size
590 (HANDLE
) hPipe
, // thread parameter
592 &dwThreadId
); // returns thread ID
596 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");
597 //DebugOutputLastError();
598 DisconnectNamedPipe(hPipe
);
600 // since we're now closing this thread, we also have to close the whole application!
601 // otherwise the thread is dead, but the app is still running, refusing new instances
602 // but no pipe will be available anymore.
603 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
606 // detach the handle, since we passed it to the thread
611 // The client could not connect, so close the pipe.
612 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
613 //DebugOutputLastError();
617 continue; // don't end the thread!
620 ATLTRACE("CommandWait thread exited\n");
624 DWORD WINAPI
InstanceThread(LPVOID lpvParam
)
626 ATLTRACE("InstanceThread started\n");
627 TGITCacheResponse response
;
628 DWORD cbBytesRead
, cbWritten
;
632 // The thread's parameter is a handle to a pipe instance.
635 InterlockedIncrement(&nThreadCount
);
638 // Read client requests from the pipe.
639 TGITCacheRequest request
;
641 hPipe
, // handle to pipe
642 &request
, // buffer to receive data
643 sizeof(request
), // size of buffer
644 &cbBytesRead
, // number of bytes read
645 NULL
); // not overlapped I/O
647 if (! fSuccess
|| cbBytesRead
== 0)
649 DisconnectNamedPipe(hPipe
);
650 ATLTRACE("Instance thread exited\n");
651 InterlockedDecrement(&nThreadCount
);
652 if (nThreadCount
== 0)
653 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
657 DWORD responseLength
;
658 GetAnswerToRequest(&request
, &response
, &responseLength
);
660 // Write the reply to the pipe.
661 fSuccess
= WriteFile(
662 hPipe
, // handle to pipe
663 &response
, // buffer to write from
664 responseLength
, // number of bytes to write
665 &cbWritten
, // number of bytes written
666 NULL
); // not overlapped I/O
668 if (! fSuccess
|| responseLength
!= cbWritten
)
670 DisconnectNamedPipe(hPipe
);
671 ATLTRACE("Instance thread exited\n");
672 InterlockedDecrement(&nThreadCount
);
673 if (nThreadCount
== 0)
674 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
679 // Flush the pipe to allow the client to read the pipe's contents
680 // before disconnecting. Then disconnect the pipe, and close the
681 // handle to this pipe instance.
683 FlushFileBuffers(hPipe
);
684 DisconnectNamedPipe(hPipe
);
685 ATLTRACE("Instance thread exited\n");
686 InterlockedDecrement(&nThreadCount
);
687 if (nThreadCount
== 0)
688 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
692 DWORD WINAPI
CommandThread(LPVOID lpvParam
)
694 ATLTRACE("CommandThread started\n");
699 // The thread's parameter is a handle to a pipe instance.
705 // Read client requests from the pipe.
706 TGITCacheCommand command
;
708 hPipe
, // handle to pipe
709 &command
, // buffer to receive data
710 sizeof(command
), // size of buffer
711 &cbBytesRead
, // number of bytes read
712 NULL
); // not overlapped I/O
714 if (! fSuccess
|| cbBytesRead
== 0)
716 DisconnectNamedPipe(hPipe
);
717 ATLTRACE("Command thread exited\n");
721 switch (command
.command
)
723 case TGITCACHECOMMAND_END
:
724 FlushFileBuffers(hPipe
);
725 DisconnectNamedPipe(hPipe
);
726 ATLTRACE("Command thread exited\n");
728 case TGITCACHECOMMAND_CRAWL
:
730 CTGitPath changedpath
;
731 changedpath
.SetFromWin(CString(command
.path
), true);
732 // remove the path from our cache - that will 'invalidate' it.
733 CGitStatusCache::Instance().WaitToWrite();
734 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
735 CGitStatusCache::Instance().Done();
736 CGitStatusCache::Instance().AddFolderForCrawling(changedpath
.GetDirectory());
739 case TGITCACHECOMMAND_REFRESHALL
:
740 CGitStatusCache::Instance().WaitToWrite();
741 CGitStatusCache::Instance().Refresh();
742 CGitStatusCache::Instance().Done();
744 case TGITCACHECOMMAND_RELEASE
:
746 CTGitPath changedpath
;
747 changedpath
.SetFromWin(CString(command
.path
), true);
748 ATLTRACE(_T("release handle for path %s\n"), changedpath
.GetWinPath());
749 CGitStatusCache::Instance().WaitToWrite();
750 CGitStatusCache::Instance().CloseWatcherHandles(changedpath
);
751 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
752 CGitStatusCache::Instance().Done();
755 case TGITCACHECOMMAND_BLOCK
:
757 CTGitPath changedpath
;
758 changedpath
.SetFromWin(CString(command
.path
));
759 ATLTRACE(_T("block path %s\n"), changedpath
.GetWinPath());
760 CGitStatusCache::Instance().BlockPath(changedpath
);
763 case TGITCACHECOMMAND_UNBLOCK
:
765 CTGitPath changedpath
;
766 changedpath
.SetFromWin(CString(command
.path
));
767 ATLTRACE(_T("block path %s\n"), changedpath
.GetWinPath());
768 CGitStatusCache::Instance().UnBlockPath(changedpath
);
774 // Flush the pipe to allow the client to read the pipe's contents
775 // before disconnecting. Then disconnect the pipe, and close the
776 // handle to this pipe instance.
778 FlushFileBuffers(hPipe
);
779 DisconnectNamedPipe(hPipe
);
780 ATLTRACE("Command thread exited\n");