25#include <wx/richmsgdlg.h>
26#include <wx/textdlg.h>
46 m_branchesList->AppendColumn(
_(
"Branch" ), wxLIST_FORMAT_LEFT, FromDIP( 220 ) );
47 m_branchesList->AppendColumn(
_(
"Status" ), wxLIST_FORMAT_LEFT, FromDIP( 70 ) );
48 m_branchesList->AppendColumn(
_(
"Commit" ), wxLIST_FORMAT_LEFT, FromDIP( 100 ) );
49 m_branchesList->AppendColumn(
_(
"Date" ), wxLIST_FORMAT_LEFT, FromDIP( 90 ) );
59 [
this]( wxCommandEvent& )
65 [
this]( wxListEvent& aEvent )
72 [
this]( wxListEvent& )
75 &&
m_branchesList->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED ) == -1 )
82 m_branchesList->SetToolTip(
_(
"Double click a local branch to switch the project to it." ) );
84 [
this]( wxListEvent& aEvent )
86 int row = aEvent.GetIndex();
93 "press New Branch to start your own branch "
102 [
this]( wxCommandEvent& )
107 [
this]( wxCommandEvent& )
112 [
this]( wxCommandEvent& )
133 if( !
m_dialog->SessionStillCurrent() )
138 auto branches = std::make_shared<std::vector<KIGIT_BRANCH_INFO>>();
144 *branches = aCommon.GetBranches();
145 KIGIT::SortBranchesForDisplay( *branches );
149 if( !m_dialog->SessionStillCurrent() )
153 wxString search = m_branchSearch->GetValue().Lower();
154 wxString selected = KIGIT::SelectedText( m_branchesList, 0 );
155 long selectRow = wxNOT_FOUND;
157 m_detailQuery.Cancel();
158 m_fillingBranches = true;
159 m_branchesList->DeleteAllItems();
160 m_branchIsRemote.clear();
162 for( const KIGIT_BRANCH_INFO& branch : *branches )
164 if( !search.IsEmpty() && !branch.name.Lower().Contains( search ) )
167 long idx = m_branchesList->InsertItem( m_branchesList->GetItemCount(), branch.name );
169 if( branch.isCurrent )
170 m_branchesList->SetItem( idx, 1, _(
"current" ) );
171 else if( branch.isRemote )
172 m_branchesList->SetItem( idx, 1, _(
"remote" ) );
174 m_branchesList->SetItem( idx, 2, branch.tipHash );
175 m_branchesList->SetItem( idx, 3, KIGIT::RelativeDate( branch.when ) );
176 m_branchIsRemote.push_back( branch.isRemote );
179 if( branch.name == selected || ( selected.IsEmpty() && branch.isCurrent ) )
184 m_branchCount->SetLabel( shown == 1 ?
_(
"1 branch" ) : wxString::Format(
_(
"%d branches" ), shown ) );
186 if( selectRow != wxNOT_FOUND )
188 m_branchesList->SetItemState( selectRow, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
211 if( aBranch.IsEmpty() || !
m_dialog->SessionStillCurrent() )
214 auto detail = std::make_shared<KIGIT_BRANCH_DETAIL>();
220 *detail = aCommon.GetBranchDetail( aBranch );
224 if( m_dialog->SessionStillCurrent() && KIGIT::SelectedText( m_branchesList, 0 ) == aBranch )
226 fillBranchDetail( *detail );
249 sync =
_(
"Not tracking another branch." );
251 sync = wxString::Format(
_(
"Could not compare with %s." ), aDetail.
upstream );
257 sync = wxString::Format(
_(
"Up to date with %s." ), aDetail.
upstream );
258 else if( counts.
behind == 0 )
259 sync = wxString::Format(
_(
"%zu commits ahead of %s." ), counts.
ahead, aDetail.
upstream );
260 else if( counts.
ahead == 0 )
261 sync = wxString::Format(
_(
"%zu commits behind %s." ), counts.
behind, aDetail.
upstream );
263 sync = wxString::Format(
_(
"%zu commits ahead and %zu behind %s." ), counts.
ahead, counts.
behind,
285 if( !
m_dialog->SessionStillCurrent() )
291 from.IsEmpty() ?
_(
"New branch name:" ) : wxString::Format(
_(
"New branch from '%s', name:" ), from );
297 if( !nameError.IsEmpty() )
300 name = wxGetTextFromUser( prompt,
_(
"New Branch" ),
name,
this );
302 if(
name.IsEmpty() || !
m_dialog->SessionStillCurrent() )
306 }
while( !nameError.IsEmpty() );
313 wxString::Format(
_(
"Could not create branch '%s'." ),
name ) );
319 if( !
m_dialog->SessionStillCurrent() )
331 DisplayInfoMessage(
this,
_(
"This branch lives on the remote, so it cannot be deleted from here." ) );
338 std::optional<size_t> unmerged;
339 bool checked =
false;
342 auto outcome = std::make_shared<OUTCOME>();
348 outcome->current = aCommon.GetCurrentBranchName();
349 outcome->unmerged = aCommon.GetUnmergedCommitCount( name );
350 outcome->checked = true;
352 [
this, outcome,
name]()
354 if( !m_dialog->SessionStillCurrent() || !outcome->checked )
357 if( name == outcome->current )
359 DisplayErrorMessage( this, _(
"You cannot delete the branch you are on." ) );
363 if( !outcome->unmerged )
365 DisplayErrorMessage( this, _(
"Could not compare this branch with the current version." ) );
371 if( *outcome->unmerged == 1 )
373 question = wxString::Format( _(
"Branch '%s' has one commit that is not in the current version."
374 "\n\nDelete it anyway?" ),
377 else if( *outcome->unmerged > 1 )
379 question = wxString::Format( _(
"Branch '%s' has %zu commits that are not in the current version."
380 "\n\nDelete it anyway?" ),
381 name, *outcome->unmerged );
385 question = wxString::Format( _(
"Delete branch '%s'?" ), name );
388 wxRichMessageDialog confirm(
this, question,
_(
"Delete Branch" ),
389 wxYES_NO | wxNO_DEFAULT | wxICON_WARNING | wxCENTER );
390 confirm.SetYesNoLabels(
_(
"&Delete" ),
_(
"Cancel" ) );
392 if( confirm.ShowModal() != wxID_YES || !
m_dialog->SessionStillCurrent() )
398 return aCommon.DeleteBranch( name );
400 wxString::Format(
_(
"Could not delete branch '%s'." ),
name ) );
413 if( !
m_dialog->SessionStillCurrent() )
421 bool hasRemote =
false;
422 bool detached =
false;
423 bool checked =
false;
426 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
427 auto outcome = std::make_shared<OUTCOME>();
440 outcome->checked =
true;
442 [
this, common, outcome]()
444 if( !
m_dialog->SessionStillCurrent() || !outcome->checked ||
m_push.IsBusy() )
447 if( outcome->detached )
450 _(
"This project is showing an old snapshot rather than a branch, so there "
451 "is no branch to push. Double click a branch to go back to it first." ) );
455 if( !outcome->hasRemote )
461 wxRichMessageDialog confirm(
463 wxString::Format(
_(
"Force push replaces '%s' with your local '%s'.\n\n"
464 "Commits that exist only on the remote will be lost." ),
465 outcome->upstream, outcome->branch ),
466 _(
"Force Push" ), wxYES_NO | wxNO_DEFAULT | wxICON_WARNING | wxCENTER );
467 confirm.SetYesNoLabels(
_(
"Force &Push" ),
_(
"Cancel" ) );
469 if( confirm.ShowModal() == wxID_YES &&
m_dialog->SessionStillCurrent() )
476 const wxString& aExpectedHeadOid )
483 bool authFailure =
false;
484 bool cancelled =
false;
487 auto outcome = std::make_shared<OUTCOME>();
489 m_dialog->SetPaneTransferBusy(
true,
_(
"Pushing..." ) );
491 bool started =
m_push.Start(
493 [aCommon, outcome, aExpectedBranch, aExpectedHeadOid, alive =
m_push.Alive(),
496 aCommon->ClearAuthFailure();
498 GIT_PUSH_HANDLER handler( aCommon.get() );
499 handler.SetProgressReporter( std::make_unique<KIGIT_STATUS_REPORTER>(
501 [dialog]( const wxString& aText )
503 dialog->ShowTransferProgress( aText );
505 handler.SetCancelToken( aToken );
506 handler.SetWaitForActionLock(
true );
507 handler.SetExpectedTarget( aExpectedBranch, aExpectedHeadOid );
508 outcome->result = handler.PerformPush(
true );
509 outcome->error = handler.GetErrorString();
510 outcome->authFailure = aCommon->WasAuthFailure();
514 outcome->cancelled = handler.IsThisOperationCancelled();
522 aCommon->NotifyRefreshListeners();
526 [
this, aCommon, outcome]()
528 if( !m_dialog->SessionStillCurrent() )
531 m_dialog->SetPaneTransferBusy(
false );
553 m_dialog->SetPaneTransferBusy(
false );
The base frame for deriving all KiCad main window classes.
The advanced Git window: a non-modal dialog with a top bar naming the repository and branch and a sid...
The project's git session.
std::shared_ptr< std::atomic< bool > > CANCEL_TOKEN
One token per operation.
wxString GetCurrentBranchName() const
wxString GetRemoteNameOrDefault() const
Returns GetRemotename() when non-empty, otherwise "origin".
bool CreateBranch(const wxString &aName, const wxString &aFrom=wxEmptyString)
wxString GetHeadCommitId() const
Full commit ID at HEAD, or empty for an unborn or missing HEAD.
bool HasPushAndPullRemote() const
bool IsHeadDetached() const
True when HEAD points at a commit rather than a branch, as after checking out a tag.
wxStaticText * m_branchAuthor
wxButton * m_newBranchButton
wxSplitterWindow * m_branchesSplitter
wxButton * m_deleteBranchButton
wxButton * m_forcePushButton
wxStaticText * m_branchSync
wxListCtrl * m_branchesList
wxTextCtrl * m_branchMessage
wxStaticText * m_branchCount
wxPanel * m_branchListPanel
PANEL_GIT_BRANCHES_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
wxStaticText * m_branchCommit
wxSearchCtrl * m_branchSearch
wxStaticText * m_branchTitle
void StopTransfer()
Ask a force push still running to stop. It finishes on its own.
PANEL_GIT_BRANCHES(wxWindow *aParent, GIT_ADVANCED_DIALOG *aDialog, EDA_BASE_FRAME *aFrame)
void fillBranchDetail(const KIGIT_BRANCH_DETAIL &aDetail)
int m_branchSashPos
Where the user left the sash between the list and its detail pane.
KIGIT_QUERY m_listQuery
List and detail reads share the session lock without waiting on the UI thread.
void showBranchDetail(const wxString &aBranch)
Fill the detail pane with aBranch, or hide it when the name is empty or gone.
GIT_ADVANCED_DIALOG * m_dialog
KIGIT_QUERY m_detailQuery
void reloadBranches()
Refill the list after the search text changed.
void onForcePush()
Force push the current branch, after an explicit confirmation.
void Populate()
Fill the list from the repository's branches. The repository is read on a worker.
std::vector< bool > m_branchIsRemote
Whether each listed row is a remote-tracking branch.
void startForcePush(std::shared_ptr< KIGIT_COMMON > aCommon, const wxString &aExpectedBranch, const wxString &aExpectedHeadOid)
KIGIT_TRANSFER m_push
The force push this page started.
~PANEL_GIT_BRANCHES() override
KIGIT_QUERY m_actionQuery
Reads needed before an action's confirmation, separate from list refreshes.
bool m_fillingBranches
True while the list is being refilled, when its selection events are its own.
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
This file is part of the common library.
Base window classes and related definitions.
@ Busy
Another git operation held the action lock, so nothing was pushed.
wxString BranchNameError(const wxString &aName)
Empty when aName is a branch name git would accept, otherwise why it would not, in the words to show ...
void UnsplitKeepingSash(wxSplitterWindow *aSplitter, wxWindow *aPane, int &aSashPos)
Close a splitter's second pane, keeping the sash where the user left it for next time.
bool PromptForCredentials(wxWindow *aParent, KIGIT_COMMON *aCommon, PROJECT *aProject=nullptr)
Ask the user for the credentials of the session's remote and apply them to the session.
void ShowBusyMessage(wxWindow *aParent)
Show a busy message for a user action. UI thread only.
wxString SelectedText(wxListCtrl *aList, int aColumn)
The text of the selected row in a report list, or empty when nothing is selected.
wxString RelativeDate(const wxDateTime &aWhen)
"just now", "3 hours ago", or the ISO date once it is more than a month old.
void CallOnUi(std::function< void()> aFn)
Run aFn on the UI thread.
void MarkDestructive(wxButton *aButton)
Paint a button's text the diff's red, for one that throws something away.
size_t behind
Commits only in the second reference.
size_t ahead
Commits only in the first reference.
std::optional< KIGIT_AHEAD_BEHIND > divergence
Comparison with upstream, if it could be read.
wxString upstream
The branch this one tracks, or empty.
wxString result
Test unit parsing edge cases and error handling.