1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2015 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // SettingGitRemote.cpp : implementation file
23 #include "TortoiseProc.h"
24 #include "SettingGitRemote.h"
26 #include "GitAdminDir.h"
27 #include "MessageBox.h"
30 #include "HistoryCombo.h"
32 // CSettingGitRemote dialog
34 IMPLEMENT_DYNAMIC(CSettingGitRemote
, ISettingsPropPage
)
36 CSettingGitRemote::CSettingGitRemote()
37 : ISettingsPropPage(CSettingGitRemote::IDD
)
40 , m_strPuttyKeyfile(_T(""))
44 , m_bPushDefault(FALSE
)
50 CSettingGitRemote::~CSettingGitRemote()
54 void CSettingGitRemote::DoDataExchange(CDataExchange
* pDX
)
56 CPropertyPage::DoDataExchange(pDX
);
57 DDX_Control(pDX
, IDC_LIST_REMOTE
, m_ctrlRemoteList
);
58 DDX_Text(pDX
, IDC_EDIT_REMOTE
, m_strRemote
);
59 DDX_Text(pDX
, IDC_EDIT_URL
, m_strUrl
);
60 DDX_Text(pDX
, IDC_EDIT_PUSHURL
, m_strPushUrl
);
61 DDX_Text(pDX
, IDC_EDIT_PUTTY_KEY
, m_strPuttyKeyfile
);
62 DDX_Control(pDX
, IDC_COMBO_TAGOPT
, m_ctrlTagOpt
);
63 DDX_Check(pDX
, IDC_CHECK_PRUNE
, m_bPrune
);
64 DDX_Check(pDX
, IDC_CHECK_PRUNEALL
, m_bPruneAll
);
65 DDX_Check(pDX
, IDC_CHECK_PUSHDEFAULT
, m_bPushDefault
);
69 BEGIN_MESSAGE_MAP(CSettingGitRemote
, CPropertyPage
)
71 ON_BN_CLICKED(IDC_BUTTON_BROWSE
, &CSettingGitRemote::OnBnClickedButtonBrowse
)
72 ON_BN_CLICKED(IDC_BUTTON_ADD
, &CSettingGitRemote::OnBnClickedButtonAdd
)
73 ON_LBN_SELCHANGE(IDC_LIST_REMOTE
, &CSettingGitRemote::OnLbnSelchangeListRemote
)
74 ON_EN_CHANGE(IDC_EDIT_REMOTE
, &CSettingGitRemote::OnEnChangeEditRemote
)
75 ON_EN_CHANGE(IDC_EDIT_URL
, &CSettingGitRemote::OnEnChangeEditUrl
)
76 ON_EN_CHANGE(IDC_EDIT_PUSHURL
, &CSettingGitRemote::OnEnChangeEditPushUrl
)
77 ON_EN_CHANGE(IDC_EDIT_PUTTY_KEY
, &CSettingGitRemote::OnEnChangeEditPuttyKey
)
78 ON_CBN_SELCHANGE(IDC_COMBO_TAGOPT
, &CSettingGitRemote::OnCbnSelchangeComboTagOpt
)
79 ON_BN_CLICKED(IDC_CHECK_PRUNE
, &CSettingGitRemote::OnBnClickedCheckprune
)
80 ON_BN_CLICKED(IDC_CHECK_PRUNEALL
, &CSettingGitRemote::OnBnClickedCheckpruneall
)
81 ON_BN_CLICKED(IDC_CHECK_PUSHDEFAULT
, &CSettingGitRemote::OnBnClickedCheckpushdefault
)
82 ON_BN_CLICKED(IDC_BUTTON_REMOVE
, &CSettingGitRemote::OnBnClickedButtonRemove
)
83 ON_BN_CLICKED(IDC_BUTTON_RENAME_REMOTE
, &CSettingGitRemote::OnBnClickedButtonRenameRemote
)
86 static void ShowEditBalloon(HWND hDlg
, UINT nIdControl
, UINT nIdText
, UINT nIdTitle
, int nIcon
= TTI_WARNING
)
88 CString
text(MAKEINTRESOURCE(nIdText
));
89 CString
title(MAKEINTRESOURCE(nIdTitle
));
91 bt
.cbStruct
= sizeof(bt
);
95 SendDlgItemMessage(hDlg
, nIdControl
, EM_SHOWBALLOONTIP
, 0, (LPARAM
)&bt
);
98 #define TIMER_PREFILL 1
100 BOOL
CSettingGitRemote::OnInitDialog()
102 ISettingsPropPage::OnInitDialog();
104 AdjustControlSize(IDC_CHECK_PRUNE
);
105 AdjustControlSize(IDC_CHECK_PRUNEALL
);
106 AdjustControlSize(IDC_CHECK_PUSHDEFAULT
);
108 STRING_VECTOR remotes
;
109 g_Git
.GetRemoteList(remotes
);
110 m_ctrlRemoteList
.ResetContent();
111 for (size_t i
= 0; i
< remotes
.size(); i
++)
112 m_ctrlRemoteList
.AddString(remotes
[i
]);
114 m_ctrlTagOpt
.AddString(CString(MAKEINTRESOURCE(IDS_FETCH_REACHABLE
)));
115 m_ctrlTagOpt
.AddString(CString(MAKEINTRESOURCE(IDS_NONE
)));
116 m_ctrlTagOpt
.AddString(CString(MAKEINTRESOURCE(CAppUtils::GetMsysgitVersion() < 0x01090000 ? IDS_FETCH_TAGS_ONLY
: IDS_ALL
)));
117 m_ctrlTagOpt
.SetCurSel(0);
119 if (CAppUtils::GetMsysgitVersion() < 0x0108030)
121 GetDlgItem(IDC_CHECK_PUSHDEFAULT
)->ShowWindow(SW_HIDE
);
123 if (CAppUtils::GetMsysgitVersion() < 0x0108050)
125 GetDlgItem(IDC_CHECK_PRUNE
)->ShowWindow(SW_HIDE
);
126 GetDlgItem(IDC_CHECK_PRUNEALL
)->ShowWindow(SW_HIDE
);
129 CString pruneAll
= g_Git
.GetConfigValue(_T("fetch.prune"));
130 m_bPruneAll
= pruneAll
== _T("true") ? TRUE
: FALSE
;
132 //this->GetDlgItem(IDC_EDIT_REMOTE)->EnableWindow(FALSE);
133 this->UpdateData(FALSE
);
135 SetTimer(TIMER_PREFILL
, 1000, nullptr);
138 // CSettingGitRemote message handlers
140 void CSettingGitRemote::OnTimer(UINT_PTR nIDEvent
)
142 if (nIDEvent
== TIMER_PREFILL
)
144 if (m_strRemote
.IsEmpty() && m_ctrlRemoteList
.GetCount() == 0)
146 ShowEditBalloon(m_hWnd
, IDC_EDIT_URL
, IDS_B_T_PREFILL_ORIGIN
, IDS_HINT
, TTI_INFO
);
149 KillTimer(TIMER_PREFILL
);
153 void CSettingGitRemote::OnBnClickedButtonBrowse()
155 CFileDialog
dlg(TRUE
,NULL
,
157 OFN_HIDEREADONLY
| OFN_OVERWRITEPROMPT
,
158 CString(MAKEINTRESOURCE(IDS_PUTTYKEYFILEFILTER
)));
161 INT_PTR ret
= dlg
.DoModal();
162 SetCurrentDirectory(g_Git
.m_CurrentDir
);
165 this->m_strPuttyKeyfile
= dlg
.GetPathName();
166 this->UpdateData(FALSE
);
167 OnEnChangeEditPuttyKey();
171 void CSettingGitRemote::OnBnClickedButtonAdd()
175 if(m_strRemote
.IsEmpty())
177 CMessageBox::Show(NULL
, IDS_PROC_GITCONFIG_REMOTEEMPTY
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
180 if(m_strUrl
.IsEmpty())
182 CMessageBox::Show(NULL
, IDS_PROC_GITCONFIG_URLEMPTY
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
186 m_ChangedMask
= REMOTE_NAME
| REMOTE_URL
| REMOTE_PUTTYKEY
| REMOTE_TAGOPT
| REMOTE_PRUNE
| REMOTE_PRUNEALL
| REMOTE_PUSHDEFAULT
| REMOTE_PUSHURL
;
187 if(IsRemoteExist(m_strRemote
))
190 msg
.Format(IDS_PROC_GITCONFIG_OVERWRITEREMOTE
, (LPCTSTR
)m_strRemote
);
191 if(CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
| MB_DEFBUTTON2
) == IDYES
)
193 m_ChangedMask
&= ~REMOTE_NAME
;
202 BOOL
CSettingGitRemote::IsRemoteExist(CString
&remote
)
205 for(int i
=0;i
<m_ctrlRemoteList
.GetCount();i
++)
207 m_ctrlRemoteList
.GetText(i
,str
);
216 struct CheckRefspecStruct
222 static int CheckRemoteCollideWithRefspec(const git_config_entry
*entry
, void * payload
)
224 auto crs
= (CheckRefspecStruct
*)payload
;
226 if (entry
->name
== "remote." + crs
->remote
+ ".fetch")
228 CStringA value
= CStringA(entry
->value
);
229 CStringA match
= ":refs/remotes/" + crs
->remote
;
230 int pos
= value
.Find(match
);
233 if ((pos
+ match
.GetLength() == value
.GetLength()) || (value
[pos
+ match
.GetLength()] == '/'))
241 bool CSettingGitRemote::IsRemoteCollideWithRefspec(CString remote
)
243 CheckRefspecStruct crs
= { CUnicodeUtils::GetUTF8(remote
), false };
244 CAutoConfig
config(true);
245 git_config_add_file_ondisk(config
, CGit::GetGitPathStringA(g_Git
.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL
, FALSE
);
246 char *patterns
[] = { "remote\\..*\\.fetch", "svn-remote\\..*\\.fetch", "svn-remote\\..*\\.branches", "svn-remote\\..*\\.tags" };
247 for (auto pattern
: patterns
)
249 git_config_foreach_match(config
, pattern
, CheckRemoteCollideWithRefspec
, &crs
);
256 void CSettingGitRemote::OnLbnSelchangeListRemote()
262 if(CMessageBox::Show(NULL
, IDS_PROC_GITCONFIG_SAVEREMOTE
, IDS_APPNAME
, 1, IDI_QUESTION
, IDS_SAVEBUTTON
, IDS_DISCARDBUTTON
) == 1)
269 index
= this->m_ctrlRemoteList
.GetCurSel();
273 m_strPushUrl
.Empty();
275 m_strPuttyKeyfile
.Empty();
276 this->UpdateData(FALSE
);
280 m_ctrlRemoteList
.GetText(index
,remote
);
281 this->m_strRemote
=remote
;
283 cmd
.Format(_T("remote.%s.url"), (LPCTSTR
)remote
);
285 m_strUrl
= g_Git
.GetConfigValue(cmd
);
287 cmd
.Format(_T("remote.%s.pushurl"), (LPCTSTR
)remote
);
288 m_strPushUrl
.Empty();
289 m_strPushUrl
= g_Git
.GetConfigValue(cmd
);
291 cmd
.Format(_T("remote.%s.puttykeyfile"), (LPCTSTR
)remote
);
293 this->m_strPuttyKeyfile
= g_Git
.GetConfigValue(cmd
);
298 cmd
.Format(_T("remote.%s.tagopt"), (LPCTSTR
)remote
);
299 CString tagopt
= g_Git
.GetConfigValue(cmd
);
301 if (tagopt
== "--no-tags")
303 else if (tagopt
== "--tags")
305 m_ctrlTagOpt
.SetCurSel(index
);
307 CString pushDefault
= g_Git
.GetConfigValue(_T("remote.pushdefault"));
308 m_bPushDefault
= pushDefault
== remote
? TRUE
: FALSE
;
309 cmd
.Format(_T("remote.%s.prune"), (LPCTSTR
)remote
);
310 CString prune
= g_Git
.GetConfigValue(cmd
);
311 m_bPrune
= prune
== _T("true") ? TRUE
: prune
== _T("false") ? FALSE
: 2;
312 CString pruneAll
= g_Git
.GetConfigValue(_T("fetch.prune"));
313 m_bPruneAll
= pruneAll
== _T("true") ? TRUE
: FALSE
;
315 GetDlgItem(IDC_BUTTON_ADD
)->EnableWindow(TRUE
);
316 GetDlgItem(IDC_BUTTON_REMOVE
)->EnableWindow(TRUE
);
317 GetDlgItem(IDC_BUTTON_RENAME_REMOTE
)->EnableWindow(TRUE
);
318 this->UpdateData(FALSE
);
322 void CSettingGitRemote::OnEnChangeEditRemote()
324 m_ChangedMask
|=REMOTE_NAME
;
328 if (IsRemoteCollideWithRefspec(m_strRemote
))
329 ShowEditBalloon(m_hWnd
, IDC_EDIT_REMOTE
, IDS_B_T_REMOTE_NAME_COLLIDE
, IDS_HINT
, TTI_WARNING
);
330 if( (!this->m_strRemote
.IsEmpty())&&(!this->m_strUrl
.IsEmpty()) )
334 void CSettingGitRemote::OnEnChangeEditUrl()
336 m_ChangedMask
|=REMOTE_URL
;
340 if (m_strRemote
.IsEmpty() && !m_strUrl
.IsEmpty() && m_ctrlRemoteList
.GetCount() == 0)
342 GetDlgItem(IDC_EDIT_REMOTE
)->SetWindowText(_T("origin"));
343 OnEnChangeEditRemote();
346 if( (!this->m_strRemote
.IsEmpty())&&(!this->m_strUrl
.IsEmpty()) )
350 void CSettingGitRemote::OnEnChangeEditPushUrl()
352 m_ChangedMask
|= REMOTE_PUSHURL
;
356 if (!this->m_strRemote
.IsEmpty())
360 void CSettingGitRemote::OnEnChangeEditPuttyKey()
362 m_ChangedMask
|=REMOTE_PUTTYKEY
;
365 if (!this->m_strUrl
.IsEmpty())
369 void CSettingGitRemote::OnCbnSelchangeComboTagOpt()
371 m_ChangedMask
|= REMOTE_TAGOPT
;
377 void CSettingGitRemote::OnBnClickedCheckprune()
379 m_ChangedMask
|= REMOTE_PRUNE
;
385 void CSettingGitRemote::OnBnClickedCheckpruneall()
387 m_ChangedMask
|= REMOTE_PRUNEALL
;
392 void CSettingGitRemote::OnBnClickedCheckpushdefault()
394 m_ChangedMask
|= REMOTE_PUSHDEFAULT
;
399 BOOL
CSettingGitRemote::Save(CString key
,CString value
)
403 cmd
.Format(_T("remote.%s.%s"), (LPCTSTR
)m_strRemote
, (LPCTSTR
)key
);
406 // don't check result code. it fails if the entry not exist
407 g_Git
.UnsetConfigValue(cmd
, CONFIG_LOCAL
);
408 if (!g_Git
.GetConfigValue(cmd
).IsEmpty())
411 msg
.Format(IDS_PROC_SAVECONFIGFAILED
, (LPCTSTR
)cmd
, (LPCTSTR
)value
);
412 CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
418 if (g_Git
.SetConfigValue(cmd
, value
, CONFIG_LOCAL
))
421 msg
.Format(IDS_PROC_SAVECONFIGFAILED
, (LPCTSTR
)cmd
, (LPCTSTR
)value
);
422 CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
428 BOOL
CSettingGitRemote::SaveGeneral(CString key
, CString value
)
432 // don't check result code. it fails if the entry not exist
433 g_Git
.UnsetConfigValue(key
, CONFIG_LOCAL
);
434 if (!g_Git
.GetConfigValue(key
).IsEmpty())
437 msg
.Format(IDS_PROC_SAVECONFIGFAILED
, (LPCTSTR
)key
, (LPCTSTR
)value
);
438 CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
444 if (g_Git
.SetConfigValue(key
, value
, CONFIG_LOCAL
))
447 msg
.Format(IDS_PROC_SAVECONFIGFAILED
, (LPCTSTR
)key
, (LPCTSTR
)value
);
448 CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
455 BOOL
CSettingGitRemote::OnApply()
460 if (m_ChangedMask
& REMOTE_PUSHDEFAULT
)
462 if (!m_strRemote
.Trim().IsEmpty() && m_bPushDefault
)
464 if (!SaveGeneral(_T("remote.pushdefault"), m_strRemote
.Trim()))
469 if (!SaveGeneral(_T("remote.pushdefault"), _T("")))
473 m_ChangedMask
&= ~REMOTE_PUSHDEFAULT
;
476 if (m_ChangedMask
& REMOTE_PRUNEALL
)
478 if (!SaveGeneral(_T("fetch.prune"), m_bPruneAll
== TRUE
? _T("true") : _T("")))
480 m_ChangedMask
&= ~REMOTE_PRUNEALL
;
483 if (m_ChangedMask
&& m_strRemote
.Trim().IsEmpty())
485 CMessageBox::Show(NULL
, IDS_PROC_GITCONFIG_REMOTEEMPTY
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
489 if(m_ChangedMask
& REMOTE_NAME
)
492 if(m_strRemote
.IsEmpty())
494 CMessageBox::Show(NULL
, IDS_PROC_GITCONFIG_REMOTEEMPTY
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
497 if(m_strUrl
.IsEmpty())
499 CMessageBox::Show(NULL
, IDS_PROC_GITCONFIG_URLEMPTY
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
503 if (m_ctrlRemoteList
.GetCount() > 0)
505 // tagopt not --no-tags
506 if (m_ctrlTagOpt
.GetCurSel() != 1)
508 if (CMessageBox::ShowCheck(GetSafeHwnd(), IDS_PROC_GITCONFIG_ASKTAGOPT
, IDS_APPNAME
, MB_YESNO
| MB_ICONQUESTION
, _T("TagOptNoTagsWarning"), IDS_MSGBOX_DONOTSHOWAGAIN
) == IDYES
)
510 m_ctrlTagOpt
.SetCurSel(1);
511 m_ChangedMask
|= REMOTE_TAGOPT
;
516 m_strUrl
.Replace(L
'\\', L
'/');
518 cmd
.Format(_T("git.exe remote add \"%s\" \"%s\""), (LPCTSTR
)m_strRemote
, (LPCTSTR
)m_strUrl
);
519 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
521 CMessageBox::Show(NULL
,out
,_T("TorotiseGit"),MB_OK
|MB_ICONERROR
);
524 m_ChangedMask
&= ~REMOTE_URL
;
526 m_ctrlRemoteList
.SetCurSel(m_ctrlRemoteList
.AddString(m_strRemote
));
527 GetDlgItem(IDC_BUTTON_ADD
)->EnableWindow(TRUE
);
528 GetDlgItem(IDC_BUTTON_RENAME_REMOTE
)->EnableWindow(TRUE
);
529 if (!m_bNoFetch
&& CMessageBox::Show(NULL
, IDS_SETTINGS_FETCH_ADDEDREMOTE
, IDS_APPNAME
, MB_ICONQUESTION
| MB_YESNO
) == IDYES
)
530 CCommonAppUtils::RunTortoiseGitProc(_T("/command:fetch /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /remote:\"") + m_strRemote
+ _T("\""));
532 if(m_ChangedMask
& REMOTE_URL
)
534 m_strUrl
.Replace(L
'\\', L
'/');
535 if (!Save(_T("url"),this->m_strUrl
))
539 if(m_ChangedMask
& REMOTE_PUTTYKEY
)
541 if (!Save(_T("puttykeyfile"),this->m_strPuttyKeyfile
))
545 if (m_ChangedMask
& REMOTE_TAGOPT
)
548 int index
= m_ctrlTagOpt
.GetCurSel();
550 tagopt
= "--no-tags";
553 if (!Save(_T("tagopt"), tagopt
))
557 if (m_ChangedMask
& REMOTE_PRUNE
)
559 if (!Save(_T("prune"), m_bPrune
== TRUE
? _T("true") : m_bPrune
== FALSE
? _T("false") : _T("")))
563 if (m_ChangedMask
& REMOTE_PUSHURL
)
565 m_strPushUrl
.Replace(L
'\\', L
'/');
566 if (!Save(_T("pushurl"), m_strPushUrl
))
573 return ISettingsPropPage::OnApply();
576 void CleanupSyncRemotes(const CString
& remote
)
578 CString workingDir
= g_Git
.m_CurrentDir
;
579 workingDir
.Replace(_T(':'), _T('_'));
580 CHistoryCombo::RemoveEntryFromHistory(_T("Software\\TortoiseGit\\History\\SyncURL\\") + workingDir
, _T("url"), remote
);
583 void CSettingGitRemote::OnBnClickedButtonRemove()
586 index
=m_ctrlRemoteList
.GetCurSel();
590 m_ctrlRemoteList
.GetText(index
,str
);
592 msg
.Format(IDS_WARN_REMOVE
, (LPCTSTR
)str
);
593 if(CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) == IDYES
)
596 cmd
.Format(_T("git.exe remote rm %s"), (LPCTSTR
)str
);
597 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
599 CMessageBox::Show(NULL
, out
,_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
603 CleanupSyncRemotes(str
);
604 m_ctrlRemoteList
.DeleteString(index
);
605 OnLbnSelchangeListRemote();
610 void CSettingGitRemote::OnBnClickedButtonRenameRemote()
612 int sel
= m_ctrlRemoteList
.GetCurSel();
615 CString oldRemote
, newRemote
;
616 m_ctrlRemoteList
.GetText(sel
, oldRemote
);
617 GetDlgItem(IDC_EDIT_REMOTE
)->GetWindowText(newRemote
);
619 cmd
.Format(_T("git.exe remote rename %s %s"), (LPCTSTR
)oldRemote
, (LPCTSTR
)newRemote
);
620 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
622 CMessageBox::Show(NULL
, out
,_T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
626 CleanupSyncRemotes(oldRemote
);
627 m_ctrlRemoteList
.DeleteString(sel
);
628 m_ctrlRemoteList
.SetCurSel(m_ctrlRemoteList
.AddString(newRemote
));
629 m_ChangedMask
&= ~REMOTE_NAME
;
631 this->SetModified(FALSE
);