1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2014 - TortoiseGit
4 // Copyright (C) 2003-2013 - TortoiseSVN
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.
20 // RepositoryBrowser.cpp : implementation file
24 #include "TortoiseProc.h"
25 #include "RepositoryBrowser.h"
29 #include "UnicodeUtils.h"
30 #include "SysImageList.h"
34 #include "PathUtils.h"
35 #include "StringUtils.h"
38 #define OVERLAY_EXTERNAL 1
39 #define OVERLAY_EXECUTABLE 2
40 #define OVERLAY_SYMLINK 3
42 void SetSortArrowA(CListCtrl
* control
, int nColumn
, bool bAscending
)
48 CHeaderCtrl
* pHeader
= control
->GetHeaderCtrl();
49 HDITEM HeaderItem
= {0};
50 HeaderItem
.mask
= HDI_FORMAT
;
51 for (int i
= 0; i
< pHeader
->GetItemCount(); ++i
)
53 pHeader
->GetItem(i
, &HeaderItem
);
54 HeaderItem
.fmt
&= ~(HDF_SORTDOWN
| HDF_SORTUP
);
55 pHeader
->SetItem(i
, &HeaderItem
);
59 pHeader
->GetItem(nColumn
, &HeaderItem
);
60 HeaderItem
.fmt
|= (bAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
61 pHeader
->SetItem(nColumn
, &HeaderItem
);
65 class CRepoListCompareFunc
68 CRepoListCompareFunc(CListCtrl
* pList
, int col
, bool desc
)
74 static int CALLBACK
StaticCompare(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
)
76 return ((CRepoListCompareFunc
*) lParamSort
)->Compare(lParam1
, lParam2
);
79 int Compare(LPARAM lParam1
, LPARAM lParam2
)
81 CShadowFilesTree
* pLeft
= (CShadowFilesTree
*)m_pList
->GetItemData((int)lParam1
);
82 CShadowFilesTree
* pRight
= (CShadowFilesTree
*)m_pList
->GetItemData((int)lParam2
);
87 case CRepositoryBrowser::eCol_Name
:
88 result
= SortStrCmp(pLeft
->m_sName
, pRight
->m_sName
);
91 case CRepositoryBrowser::eCol_Extension
:
92 result
= m_pList
->GetItemText(static_cast<int>(lParam1
), 1).CompareNoCase(m_pList
->GetItemText(static_cast<int>(lParam2
), 1));
93 if (result
== 0) // if extensions are the same, use the filename to sort
94 result
= SortStrCmp(pRight
->m_sName
, pRight
->m_sName
);
97 case CRepositoryBrowser::eCol_FileSize
:
98 if (pLeft
->m_iSize
> pRight
->m_iSize
)
100 else if (pLeft
->m_iSize
< pRight
->m_iSize
)
103 result
= SortStrCmp(pLeft
->m_sName
, pRight
->m_sName
);
109 if (pLeft
->m_bFolder
!= pRight
->m_bFolder
)
111 if (pRight
->m_bFolder
)
119 int SortStrCmp(const CString
&left
, const CString
&right
)
121 if (CRepositoryBrowser::s_bSortLogical
)
122 return StrCmpLogicalW(left
, right
);
123 return StrCmpI(left
, right
);
131 // CRepositoryBrowser dialog
133 bool CRepositoryBrowser::s_bSortLogical
= true;
135 IMPLEMENT_DYNAMIC(CRepositoryBrowser
, CResizableStandAloneDialog
)
137 CRepositoryBrowser::CRepositoryBrowser(CString rev
, CWnd
* pParent
/*=NULL*/)
138 : CResizableStandAloneDialog(CRepositoryBrowser::IDD
, pParent
)
140 , m_currSortDesc(false)
143 , m_ColumnManager(&m_RepoList
)
145 , m_nOpenIconFolder(0)
147 , m_nExecutableOvl(0)
155 CRepositoryBrowser::~CRepositoryBrowser()
159 void CRepositoryBrowser::DoDataExchange(CDataExchange
* pDX
)
161 CDialog::DoDataExchange(pDX
);
162 DDX_Control(pDX
, IDC_REPOTREE
, m_RepoTree
);
163 DDX_Control(pDX
, IDC_REPOLIST
, m_RepoList
);
167 BEGIN_MESSAGE_MAP(CRepositoryBrowser
, CResizableStandAloneDialog
)
168 ON_NOTIFY(TVN_SELCHANGED
, IDC_REPOTREE
, &CRepositoryBrowser::OnTvnSelchangedRepoTree
)
169 ON_NOTIFY(TVN_ITEMEXPANDING
, IDC_REPOTREE
, &CRepositoryBrowser::OnTvnItemExpandingRepoTree
)
171 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_REPOLIST
, &CRepositoryBrowser::OnLvnColumnclickRepoList
)
172 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_REPOLIST
, &CRepositoryBrowser::OnLvnItemchangedRepolist
)
173 ON_NOTIFY(NM_DBLCLK
, IDC_REPOLIST
, &CRepositoryBrowser::OnNMDblclk_RepoList
)
174 ON_BN_CLICKED(IDC_BUTTON_REVISION
, &CRepositoryBrowser::OnBnClickedButtonRevision
)
183 // CRepositoryBrowser message handlers
185 BOOL
CRepositoryBrowser::OnInitDialog()
187 CResizableStandAloneDialog::OnInitDialog();
188 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
190 AddAnchor(IDC_STATIC_REPOURL
, TOP_LEFT
);
191 AddAnchor(IDC_REPOBROWSER_URL
, TOP_LEFT
, TOP_RIGHT
);
192 AddAnchor(IDC_STATIC_REF
, TOP_RIGHT
);
193 AddAnchor(IDC_BUTTON_REVISION
, TOP_RIGHT
);
194 AddAnchor(IDC_REPOTREE
, TOP_LEFT
, BOTTOM_LEFT
);
195 AddAnchor(IDC_REPOLIST
, TOP_LEFT
, BOTTOM_RIGHT
);
196 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
197 AddAnchor(IDC_INFOLABEL
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
198 AddAnchor(IDOK
, BOTTOM_RIGHT
);
199 AddAnchor(IDCANCEL
, BOTTOM_RIGHT
);
201 CRepositoryBrowser::s_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER
);
202 if (CRepositoryBrowser::s_bSortLogical
)
203 CRepositoryBrowser::s_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE
);
205 static UINT columnNames
[] = { IDS_STATUSLIST_COLFILENAME
, IDS_STATUSLIST_COLEXT
, IDS_LOG_SIZE
};
206 static int columnWidths
[] = { 150, 100, 100 };
207 DWORD dwDefaultColumns
= (1 << eCol_Name
) | (1 << eCol_Extension
) | (1 << eCol_FileSize
);
208 m_ColumnManager
.SetNames(columnNames
, _countof(columnNames
));
209 m_ColumnManager
.ReadSettings(dwDefaultColumns
, 0, _T("RepoBrowser"), _countof(columnNames
), columnWidths
);
211 // set up the list control
212 // set the extended style of the list control
213 // the style LVS_EX_FULLROWSELECT interferes with the background watermark image but it's more important to be able to select in the whole row.
214 CRegDWORD
regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE
);
215 DWORD exStyle
= LVS_EX_HEADERDRAGDROP
| LVS_EX_DOUBLEBUFFER
| LVS_EX_INFOTIP
| LVS_EX_SUBITEMIMAGES
;
216 if (DWORD(regFullRowSelect
))
217 exStyle
|= LVS_EX_FULLROWSELECT
;
218 m_RepoList
.SetExtendedStyle(exStyle
);
219 m_RepoList
.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL
);
220 CAppUtils::SetListCtrlBackgroundImage(m_RepoList
.GetSafeHwnd(), IDI_REPOBROWSER_BKG
);
222 m_RepoTree
.SetImageList(&SYS_IMAGE_LIST(), TVSIL_NORMAL
);
223 if (SysInfo::Instance().IsVistaOrLater())
225 DWORD exStyle
= TVS_EX_FADEINOUTEXPANDOS
| TVS_EX_AUTOHSCROLL
| TVS_EX_DOUBLEBUFFER
;
226 m_RepoTree
.SetExtendedStyle(exStyle
, exStyle
);
229 m_nExternalOvl
= SYS_IMAGE_LIST().AddIcon((HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EXTERNALOVL
), IMAGE_ICON
, 0, 0, LR_DEFAULTSIZE
));
230 m_nExecutableOvl
= SYS_IMAGE_LIST().AddIcon((HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EXECUTABLEOVL
), IMAGE_ICON
, 0, 0, LR_DEFAULTSIZE
));
231 m_nSymlinkOvl
= SYS_IMAGE_LIST().AddIcon((HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SYMLINKOVL
), IMAGE_ICON
, 0, 0, LR_DEFAULTSIZE
));
232 // set externaloverlay in SYS_IMAGE_LIST() in Refresh method, so that it is updated after every launch of the logdialog
234 SetWindowTheme(m_RepoTree
.GetSafeHwnd(), L
"Explorer", NULL
);
235 SetWindowTheme(m_RepoList
.GetSafeHwnd(), L
"Explorer", NULL
);
237 m_nIconFolder
= SYS_IMAGE_LIST().GetDirIconIndex();
238 m_nOpenIconFolder
= SYS_IMAGE_LIST().GetDirOpenIconIndex();
240 EnableSaveRestore(L
"Reposbrowser");
242 DWORD xPos
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RepobrowserDivider"), 0);
246 GetDlgItem(IDC_REPOTREE
)->GetClientRect(&rc
);
247 xPos
= rc
.right
- rc
.left
;
249 HandleDividerMove(CPoint(xPos
+ 20, 10), false);
251 CString sWindowTitle
;
252 GetWindowText(sWindowTitle
);
253 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
255 m_bHasWC
= !g_GitAdminDir
.IsBareRepo(g_Git
.m_CurrentDir
);
259 m_RepoList
.SetFocus();
264 void CRepositoryBrowser::OnDestroy()
266 int maxcol
= m_ColumnManager
.GetColumnCount();
267 for (int col
= 0; col
< maxcol
; ++col
)
268 if (m_ColumnManager
.IsVisible(col
))
269 m_ColumnManager
.ColumnResized(col
);
270 m_ColumnManager
.WriteSettings();
272 CResizableStandAloneDialog::OnDestroy();
275 void CRepositoryBrowser::OnOK()
277 if (GetFocus() == &m_RepoList
&& (GetKeyState(VK_MENU
) & 0x8000) == 0)
279 // list control has focus: 'enter' the folder
280 if (m_RepoList
.GetSelectedCount() != 1)
283 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
286 CShadowFilesTree
*item
= (CShadowFilesTree
*)m_RepoList
.GetItemData(m_RepoList
.GetNextSelectedItem(pos
));
289 FillListCtrlForShadowTree(item
);
290 m_RepoTree
.SelectItem(item
->m_hTree
);
293 OpenFile(item
->GetFullName(), OPEN
, item
->m_bSubmodule
, item
->m_hash
);
298 SaveDividerPosition();
299 CResizableStandAloneDialog::OnOK();
302 void CRepositoryBrowser::OnCancel()
304 SaveDividerPosition();
305 CResizableStandAloneDialog::OnCancel();
308 void CRepositoryBrowser::OnNMDblclk_RepoList(NMHDR
*pNMHDR
, LRESULT
*pResult
)
312 LPNMITEMACTIVATE pNmItemActivate
= reinterpret_cast<LPNMITEMACTIVATE
>(pNMHDR
);
313 if (pNmItemActivate
->iItem
< 0)
316 CShadowFilesTree
* pItem
= (CShadowFilesTree
*)m_RepoList
.GetItemData(pNmItemActivate
->iItem
);
320 if (!pItem
->m_bFolder
)
322 OpenFile(pItem
->GetFullName(), OPEN
, pItem
->m_bSubmodule
, pItem
->m_hash
);
327 FillListCtrlForShadowTree(pItem
);
328 m_RepoTree
.SelectItem(pItem
->m_hTree
);
332 void CRepositoryBrowser::Refresh()
335 if (m_nExternalOvl
>= 0)
336 SYS_IMAGE_LIST().SetOverlayImage(m_nExternalOvl
, OVERLAY_EXTERNAL
);
337 if (m_nExecutableOvl
>= 0)
338 SYS_IMAGE_LIST().SetOverlayImage(m_nExecutableOvl
, OVERLAY_EXECUTABLE
);
339 if (m_nSymlinkOvl
>= 0)
340 SYS_IMAGE_LIST().SetOverlayImage(m_nSymlinkOvl
, OVERLAY_SYMLINK
);
342 m_RepoTree
.DeleteAllItems();
343 m_RepoList
.DeleteAllItems();
344 m_TreeRoot
.m_ShadowTree
.clear();
345 m_TreeRoot
.m_sName
= "";
346 m_TreeRoot
.m_bFolder
= true;
348 TVINSERTSTRUCT tvinsert
= {0};
349 tvinsert
.hParent
= TVI_ROOT
;
350 tvinsert
.hInsertAfter
= TVI_ROOT
;
351 tvinsert
.itemex
.mask
= TVIF_DI_SETITEM
| TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_STATE
;
352 tvinsert
.itemex
.pszText
= L
"/";
353 tvinsert
.itemex
.lParam
= (LPARAM
)&m_TreeRoot
;
354 tvinsert
.itemex
.iImage
= m_nIconFolder
;
355 tvinsert
.itemex
.iSelectedImage
= m_nOpenIconFolder
;
356 m_TreeRoot
.m_hTree
= m_RepoTree
.InsertItem(&tvinsert
);
358 ReadTree(&m_TreeRoot
);
359 m_RepoTree
.Expand(m_TreeRoot
.m_hTree
, TVE_EXPAND
);
360 FillListCtrlForShadowTree(&m_TreeRoot
);
361 m_RepoTree
.SelectItem(m_TreeRoot
.m_hTree
);
365 int CRepositoryBrowser::ReadTreeRecursive(git_repository
&repo
, const git_tree
* tree
, CShadowFilesTree
* treeroot
)
367 size_t count
= git_tree_entrycount(tree
);
368 bool hasSubfolders
= false;
370 for (size_t i
= 0; i
< count
; ++i
)
372 const git_tree_entry
*entry
= git_tree_entry_byindex(tree
, i
);
376 const int mode
= git_tree_entry_filemode(entry
);
378 CString base
= CUnicodeUtils::GetUnicode(git_tree_entry_name(entry
), CP_UTF8
);
380 const git_oid
*oid
= git_tree_entry_id(entry
);
381 CShadowFilesTree
* pNextTree
= &treeroot
->m_ShadowTree
[base
];
382 pNextTree
->m_sName
= base
;
383 pNextTree
->m_pParent
= treeroot
;
384 pNextTree
->m_hash
= CGitHash((char *)oid
->id
);
386 if (mode
== GIT_FILEMODE_COMMIT
)
387 pNextTree
->m_bSubmodule
= true;
388 else if (mode
& S_IFDIR
)
390 hasSubfolders
= true;
391 pNextTree
->m_bFolder
= true;
392 pNextTree
->m_bLoaded
= false;
394 TVINSERTSTRUCT tvinsert
= {0};
395 tvinsert
.hParent
= treeroot
->m_hTree
;
396 tvinsert
.hInsertAfter
= TVI_SORT
;
397 tvinsert
.itemex
.mask
= TVIF_DI_SETITEM
| TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_STATE
| TVIF_CHILDREN
;
398 tvinsert
.itemex
.pszText
= base
.GetBuffer(base
.GetLength());
399 tvinsert
.itemex
.cChildren
= 1;
400 tvinsert
.itemex
.lParam
= (LPARAM
)pNextTree
;
401 tvinsert
.itemex
.iImage
= m_nIconFolder
;
402 tvinsert
.itemex
.iSelectedImage
= m_nOpenIconFolder
;
403 pNextTree
->m_hTree
= m_RepoTree
.InsertItem(&tvinsert
);
404 base
.ReleaseBuffer();
408 if (mode
== GIT_FILEMODE_BLOB_EXECUTABLE
)
409 pNextTree
->m_bExecutable
= true;
410 if (mode
== GIT_FILEMODE_LINK
)
411 pNextTree
->m_bSymlink
= true;
413 git_blob_lookup(blob
.GetPointer(), &repo
, oid
);
417 pNextTree
->m_iSize
= git_blob_rawsize(blob
);
423 TVITEM tvitem
= { 0 };
424 tvitem
.hItem
= treeroot
->m_hTree
;
425 tvitem
.mask
= TVIF_CHILDREN
;
426 tvitem
.cChildren
= 0;
427 m_RepoTree
.SetItem(&tvitem
);
433 int CRepositoryBrowser::ReadTree(CShadowFilesTree
* treeroot
, const CString
& root
)
436 CAutoRepository
repository(g_Git
.GetGitRepository());
439 MessageBox(CGit::GetLibGit2LastErr(_T("Could not open repository.")), _T("TortoiseGit"), MB_ICONERROR
);
443 if (m_sRevision
== _T("HEAD"))
445 int ret
= git_repository_head_unborn(repository
);
446 if (ret
== 1) // is orphan
450 MessageBox(g_Git
.GetLibGit2LastErr(_T("Could not check HEAD.")), _T("TortoiseGit"), MB_ICONERROR
);
456 if (CGit::GetHash(repository
, hash
, m_sRevision
))
458 MessageBox(CGit::GetLibGit2LastErr(_T("Could not get hash of ") + m_sRevision
+ _T(".")), _T("TortoiseGit"), MB_ICONERROR
);
463 if (git_commit_lookup(commit
.GetPointer(), repository
, (git_oid
*)hash
.m_hash
))
465 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup commit.")), _T("TortoiseGit"), MB_ICONERROR
);
470 if (git_commit_tree(tree
.GetPointer(), commit
))
472 MessageBox(CGit::GetLibGit2LastErr(_T("Could not get tree of commit.")), _T("TortoiseGit"), MB_ICONERROR
);
478 CAutoTreeEntry treeEntry
;
479 if (git_tree_entry_bypath(treeEntry
.GetPointer(), tree
, CUnicodeUtils::GetUTF8(root
)))
481 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR
);
484 if (git_tree_entry_type(treeEntry
) != GIT_OBJ_TREE
)
486 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR
);
491 if (git_tree_entry_to_object(object
.GetPointer(), repository
, treeEntry
))
493 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR
);
497 tree
= (git_tree
*)object
.Detach();
500 treeroot
->m_hash
= CGitHash((char *)git_tree_id(tree
)->id
);
501 ReadTreeRecursive(*repository
, tree
, treeroot
);
503 // try to resolve hash to a branch name
504 if (m_sRevision
== hash
.ToString())
507 if (CGit::GetMapHashToFriendName(repository
, map
))
508 MessageBox(g_Git
.GetLibGit2LastErr(_T("Could not get all refs.")), _T("TortoiseGit"), MB_ICONERROR
);
509 if (!map
[hash
].empty())
510 m_sRevision
= map
[hash
].at(0);
512 this->GetDlgItem(IDC_BUTTON_REVISION
)->SetWindowText(m_sRevision
);
517 void CRepositoryBrowser::OnTvnSelchangedRepoTree(NMHDR
*pNMHDR
, LRESULT
*pResult
)
519 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
522 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
525 void CRepositoryBrowser::OnTvnItemExpandingRepoTree(NMHDR
*pNMHDR
, LRESULT
*pResult
)
527 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
530 CShadowFilesTree
* pTree
= (CShadowFilesTree
*)(m_RepoTree
.GetItemData(pNMTreeView
->itemNew
.hItem
));
537 if (!pTree
->m_bLoaded
)
539 pTree
->m_bLoaded
= true;
540 ReadTree(pTree
, pTree
->GetFullName());
544 void CRepositoryBrowser::FillListCtrlForTreeNode(HTREEITEM treeNode
)
546 m_RepoList
.DeleteAllItems();
548 CShadowFilesTree
* pTree
= (CShadowFilesTree
*)(m_RepoTree
.GetItemData(treeNode
));
555 CString url
= _T("/") + pTree
->GetFullName();
556 GetDlgItem(IDC_REPOBROWSER_URL
)->SetWindowText(url
);
558 if (!pTree
->m_bLoaded
)
560 pTree
->m_bLoaded
= true;
561 ReadTree(pTree
, pTree
->GetFullName());
564 FillListCtrlForShadowTree(pTree
);
567 void CRepositoryBrowser::FillListCtrlForShadowTree(CShadowFilesTree
* pTree
)
569 for (TShadowFilesTreeMap::iterator itShadowTree
= pTree
->m_ShadowTree
.begin(); itShadowTree
!= pTree
->m_ShadowTree
.end(); ++itShadowTree
)
571 int icon
= m_nIconFolder
;
572 if (!(*itShadowTree
).second
.m_bFolder
&& !(*itShadowTree
).second
.m_bSubmodule
)
574 icon
= SYS_IMAGE_LIST().GetFileIconIndex((*itShadowTree
).second
.m_sName
);
577 int indexItem
= m_RepoList
.InsertItem(m_RepoList
.GetItemCount(), (*itShadowTree
).second
.m_sName
, icon
);
579 if ((*itShadowTree
).second
.m_bSubmodule
)
581 m_RepoList
.SetItemState(indexItem
, INDEXTOOVERLAYMASK(OVERLAY_EXTERNAL
), LVIS_OVERLAYMASK
);
583 if ((*itShadowTree
).second
.m_bExecutable
)
584 m_RepoList
.SetItemState(indexItem
, INDEXTOOVERLAYMASK(OVERLAY_EXECUTABLE
), LVIS_OVERLAYMASK
);
585 if ((*itShadowTree
).second
.m_bSymlink
)
586 m_RepoList
.SetItemState(indexItem
, INDEXTOOVERLAYMASK(OVERLAY_SYMLINK
), LVIS_OVERLAYMASK
);
587 m_RepoList
.SetItemData(indexItem
, (DWORD_PTR
)&(*itShadowTree
).second
);
588 if (!(*itShadowTree
).second
.m_bFolder
&& !(*itShadowTree
).second
.m_bSubmodule
)
592 temp
= CPathUtils::GetFileExtFromPath((*itShadowTree
).second
.m_sName
);
593 m_RepoList
.SetItemText(indexItem
, eCol_Extension
, temp
);
595 StrFormatByteSize64((*itShadowTree
).second
.m_iSize
, temp
.GetBuffer(20), 20);
596 temp
.ReleaseBuffer();
597 m_RepoList
.SetItemText(indexItem
, eCol_FileSize
, temp
);
601 CRepoListCompareFunc
compareFunc(&m_RepoList
, m_currSortCol
, m_currSortDesc
);
602 m_RepoList
.SortItemsEx(&CRepoListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
604 SetSortArrowA(&m_RepoList
, m_currSortCol
, !m_currSortDesc
);
609 void CRepositoryBrowser::UpdateInfoLabel()
612 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
615 if (m_RepoList
.GetSelectedCount() > 1)
617 temp
.FormatMessage(IDS_REPOBROWSE_INFOMULTI
, m_RepoList
.GetSelectedCount());
621 int index
= m_RepoList
.GetNextSelectedItem(pos
);
622 CShadowFilesTree
*item
= (CShadowFilesTree
*)m_RepoList
.GetItemData(index
);
623 if (item
->m_bSubmodule
)
624 temp
.FormatMessage(IDS_REPOBROWSE_INFOEXT
, (LPCTSTR
)m_RepoList
.GetItemText(index
, eCol_Name
), item
->m_hash
.ToString());
625 else if (item
->m_bFolder
)
626 temp
.FormatMessage(IDS_REPOBROWSE_INFODIR
, (LPCTSTR
)m_RepoList
.GetItemText(index
, eCol_Name
));
628 temp
.FormatMessage(IDS_REPOBROWSE_INFOFILE
, (LPCTSTR
)m_RepoList
.GetItemText(index
, eCol_Name
), (LPCTSTR
)m_RepoList
.GetItemText(index
, eCol_FileSize
));
633 HTREEITEM hTreeItem
= m_RepoTree
.GetSelectedItem();
634 if (hTreeItem
!= nullptr)
636 CShadowFilesTree
* pTree
= (CShadowFilesTree
*)m_RepoTree
.GetItemData(hTreeItem
);
637 if (pTree
!= nullptr)
639 size_t files
= 0, submodules
= 0;
640 for (TShadowFilesTreeMap::iterator itShadowTree
= pTree
->m_ShadowTree
.begin(); itShadowTree
!= pTree
->m_ShadowTree
.end(); ++itShadowTree
)
642 if (!(*itShadowTree
).second
.m_bFolder
&& !(*itShadowTree
).second
.m_bSubmodule
)
644 if ((*itShadowTree
).second
.m_bSubmodule
)
647 temp
.FormatMessage(IDS_REPOBROWSE_INFO
, (LPCTSTR
)pTree
->m_sName
, files
, submodules
, pTree
->m_ShadowTree
.size() - files
- submodules
, pTree
->m_ShadowTree
.size());
651 SetDlgItemText(IDC_INFOLABEL
, temp
);
654 void CRepositoryBrowser::OnLvnItemchangedRepolist(NMHDR
* /* pNMHDR */, LRESULT
*pResult
)
660 void CRepositoryBrowser::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
662 if (pWndFrom
== &m_RepoList
)
664 CRect headerPosition
;
665 m_RepoList
.GetHeaderCtrl()->GetWindowRect(headerPosition
);
666 if (!headerPosition
.PtInRect(point
))
667 OnContextMenu_RepoList(point
);
669 else if (pWndFrom
== &m_RepoTree
)
670 OnContextMenu_RepoTree(point
);
673 void CRepositoryBrowser::OnContextMenu_RepoTree(CPoint point
)
675 CPoint clientPoint
= point
;
676 m_RepoTree
.ScreenToClient(&clientPoint
);
678 HTREEITEM hTreeItem
= m_RepoTree
.HitTest(clientPoint
);
679 if (hTreeItem
== NULL
)
682 TShadowFilesTreeList selectedLeafs
;
683 selectedLeafs
.push_back((CShadowFilesTree
*)m_RepoTree
.GetItemData(hTreeItem
));
685 ShowContextMenu(point
, selectedLeafs
, ONLY_FOLDERS
);
688 void CRepositoryBrowser::OnContextMenu_RepoList(CPoint point
)
690 TShadowFilesTreeList selectedLeafs
;
691 selectedLeafs
.reserve(m_RepoList
.GetSelectedCount());
693 bool folderSelected
= false;
694 bool filesSelected
= false;
695 bool submodulesSelected
= false;
697 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
700 CShadowFilesTree
* item
= (CShadowFilesTree
*)m_RepoList
.GetItemData(m_RepoList
.GetNextSelectedItem(pos
));
701 if (item
->m_bSubmodule
)
702 submodulesSelected
= true;
704 folderSelected
= true;
706 filesSelected
= true;
707 selectedLeafs
.push_back(item
);
710 eSelectionType selType
= ONLY_FILES
;
711 if (folderSelected
&& filesSelected
)
712 selType
= MIXED_FOLDERS_FILES
;
713 else if (folderSelected
)
714 selType
= ONLY_FOLDERS
;
715 else if (submodulesSelected
)
716 selType
= ONLY_FILESSUBMODULES
;
717 ShowContextMenu(point
, selectedLeafs
, selType
);
720 void CRepositoryBrowser::ShowContextMenu(CPoint point
, TShadowFilesTreeList
&selectedLeafs
, eSelectionType selType
)
723 popupMenu
.CreatePopupMenu();
725 bool bAddSeparator
= false;
727 if (selectedLeafs
.size() == 1)
729 popupMenu
.AppendMenuIcon(eCmd_Open
, IDS_REPOBROWSE_OPEN
, IDI_OPEN
);
730 popupMenu
.SetDefaultItem(eCmd_Open
, FALSE
);
731 if (selType
== ONLY_FILES
|| selType
== ONLY_FILESSUBMODULES
)
733 popupMenu
.AppendMenuIcon(eCmd_OpenWith
, IDS_LOG_POPUP_OPENWITH
, IDI_OPEN
);
734 popupMenu
.AppendMenuIcon(eCmd_OpenWithAlternativeEditor
, IDS_LOG_POPUP_VIEWREV
);
737 popupMenu
.AppendMenu(MF_SEPARATOR
);
739 if (m_bHasWC
&& (selType
== ONLY_FILES
|| selType
== ONLY_FILESSUBMODULES
))
741 popupMenu
.AppendMenuIcon(eCmd_CompareWC
, IDS_LOG_POPUP_COMPARE
, IDI_DIFF
);
742 bAddSeparator
= true;
746 popupMenu
.AppendMenu(MF_SEPARATOR
);
747 bAddSeparator
= false;
750 temp
.LoadString(IDS_MENULOG
);
751 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
752 if (selectedLeafs
[0]->m_bSubmodule
)
754 temp
.LoadString(IDS_MENULOGSUBMODULE
);
755 popupMenu
.AppendMenuIcon(eCmd_ViewLogSubmodule
, temp
, IDI_LOG
);
758 if (selType
== ONLY_FILES
)
761 popupMenu
.AppendMenuIcon(eCmd_Blame
, IDS_LOG_POPUP_BLAME
, IDI_BLAME
);
763 popupMenu
.AppendMenu(MF_SEPARATOR
);
764 temp
.LoadString(IDS_LOG_POPUP_SAVE
);
765 popupMenu
.AppendMenuIcon(eCmd_SaveAs
, temp
, IDI_SAVEAS
);
768 bAddSeparator
= true;
771 if (!selectedLeafs
.empty() && selType
== ONLY_FILES
&& m_bHasWC
)
773 popupMenu
.AppendMenuIcon(eCmd_Revert
, IDS_LOG_POPUP_REVERTTOREV
, IDI_REVERT
);
774 bAddSeparator
= true;
778 popupMenu
.AppendMenu(MF_SEPARATOR
);
779 bAddSeparator
= false;
781 if (!selectedLeafs
.empty())
783 popupMenu
.AppendMenuIcon(eCmd_CopyPath
, IDS_STATUSLIST_CONTEXT_COPY
, IDI_COPYCLIP
);
784 popupMenu
.AppendMenuIcon(eCmd_CopyHash
, IDS_COPY_COMMIT_HASH
, IDI_COPYCLIP
);
787 eCmd cmd
= (eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
791 case eCmd_ViewLogSubmodule
:
794 sCmd
.Format(_T("/command:log /path:\"%s\\%s\""), g_Git
.m_CurrentDir
, selectedLeafs
.at(0)->GetFullName());
795 if (cmd
== eCmd_ViewLog
&& selectedLeafs
.at(0)->m_bSubmodule
)
796 sCmd
+= _T(" /submodule");
797 CAppUtils::RunTortoiseGitProc(sCmd
);
802 CAppUtils::LaunchTortoiseBlame(g_Git
.CombinePath(selectedLeafs
.at(0)->GetFullName()), m_sRevision
);
806 if (!selectedLeafs
.at(0)->m_bSubmodule
&& selectedLeafs
.at(0)->m_bFolder
)
808 FillListCtrlForTreeNode(selectedLeafs
.at(0)->m_hTree
);
809 m_RepoTree
.SelectItem(selectedLeafs
.at(0)->m_hTree
);
812 OpenFile(selectedLeafs
.at(0)->GetFullName(), OPEN
, selectedLeafs
.at(0)->m_bSubmodule
, selectedLeafs
.at(0)->m_hash
);
815 OpenFile(selectedLeafs
.at(0)->GetFullName(), OPEN_WITH
, selectedLeafs
.at(0)->m_bSubmodule
, selectedLeafs
.at(0)->m_hash
);
817 case eCmd_OpenWithAlternativeEditor
:
818 OpenFile(selectedLeafs
.at(0)->GetFullName(), ALTERNATIVEEDITOR
, selectedLeafs
.at(0)->m_bSubmodule
, selectedLeafs
.at(0)->m_hash
);
822 CTGitPath
file(selectedLeafs
.at(0)->GetFullName());
823 CGitDiff::Diff(&file
, &file
, GIT_REV_ZERO
, m_sRevision
);
829 for (TShadowFilesTreeList::iterator itShadowTree
= selectedLeafs
.begin(); itShadowTree
!= selectedLeafs
.end(); ++itShadowTree
)
831 if (RevertItemToVersion((*itShadowTree
)->GetFullName()))
837 msg
.Format(IDS_STATUSLIST_FILESREVERTED
, count
, m_sRevision
);
838 MessageBox(msg
, _T("TortoiseGit"), MB_OK
);
842 FileSaveAs(selectedLeafs
.at(0)->GetFullName());
847 for (TShadowFilesTreeList::iterator itShadowTree
= selectedLeafs
.begin(); itShadowTree
!= selectedLeafs
.end(); ++itShadowTree
)
849 sClipboard
+= (*itShadowTree
)->m_sName
+ _T("\r\n");
851 CStringUtils::WriteAsciiStringToClipboard(sClipboard
);
856 CopyHashToClipboard(selectedLeafs
);
862 BOOL
CRepositoryBrowser::PreTranslateMessage(MSG
* pMsg
)
864 if (pMsg
->message
== WM_KEYDOWN
)
866 switch (pMsg
->wParam
)
876 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
879 void CRepositoryBrowser::OnLvnColumnclickRepoList(NMHDR
*pNMHDR
, LRESULT
*pResult
)
881 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
885 if (m_currSortCol
== pNMLV
->iSubItem
)
886 m_currSortDesc
= !m_currSortDesc
;
889 m_currSortCol
= pNMLV
->iSubItem
;
890 m_currSortDesc
= false;
893 CRepoListCompareFunc
compareFunc(&m_RepoList
, m_currSortCol
, m_currSortDesc
);
894 m_RepoList
.SortItemsEx(&CRepoListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
896 SetSortArrowA(&m_RepoList
, m_currSortCol
, !m_currSortDesc
);
899 void CRepositoryBrowser::OnBnClickedButtonRevision()
901 // use the git log to allow selection of a version
903 dlg
.SetParams(CTGitPath(), CTGitPath(), m_sRevision
, m_sRevision
, 0);
904 // tell the dialog to use mode for selecting revisions
906 // only one revision must be selected however
907 dlg
.SingleSelection(true);
908 if (dlg
.DoModal() == IDOK
)
910 // get selected hash if any
911 m_sRevision
= dlg
.GetSelectedHash();
916 void CRepositoryBrowser::SaveDividerPosition()
919 GetDlgItem(IDC_REPOTREE
)->GetClientRect(&rc
);
920 CRegDWORD
xPos(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RepobrowserDivider"));
921 xPos
= rc
.right
- rc
.left
;
924 void CRepositoryBrowser::HandleDividerMove(CPoint point
, bool bDraw
)
926 RECT rect
, tree
, list
, treelist
, treelistclient
;
928 // create an union of the tree and list control rectangle
929 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&list
);
930 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&tree
);
931 UnionRect(&treelist
, &tree
, &list
);
932 treelistclient
= treelist
;
933 ScreenToClient(&treelistclient
);
935 ClientToScreen(&point
);
936 GetClientRect(&rect
);
937 ClientToScreen(&rect
);
939 CPoint point2
= point
;
940 if (point2
.x
< treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
)
941 point2
.x
= treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
;
942 if (point2
.x
> treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
)
943 point2
.x
= treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
;
945 point
.x
-= rect
.left
;
946 point
.y
-= treelist
.top
;
948 OffsetRect(&treelist
, -treelist
.left
, -treelist
.top
);
950 if (point
.x
< treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
)
951 point
.x
= treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
;
952 if (point
.x
> treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
)
953 point
.x
= treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
;
958 DrawXorBar(pDC
, oldx
+ 2, treelistclient
.top
, 4, treelistclient
.bottom
- treelistclient
.top
- 2);
965 //position the child controls
966 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&treelist
);
967 treelist
.right
= point2
.x
- 2;
968 ScreenToClient(&treelist
);
969 RemoveAnchor(IDC_REPOTREE
);
970 GetDlgItem(IDC_REPOTREE
)->MoveWindow(&treelist
);
971 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&treelist
);
972 treelist
.left
= point2
.x
+ 2;
973 ScreenToClient(&treelist
);
974 RemoveAnchor(IDC_REPOLIST
);
975 GetDlgItem(IDC_REPOLIST
)->MoveWindow(&treelist
);
977 AddAnchor(IDC_REPOTREE
, TOP_LEFT
, BOTTOM_LEFT
);
978 AddAnchor(IDC_REPOLIST
, TOP_LEFT
, BOTTOM_RIGHT
);
981 void CRepositoryBrowser::OnMouseMove(UINT nFlags
, CPoint point
)
983 if (bDragMode
== FALSE
)
986 RECT rect
, tree
, list
, treelist
, treelistclient
;
987 // create an union of the tree and list control rectangle
988 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&list
);
989 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&tree
);
990 UnionRect(&treelist
, &tree
, &list
);
991 treelistclient
= treelist
;
992 ScreenToClient(&treelistclient
);
994 //convert the mouse coordinates relative to the top-left of
996 ClientToScreen(&point
);
997 GetClientRect(&rect
);
998 ClientToScreen(&rect
);
999 point
.x
-= rect
.left
;
1000 point
.y
-= treelist
.top
;
1002 //same for the window coordinates - make them relative to 0,0
1003 OffsetRect(&treelist
, -treelist
.left
, -treelist
.top
);
1005 if (point
.x
< treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
)
1006 point
.x
= treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
;
1007 if (point
.x
> treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
)
1008 point
.x
= treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
;
1010 if ((nFlags
& MK_LBUTTON
) && (point
.x
!= oldx
))
1012 CDC
* pDC
= GetDC();
1016 DrawXorBar(pDC
, oldx
+ 2, treelistclient
.top
, 4, treelistclient
.bottom
- treelistclient
.top
- 2);
1017 DrawXorBar(pDC
, point
.x
+ 2, treelistclient
.top
, 4, treelistclient
.bottom
- treelistclient
.top
- 2);
1026 CStandAloneDialogTmpl
<CResizableDialog
>::OnMouseMove(nFlags
, point
);
1029 void CRepositoryBrowser::OnLButtonDown(UINT nFlags
, CPoint point
)
1031 RECT rect
, tree
, list
, treelist
, treelistclient
;
1033 // create an union of the tree and list control rectangle
1034 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&list
);
1035 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&tree
);
1036 UnionRect(&treelist
, &tree
, &list
);
1037 treelistclient
= treelist
;
1038 ScreenToClient(&treelistclient
);
1040 //convert the mouse coordinates relative to the top-left of
1042 ClientToScreen(&point
);
1043 GetClientRect(&rect
);
1044 ClientToScreen(&rect
);
1045 point
.x
-= rect
.left
;
1046 point
.y
-= treelist
.top
;
1048 //same for the window coordinates - make them relative to 0,0
1049 OffsetRect(&treelist
, -treelist
.left
, -treelist
.top
);
1051 if (point
.x
< treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
)
1052 return CStandAloneDialogTmpl
< CResizableDialog
>::OnLButtonDown(nFlags
, point
);
1053 if (point
.x
> treelist
.right
- 3)
1054 return CStandAloneDialogTmpl
< CResizableDialog
>::OnLButtonDown(nFlags
, point
);
1055 if (point
.x
> treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
)
1056 point
.x
= treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
;
1058 if ((point
.y
< treelist
.top
+ 3) || (point
.y
> treelist
.bottom
- 3))
1059 return CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonDown(nFlags
, point
);
1065 CDC
* pDC
= GetDC();
1066 DrawXorBar(pDC
, point
.x
+ 2, treelistclient
.top
, 4, treelistclient
.bottom
- treelistclient
.top
- 2);
1072 CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonDown(nFlags
, point
);
1075 void CRepositoryBrowser::OnLButtonUp(UINT nFlags
, CPoint point
)
1077 if (bDragMode
== FALSE
)
1080 HandleDividerMove(point
, true);
1085 CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonUp(nFlags
, point
);
1088 void CRepositoryBrowser::OnCaptureChanged(CWnd
*pWnd
)
1092 __super::OnCaptureChanged(pWnd
);
1095 void CRepositoryBrowser::DrawXorBar(CDC
* pDC
, int x1
, int y1
, int width
, int height
)
1097 static WORD _dotPatternBmp
[8] =
1099 0x0055, 0x00aa, 0x0055, 0x00aa,
1100 0x0055, 0x00aa, 0x0055, 0x00aa
1104 HBRUSH hbr
, hbrushOld
;
1106 hbm
= CreateBitmap(8, 8, 1, 1, _dotPatternBmp
);
1107 hbr
= CreatePatternBrush(hbm
);
1109 pDC
->SetBrushOrg(x1
, y1
);
1110 hbrushOld
= (HBRUSH
)pDC
->SelectObject(hbr
);
1112 PatBlt(pDC
->GetSafeHdc(), x1
, y1
, width
, height
, PATINVERT
);
1114 pDC
->SelectObject(hbrushOld
);
1120 BOOL
CRepositoryBrowser::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
1126 GetClientRect(&rect
);
1128 ScreenToClient(&pt
);
1129 if (PtInRect(&rect
, pt
))
1131 ClientToScreen(&pt
);
1132 // are we right of the tree control?
1133 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&rect
);
1134 if ((pt
.x
> rect
.right
) && (pt
.y
>= rect
.top
+ 3) && (pt
.y
<= rect
.bottom
- 3))
1136 // but left of the list control?
1137 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&rect
);
1138 if (pt
.x
< rect
.left
)
1140 HCURSOR hCur
= LoadCursor(NULL
, MAKEINTRESOURCE(IDC_SIZEWE
));
1147 return CStandAloneDialogTmpl
<CResizableDialog
>::OnSetCursor(pWnd
, nHitTest
, message
);
1150 void CRepositoryBrowser::FileSaveAs(const CString path
)
1152 CTGitPath
gitPath(path
);
1155 if (g_Git
.GetHash(hash
, m_sRevision
))
1157 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of ") + m_sRevision
+ _T(".")), _T("TortoiseGit"), MB_ICONERROR
);
1162 filename
.Format(_T("%s-%s%s"), gitPath
.GetBaseFilename(), hash
.ToString().Left(g_Git
.GetShortHASHLength()), gitPath
.GetFileExtension());
1163 CFileDialog
dlg(FALSE
, NULL
, filename
, OFN_HIDEREADONLY
| OFN_OVERWRITEPROMPT
, NULL
);
1165 CString
currentpath(g_Git
.CombinePath(gitPath
.GetContainingDirectory()));
1166 dlg
.m_ofn
.lpstrInitialDir
= currentpath
.GetBuffer();
1169 INT_PTR ret
= dlg
.DoModal();
1170 SetCurrentDirectory(g_Git
.m_CurrentDir
);
1173 filename
= dlg
.GetPathName();
1174 if (g_Git
.GetOneFile(m_sRevision
, gitPath
, filename
))
1176 out
.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED
, gitPath
.GetGitPathString(), m_sRevision
, filename
);
1177 MessageBox(g_Git
.GetGitLastErr(out
, CGit::GIT_CMD_GETONEFILE
), _T("TortoiseGit"), MB_ICONERROR
);
1183 void CRepositoryBrowser::OpenFile(const CString path
, eOpenType mode
, bool isSubmodule
, CGitHash itemHash
)
1185 CTGitPath
gitPath(path
);
1189 GetTempPath(temppath
);
1191 if (g_Git
.GetHash(hash
, m_sRevision
))
1193 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of ") + m_sRevision
+ _T(".")), _T("TortoiseGit"), MB_ICONERROR
);
1197 file
.Format(_T("%s%s_%s%s"), temppath
, gitPath
.GetBaseFilename(), hash
.ToString().Left(g_Git
.GetShortHASHLength()), gitPath
.GetFileExtension());
1201 if (mode
== OPEN
&& !g_GitAdminDir
.IsBareRepo(g_Git
.m_CurrentDir
))
1203 CTGitPath subPath
= CTGitPath(g_Git
.m_CurrentDir
);
1204 subPath
.AppendPathString(gitPath
.GetWinPathString());
1205 CAutoRepository
repo(subPath
.GetGitPathString());
1207 if (!repo
|| git_commit_lookup(commit
.GetPointer(), repo
, (const git_oid
*)itemHash
.m_hash
))
1210 out
.Format(IDS_REPOBROWSEASKSUBMODULEUPDATE
, itemHash
.ToString(), gitPath
.GetGitPathString());
1211 if (MessageBox(out
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) != IDYES
)
1215 sCmd
.Format(_T("/command:subupdate /bkpath:\"%s\" /selectedpath:\"%s\""), g_Git
.m_CurrentDir
, gitPath
.GetGitPathString());
1216 CAppUtils::RunTortoiseGitProc(sCmd
);
1221 cmd
.Format(_T("/command:repobrowser /path:\"%s\" /rev:%s"), g_Git
.CombinePath(path
), itemHash
.ToString());
1222 CAppUtils::RunTortoiseGitProc(cmd
);
1227 CFile
submoduleCommit(file
, CFile::modeCreate
| CFile::modeWrite
);
1228 CStringA commitInfo
= "Subproject commit " + CStringA(itemHash
.ToString());
1229 submoduleCommit
.Write(commitInfo
, commitInfo
.GetLength());
1231 else if (g_Git
.GetOneFile(m_sRevision
, gitPath
, file
))
1234 out
.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED
, gitPath
.GetGitPathString(), m_sRevision
, file
);
1235 MessageBox(g_Git
.GetGitLastErr(out
, CGit::GIT_CMD_GETONEFILE
), _T("TortoiseGit"), MB_ICONERROR
);
1239 if (mode
== ALTERNATIVEEDITOR
)
1241 CAppUtils::LaunchAlternativeEditor(file
);
1244 else if (mode
== OPEN
)
1246 CAppUtils::ShellOpen(file
);
1250 CAppUtils::ShowOpenWithDialog(file
);
1252 bool CRepositoryBrowser::RevertItemToVersion(const CString
&path
)
1255 cmd
.Format(_T("git.exe checkout %s -- \"%s\""), m_sRevision
, path
);
1256 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
1258 if (MessageBox(out
, _T("TortoiseGit"), MB_ICONEXCLAMATION
| MB_OKCANCEL
) == IDCANCEL
)
1265 void CRepositoryBrowser::CopyHashToClipboard(TShadowFilesTreeList
&selectedLeafs
)
1267 if (!selectedLeafs
.empty())
1271 for (size_t i
= 0; i
< selectedLeafs
.size(); ++i
)
1274 sClipdata
+= _T("\r\n");
1275 sClipdata
+= selectedLeafs
[i
]->m_hash
;
1278 CStringUtils::WriteAsciiStringToClipboard(sClipdata
, GetSafeHwnd());