34#include <wx/dataview.h>
37#include <wx/filename.h>
40#include <wx/settings.h>
42#include <wx/statbox.h>
43#include <wx/stattext.h>
44#include <wx/textctrl.h>
45#include <wx/weakref.h>
47#include <wx/choicdlg.h>
81 ID_OPEN_ADVANCED = wxID_HIGHEST + 1,
91 std::pair<wxString, wxString> state;
92 git_reference* head =
nullptr;
94 if( git_reference_lookup( &head, aRepo,
"HEAD" ) == GIT_OK )
96 if(
const char* target = git_reference_symbolic_target( head ) )
97 state.first = wxString::FromUTF8( target );
99 git_reference_free( head );
104 if( git_reference_name_to_id( &oid, aRepo,
"HEAD" ) == GIT_OK )
105 state.second = wxString::FromUTF8( git_oid_tostr_s( &oid ) );
114 if( aExpectedHead.second.IsEmpty() || aExpectedHead !=
commitHead( aCommon.
GetRepo() ) )
115 return _(
"The branch or its last commit changed. Select the commit to amend again." );
118 return _(
"Switch to a branch before amending its last commit." );
121 return _(
"Finish or abandon the current Git operation before amending a commit." );
124 return _(
"Only an unpushed commit can be amended." );
126 return wxEmptyString;
141 aError = wxString::Format(
_(
"Could not start branch '%s'." ), aName );
151 aError = wxString::Format(
_(
"Could not create branch '%s'." ), aName );
158 aError = wxString::Format(
_(
"Could not switch to the new branch '%s'." ), aName );
174 if( aPrevious.IsEmpty() )
185static bool reserveRecoveryFile(
const wxString& aPreferredPath, wxString& aPathOut, wxString& aCreatedDir )
190 wxFileName parent( aPreferredPath );
192 if( !parent.DirExists() )
194 if( !parent.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
197 aCreatedDir = parent.GetPath();
200 for(
int attempt = 0; attempt < 100; attempt++ )
203 attempt == 0 ? aPreferredPath : wxString::Format( wxS(
"%s.%d" ), aPreferredPath, attempt );
208 if( out.Create( candidate,
false ) )
210 aPathOut = candidate;
216 if( !wxFileName::FileExists( candidate ) )
241 m_summary->SetHint(
_(
"Summarize your changes..." ) );
251 if( !
m_description->SetHint(
_(
"Extended Description (optional)" ) ) )
253 m_descriptionHint =
new wxStaticText(
this, wxID_ANY,
_(
"Extended Description (optional)" ) );
254 m_descriptionHint->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
256 [
this]( wxMouseEvent& )
268 auto commitOnEnter = [
this]( wxKeyEvent& aEvent )
270 bool enter = aEvent.GetKeyCode() == WXK_RETURN || aEvent.GetKeyCode() == WXK_NUMPAD_ENTER;
272 if( enter && ( aEvent.CmdDown() || aEvent.ControlDown() ) )
274 if( aEvent.ShiftDown() )
292 m_summary->Bind( wxEVT_CHAR_HOOK, commitOnEnter );
301 [
this]( wxCommandEvent& )
308 m_showDiffButton->SetToolTip(
_(
"Show what changed in the selected file. Pick a file in the list first." ) );
310 [
this]( wxCommandEvent& )
318 m_checklist->SetToolTip(
_(
"Double click a file to see what changed in it. Right click for more." ) );
321 m_checklist->Bind( wxEVT_DATAVIEW_SELECTION_CHANGED,
322 [
this]( wxDataViewEvent& )
351 wxSizer* column = GetSizer();
356 column->SetMinSize( FromDIP( wxSize( 280, -1 ) ) );
358 wxStaticText* heading =
new wxStaticText(
this, wxID_ANY,
_(
"Commit Message" ) );
359 heading->SetFont( heading->GetFont().Bold() );
360 column->Insert( 0, heading, 0, wxLEFT | wxRIGHT | wxTOP, 8 );
363 wxStaticBoxSizer* summary =
new wxStaticBoxSizer( wxVERTICAL,
this,
_(
"Summary" ) );
364 wxWindow* box = summary->GetStaticBox();
366 m_summaryFiles =
new wxStaticText( box, wxID_ANY, wxEmptyString );
367 m_summaryAdded =
new wxStaticText( box, wxID_ANY, wxEmptyString );
374 wxBoxSizer* lines =
new wxBoxSizer( wxHORIZONTAL );
378 summary->Add( lines, 0, wxALL, 4 );
381 size_t afterOptions = column->GetItemCount();
383 for(
size_t ii = 0; ii < column->GetItemCount(); ++ii )
386 afterOptions = ii + 1;
389 column->Insert( afterOptions, summary, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 8 );
391 wxBoxSizer* files =
new wxBoxSizer( wxVERTICAL );
392 files->Add(
m_changesRow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 8 );
396 wxBoxSizer* row =
new wxBoxSizer( wxHORIZONTAL );
397 row->Add( files, 1, wxEXPAND );
398 row->Add( column, 0, wxEXPAND );
401 SetSizer( row,
false );
423 common->RemoveRefreshListener(
this );
429 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
436 previous->RemoveRefreshListener(
this );
449 setBusy(
false, wxEmptyString );
461 common->AddRefreshListener(
this,
493 if( !
Pgm().GetCommonSettings()->m_Git.enableGit )
500 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
501 wxString projectPath =
m_frame->Prj().GetProjectPath();
504 auto status = std::make_shared<STATUS>();
505 status->attachError = common->GetAttachError();
509 [status, projectPath, dark, countLines](
KIGIT_COMMON& aCommon )
511 status->hasRepo =
true;
526 std::set<wxString> changed;
529 changed.insert( entry.
path );
534 [
this, common, status]()
536 if(
m_frame->Prj().GetGitCommon() == common && !common->IsShuttingDown()
537 &&
Pgm().GetCommonSettings()->m_Git.enableGit )
547 std::vector<FILE_CHECKLIST_ENTRY> entries = aStatus.
entries;
569 std::set<wxString> stillChecked;
572 stillChecked.insert(
path );
576 if( (
m_shownPaths.count( entry.path ) && !stillChecked.count( entry.path ) )
578 entry.checked =
false;
589 bool hasChanges = !entries.empty();
600 setStatusText( wxString::Format(
_(
"This project is in the middle of a %s that was started outside "
601 "KiCad or interrupted. Abandon it to go back to the last commit, "
602 "or finish it with the program that started it." ),
608 setStatusText(
_(
"This project is showing an old snapshot rather than a branch, so it cannot be "
609 "committed or pushed from here. In the Advanced Git window, choose Branches "
610 "and double click a branch to go back to it." ) );
613 else if( hasChanges )
619 if( !
Pgm().GetCommonSettings()->m_Git.enableGit )
620 setStatusText(
_(
"Version control is turned off in Preferences." ) );
624 setStatusText(
_(
"This project is not under version control. To set it up, right click "
625 "the project in the project manager and choose Version Control." ) );
627 setStatusText(
_(
"The tracked branch has changes this project does not. Get Changes brings them in." ) );
629 setStatusText(
_(
"Committed changes are waiting to be pushed." ) );
645 dialog->ReloadPages();
656 setBusy(
false, wxEmptyString );
680 setStatusText(
_(
"Version control is turned off in Preferences." ) );
694 m_emptyLabel->Wrap( std::max( 40, GetClientSize().GetWidth() - 16 ) );
708 || !IsShownOnScreen() )
734 menu.Append( ID_OPEN_ADVANCED,
_(
"Open Advanced Git Window..." ) );
735 menu.Bind( wxEVT_MENU,
736 [
this]( wxCommandEvent& )
745 menu.Append( ID_SET_AUTHOR,
_(
"Set Author..." ) );
746 menu.Bind( wxEVT_MENU,
747 [
this]( wxCommandEvent& )
759 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
760 auto identity = std::make_shared<KIGIT::GIT_IDENTITY>();
768 [
this, common, identity]()
772 _(
"Reading author..." ) );
802 default:
return _(
"git operation" );
815 default:
return _(
"Abandon Operation" );
847 m_pullButton->SetToolTip(
_(
"The tracked branch has commits this project does not have." ) );
851 bool hasCheckedChanges = !
m_checklist->GetCheckedPaths().empty();
854 bool hasMessage = !
m_summary->GetValue().Trim().IsEmpty();
855 bool hasBranchName = !newBranch || !
m_newBranchName->GetValue().Trim().IsEmpty();
861 wxString mainLabel =
_(
"Commit" );
862 wxString menuLabel =
_(
"Commit and Push" );
863 bool mainEnabled =
false;
864 bool menuEnabled =
false;
874 mainLabel =
_(
"Amend" );
875 menuLabel =
_(
"Amend and Push" );
880 blocked =
_(
"This commit can no longer be amended. Untick Amend to start a new commit." );
881 else if( !hasMessage )
882 blocked =
_(
"Write a summary to amend the last commit." );
887 mainEnabled = hasMessage && hasBranchName;
888 menuEnabled = hasMessage && hasBranchName;
891 blocked =
_(
"Write a summary to commit." );
892 else if( !hasBranchName )
893 blocked =
_(
"Name the new branch, or untick Create a new branch." );
897 mainLabel =
_(
"Push" );
903 blocked =
_(
"Tick a file to commit it." );
916 reason =
_(
"The open editors have unsaved changes. Committing offers to save them first." );
922 if( !reason.IsEmpty() )
923 reason += wxS(
"\n" );
925 reason +=
_(
"This project has no remote, so there is nothing to get changes from or push to. "
926 "Add one from the Remotes page of the Advanced Git window." );
929 if( !blocked.IsEmpty() )
931 if( !reason.IsEmpty() )
932 reason += wxS(
"\n" );
937 if( !reason.IsEmpty() )
941 bool showReason = !reason.IsEmpty() ||
m_shownPaths.empty();
957 int left = 72 -
static_cast<int>(
m_summary->GetValue().length() );
981 wxString description;
1006 if( !
path.IsEmpty() )
1030 if( shown.IsEmpty() )
1048 wxString line =
message.BeforeFirst(
'\n' );
1050 if( line.length() > 40 )
1051 line = line.Left( 37 ) + wxS(
"..." );
1064 wxString summary, description;
1083 wxPoint at =
m_description->GetPosition() + wxPoint( FromDIP( 6 ), FromDIP( 4 ) );
1110 int staged =
static_cast<int>(
m_checklist->GetCheckedPaths().size() );
1111 int unstaged =
static_cast<int>(
m_shownPaths.size() ) - staged;
1113 m_changesLabel->SetLabel( wxString::Format(
_(
"Changes to Commit (%d of %d)" ), staged,
1119 wxString stagedText = staged == 1 ?
_(
"1 file will be committed" )
1120 : wxString::Format(
_(
"%d files will be committed" ), staged );
1121 wxString unstagedText = unstaged == 1 ?
_(
"1 left out" )
1122 : wxString::Format(
_(
"%d left out" ), unstaged );
1124 m_summaryFiles->SetLabel( wxString::Format( wxS(
"%s, %s" ), stagedText, unstagedText ) );
1140 if(
path.IsEmpty() )
1144 menu.Append( 1,
_(
"Show History for This File" ) );
1145 menu.AppendSeparator();
1146 menu.Append( 2,
_(
"Discard Changes" ) );
1148 switch( GetPopupMenuSelectionFromUser( menu ) )
1153 dialog->ShowHistoryForFile(
path );
1173 bool tracked =
false;
1174 bool conflicted =
false;
1178 auto state = std::make_shared<FILE_STATE>();
1188 [
this, aCommon, aPath, state]()
1190 confirmDiscardFile( aCommon, aPath, state->tracked, state->conflicted, state->root );
1192 _(
"Checking changes..." ) );
1197 bool aConflicted, wxString aRoot )
1202 DisplayInfoMessage(
this,
_(
"An editor has unsaved changes. Save or revert them before discarding "
1203 "files, so the discard is not undone by the next save." ) );
1207 wxString
name = wxFileName( aPath ).GetFullName();
1214 question = wxString::Format(
_(
"Resolve the conflict in '%s' by going back to the version this "
1215 "project was on?\n\n"
1216 "The version that arrived is dropped, and the file no longer needs "
1222 question = wxString::Format(
_(
"Throw away the changes to '%s'?\n\n"
1223 "The file goes back to the last committed version." ),
1228 question = wxString::Format(
_(
"Move '%s' to the trash?\n\n"
1229 "It has never been committed, so there is no version to bring back." ),
1233 wxMessageDialog dlg(
this, question,
_(
"Discard Changes" ), wxYES_NO | wxNO_DEFAULT | wxICON_WARNING );
1234 dlg.SetYesNoLabels( aConflicted ?
_(
"Resolve" )
1235 : aTracked ?
_(
"Discard" )
1236 :
_(
"Move to Trash" ),
1239 if( dlg.ShowModal() != wxID_YES )
1242 if( aCommon !=
m_frame->Prj().GetGitCommon() || aCommon->IsShuttingDown() )
1255 if( !lock.owns_lock() )
1259 if( !aCommon->GetRepo() )
1264 if( !aCommon->DiscardChanges( { aPath }, error ) )
1276 if( !aRoot.EndsWith( wxS(
"/" ) ) )
1277 aRoot += wxS(
"/" );
1279 wxFileName fullPath( aRoot + aPath );
1286 aCommon->NotifyRefreshListeners();
1288 wxString::Format(
_(
"Could not move '%s' to the trash. The file is "
1289 "still on disk, so discard it again to retry." ),
1297 aCommon->NotifyRefreshListeners();
1305 if( !
path.IsEmpty() )
1326 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
1328 if( !common->GetRepo() )
1335 setBusy(
false,
_(
"Cancelled." ) );
1347 m_frame->SetStatusText(
_(
"Cancelling. A stalled connection stops only when it times out." ) );
1372 DisplayInfoMessage(
this, wxString::Format(
_(
"Finish or abandon the %s in progress first." ),
1380 DisplayInfoMessage(
this,
_(
"This project is showing an old snapshot rather than a branch, so it cannot be "
1381 "committed or pushed from here. In the Advanced Git window, choose Branches "
1382 "and double click a branch to go back to it." ) );
1394 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
1413 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
1438 std::vector<wxString> outside;
1442 auto preparation = std::make_shared<PREPARATION>();
1443 const wxString projectPath =
m_frame->Prj().GetProjectPath();
1452 preparation->ready =
true;
1454 [
this, aCommon, preparation]()
1456 if( preparation->ready )
1459 _(
"Checking working files..." ) );
1464 const std::vector<wxString>& aOutside )
1468 auto canAbandon = [
this, aCommon]()
1470 if( aCommon !=
m_frame->Prj().GetGitCommon() || aCommon->IsShuttingDown()
1471 || !
Pgm().GetCommonSettings()->m_Git.enableGit )
1478 DisplayInfoMessage(
this,
_(
"The open editors have unsaved changes. Save or revert them first, so it "
1479 "is clear what is being abandoned." ) );
1489 wxString question = wxString::Format(
_(
"Abandon the %s and go back to the last commit?\n\n"
1490 "Changes made to the files since that commit are discarded." ),
1493 if( !aOutside.empty() )
1497 for(
const wxString&
path : aOutside )
1498 listed += wxS(
"\n " ) +
path;
1500 question += wxString::Format(
_(
"\n\nThis also discards uncommitted changes elsewhere in this "
1505 if( !
IsOK(
this, question ) || !canAbandon() )
1515 if( !lock.owns_lock() )
1518 ok = aCommon->AbandonOperation( error );
1528 aCommon->NotifyRefreshListeners();
1529 setBusy(
false,
_(
"Back at the last commit." ) );
1552 std::pair<wxString, wxString> head;
1557 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
1558 auto info = std::make_shared<AMEND_INFO>();
1567 if( !aExpectedCommit.IsEmpty() && aExpectedCommit !=
info->head.second )
1568 info->refusal =
_(
"The selected commit is no longer the last commit on this branch." );
1570 if(
info->refusal.IsEmpty() )
1572 wxString summary, description;
1577 [
this, common,
info, aExpectedCommit]()
1579 if( !
info->refusal.IsEmpty() )
1589 bool fromHistory = !aExpectedCommit.IsEmpty();
1591 wxWeakRef<GIT_COMMIT_PANE> pane(
this );
1592 wxWeakRef<EDA_BASE_FRAME> frame(
m_frame );
1594 if( fromHistory && !draft.IsEmpty() && draft !=
info->message
1595 && !
IsOK(
this,
_(
"Replace the current commit message with the selected commit's message?" ) ) )
1600 if( !pane || pane->IsBeingDeleted() || !frame || frame->IsBeingDeleted()
1601 || common != frame->Prj().GetGitCommon() || common->IsShuttingDown()
1602 || !
Pgm().GetCommonSettings()->m_Git.enableGit
1603 || frame->Prj().GetLocalSettings().m_GitIntegrationDisabled )
1621 if( fromHistory || draft.IsEmpty() )
1634 _(
"Reading last commit..." ) );
1652 std::pair<wxString, wxString>
head;
1666 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
1668 if( !common->GetRepo() )
1674 branchName.Trim(
true ).Trim(
false );
1686 std::vector<wxString> files =
m_checklist->GetCheckedPaths();
1699 auto request = std::make_shared<COMMIT_REQUEST>();
1700 request->amend = amend;
1702 request->newBranch = newBranch;
1703 request->andPush = aAndPush;
1705 request->branchName = branchName;
1707 request->summary =
m_summary->GetValue();
1709 request->files = std::move( files );
1715 if( request->amend )
1716 request->amendRefusal =
amendRefusal( aCommon, request->head );
1723 [
this, common, request]()
1727 _(
"Checking changes..." ) );
1733 if( !aRequest->amendRefusal.IsEmpty() )
1736 aCommon->NotifyRefreshListeners();
1740 if( !aRequest->displayedBranch.IsEmpty()
1741 && aRequest->head.first != wxS(
"refs/heads/" ) + aRequest->displayedBranch )
1744 "Review the changes and commit again." ) );
1745 aCommon->NotifyRefreshListeners();
1749 if( !aRequest->lfsMessage.IsEmpty() )
1760 if( aCommon !=
m_frame->Prj().GetGitCommon() || aCommon->IsShuttingDown() )
1766 aCommon->NotifyRefreshListeners();
1776 const std::vector<wxString>& files = aRequest.
files;
1777 const bool amend = aRequest.
amend;
1778 const bool newBranch = aRequest.
newBranch;
1779 const wxString& branchName = aRequest.
branchName;
1783 if( files.empty() && !amend )
1785 setBusy(
false,
_(
"Nothing was saved, so there is nothing to commit." ) );
1793 if( common !=
m_frame->Prj().GetGitCommon() || common->IsShuttingDown() )
1798 wxString branchError;
1799 wxString branchDetail;
1800 wxString previousBranch;
1806 if( !lock.owns_lock() )
1812 DisplayInfoMessage(
this,
_(
"The branch or its last commit changed while preparing the commit. "
1813 "Review the changes and commit again." ) );
1814 common->NotifyRefreshListeners();
1822 if( !refusal.IsEmpty() )
1826 common->NotifyRefreshListeners();
1842 previousBranch = common->GetCurrentBranchName();
1844 if( !newBranch ||
moveToNewBranch( *common, branchName, branchError, branchDetail ) )
1858 if( newBranch && !committed )
1863 if( !branchError.IsEmpty() )
1876 DisplayErrorMessage(
this, wxString::Format(
_(
"Could not create the commit: %s" ), error ) );
1890 common->NotifyRefreshListeners();
1895 setBusy(
false,
_(
"Committed." ) );
1900 std::function<
void()> aDone,
const wxString& aStatus )
1910 auto read = std::make_shared<bool>(
false );
1914 [read, work = std::move( aWork )](
KIGIT_COMMON& aSession )
1919 [
this, aCommon, read, done = std::move( aDone )]()
1922 setBusy(
false, wxEmptyString );
1924 if( *read && aCommon ==
m_frame->Prj().GetGitCommon() && !aCommon->IsShuttingDown()
1925 && aCommon->GetRepo() &&
Pgm().GetCommonSettings()->m_Git.enableGit )
1944 m_frame->SetStatusText( aText );
1959 m_frame->SetStatusText( aStatus );
1987 bool hasRemote =
false;
1992 auto target = std::make_shared<TARGET>();
2002 [
this, aCommon, target]()
2004 if( !target->hasRemote )
2006 setBusy(
false,
_(
"Committed. No remote is configured to push to." ) );
2007 aCommon->NotifyRefreshListeners();
2011 startPush( aCommon, target->branch, target->head );
2013 _(
"Checking remote..." ) );
2019 setBusy(
true,
_(
"Pushing..." ) );
2026 bool authFailure =
false;
2027 bool cancelled =
false;
2030 auto outcome = std::make_shared<OUTCOME>();
2034 [aCommon, outcome, aBranch, aHead, alive =
m_transfer.Alive(),
2037 aCommon->ClearAuthFailure();
2039 GIT_PUSH_HANDLER handler( aCommon.get() );
2040 handler.SetProgressReporter( std::make_unique<KIGIT_STATUS_REPORTER>(
2042 [pane]( const wxString& aText )
2044 pane->ShowTransferProgress( aText );
2046 handler.SetWaitForActionLock(
true );
2047 handler.SetExpectedTarget( aBranch, aHead );
2048 handler.SetCancelToken( aToken );
2049 outcome->result = handler.PerformPush();
2050 outcome->error = handler.GetErrorString();
2051 outcome->authFailure = aCommon->WasAuthFailure();
2055 outcome->cancelled = handler.IsThisOperationCancelled();
2063 aCommon->NotifyRefreshListeners();
2067 [
this, aCommon, outcome]()
2069 onPushDone( aCommon,
static_cast<int>( outcome->result ), outcome->error, outcome->authFailure,
2070 outcome->cancelled );
2074 setBusy(
false,
_(
"Could not start the push." ) );
2079 bool aAuthFailure,
bool aCancelled )
2086 setBusy(
false,
_(
"Pushed to remote." ) );
2092 setBusy(
false, wxEmptyString );
2099 setBusy(
false,
_(
"Cancelled." ) );
2108 setBusy(
false,
_(
"Cancelled." ) );
2119 auto worktree = std::make_shared<GIT_WORKTREE_STATUS>();
2120 wxString projectPath =
m_frame->Prj().GetProjectPath();
2128 [
this, aCommon, worktree]()
2133 if(
IsOK(
this,
_(
"Someone else pushed changes first.\n\n"
2134 "Get their changes and push again?" ) )
2135 && aCommon ==
m_frame->Prj().GetGitCommon() && !aCommon->IsShuttingDown()
2136 &&
Pgm().GetCommonSettings()->m_Git.enableGit )
2141 _(
"Checking working files..." ) );
2146 setBusy(
false,
_(
"Push failed." ) );
2169 auto preparation = std::make_shared<MERGE_PREPARATION>();
2175 preparation->workspace = std::make_unique<GIT_DIFF_WORKSPACE>();
2177 if( !preparation->workspace->IsOk() )
2184 preparation->workspace->Root() + wxFileName::GetPathSeparator() + wxS(
"stages" ),
2185 preparation->headOid, preparation->theirsOid, preparation->conflicts, preparation->error );
2187 [
this, aCommon, aThenPush, preparation]()
2191 _(
"Preparing the merge..." ) );
2196 std::shared_ptr<MERGE_PREPARATION> aPreparation )
2198 if( !aPreparation->workspace || !aPreparation->workspace->IsOk() )
2204 if( !aPreparation->prepared )
2212 auto canResolve = [
this, aCommon]()
2214 if( aCommon !=
m_frame->Prj().GetGitCommon() || aCommon->IsShuttingDown()
2215 || !
Pgm().GetCommonSettings()->m_Git.enableGit )
2222 DisplayInfoMessage(
this,
_(
"The open editors have unsaved changes. Save or revert them before "
2223 "resolving the merge." ) );
2236 if( aPreparation->rebaseConfigured
2237 && !
IsOK(
this,
_(
"This project is set to rebase when getting changes, but a conflict can only be "
2238 "settled here by merging. Carry on and make a merge commit, or cancel and rebase "
2239 "from a git command line?" ) ) )
2241 setBusy(
false,
_(
"Getting changes was cancelled. Your files are unchanged." ) );
2252 setBusy(
false,
_(
"Getting changes was cancelled. Your files are unchanged." ) );
2260 const wxString sep = wxFileName::GetPathSeparator();
2261 const wxString& headOid = aPreparation->headOid;
2262 const wxString& theirsOid = aPreparation->theirsOid;
2263 const std::vector<KIGIT_CONFLICT_SIDES>& conflicts = aPreparation->conflicts;
2266 std::vector<std::pair<wxString, wxString>> resolved;
2273 wxString relativeDest;
2276 std::vector<LOSING_COPY> losingCopies;
2281 for(
size_t ii = 0; ii < conflicts.size(); )
2294 wxArrayString choices;
2297 choices.Add(
_(
"Open the merge tool" ) );
2300 choices.Add( conflict.
hasOurs ?
_(
"Keep my version" )
2302 :
_(
"Leave it out" ) );
2303 choices.Add( conflict.
hasTheirs ?
_(
"Use the remote version" )
2304 : conflict.
hasAncestor ?
_(
"Delete it, as on the remote" )
2305 :
_(
"Leave it out, as on the remote" ) );
2311 situation =
_(
"'%s' exists only on the remote (%zu of %zu).\n\n"
2312 "How should it be resolved?" );
2316 situation =
_(
"'%s' was deleted here but changed on the remote (%zu of %zu).\n\n"
2317 "How should it be resolved?" );
2321 situation =
_(
"'%s' exists only here (%zu of %zu).\n\n"
2322 "How should it be resolved?" );
2326 situation =
_(
"'%s' was changed here but deleted on the remote (%zu of %zu).\n\n"
2327 "How should it be resolved?" );
2331 situation =
_(
"'%s' was changed both here and on the remote (%zu of %zu).\n\n"
2332 "How should it be resolved?" );
2335 int sel = wxGetSingleChoiceIndex( wxString::Format( situation, conflict.
path, ii + 1, conflicts.size() ),
2336 _(
"Resolve Conflict" ), choices,
this );
2340 wxString warning = handMerged == 1
2341 ?
_(
"Stop resolving? The file you merged by hand is discarded "
2343 : wxString::Format(
_(
"Stop resolving? The %d files you merged by "
2344 "hand are discarded with the rest." ),
2347 if( handMerged > 0 && !
IsOK(
this, warning ) )
2351 setBusy(
false,
_(
"Getting changes was cancelled. Your files are unchanged." ) );
2355 int action = mergeable ? sel : sel + 1;
2359 wxString mergedOut = workspace.
Root() + sep + wxString::Format( wxS(
"m%zu_" ), ii )
2360 + wxFileName( conflict.
path ).GetFullName();
2377 resolved.emplace_back( conflict.
path, mergedOut );
2382 losingCopies.push_back( { conflict.
oursFile, conflict.
path + wxS(
".mine" ) } );
2385 losingCopies.push_back( { conflict.
theirsFile, conflict.
path + wxS(
".theirs" ) } );
2387 else if( action == 1 )
2390 resolved.emplace_back( conflict.
path, conflict.
hasOurs ? conflict.
oursFile : wxString() );
2393 losingCopies.push_back( { conflict.
theirsFile, conflict.
path + wxS(
".theirs" ) } );
2400 losingCopies.push_back( { conflict.
oursFile, conflict.
path + wxS(
".mine" ) } );
2411 wxString rootDir = aPreparation->rootDir;
2413 if( !rootDir.EndsWith( wxS(
"/" ) ) )
2414 rootDir += wxS(
"/" );
2416 std::vector<wxString> savedCopies;
2417 std::vector<wxString> createdDirs;
2421 auto discardSavedCopies = [&savedCopies, &createdDirs]()
2423 for(
const wxString&
path : savedCopies )
2424 wxRemoveFile(
path );
2427 for(
auto dir = createdDirs.rbegin(); dir != createdDirs.rend(); ++dir )
2428 wxFileName::Rmdir( *dir );
2430 savedCopies.clear();
2431 createdDirs.clear();
2434 for(
const LOSING_COPY&
copy : losingCopies )
2437 wxString createdDir;
2441 if( !createdDir.IsEmpty() )
2442 createdDirs.push_back( createdDir );
2444 if( !reserved || !wxCopyFile(
copy.source, saved,
true ) )
2446 if( !saved.IsEmpty() )
2447 wxRemoveFile( saved );
2449 discardSavedCopies();
2450 setBusy(
false,
_(
"Getting changes was cancelled. Your files are unchanged." ) );
2451 DisplayErrorMessage(
this, wxString::Format(
_(
"Could not save the version you are replacing "
2452 "as '%s', so the merge was stopped." ),
2453 copy.relativeDest ) );
2457 savedCopies.push_back( saved );
2468 discardSavedCopies();
2472 std::unique_lock<std::mutex> lock = aCommon->TryLockAction();
2474 if( lock.owns_lock() )
2476 result = aCommon->CreateMergedCommit( headOid, theirsOid, resolved, error );
2480 if( !
IsOK(
this,
_(
"Another git operation is in progress. Try again?" ) ) )
2482 discardSavedCopies();
2483 setBusy(
false,
_(
"Getting changes was cancelled. Your files are unchanged." ) );
2491 discardSavedCopies();
2498 aCommon->NotifyRefreshListeners();
2510 setBusy(
false,
_(
"Changes merged." ) );
2518 if( !aCommon->GetRepo() )
2520 setBusy(
false, wxEmptyString );
2530 auto result = std::make_shared<CHECK>();
2531 wxString projectPath =
m_frame->Prj().GetProjectPath();
2540 [
this, aCommon, aThenPush,
result]()
2550 if( aCommon !=
m_frame->Prj().GetGitCommon() || aCommon->IsShuttingDown() )
2559 _(
"Checking changes..." ) );
2565 setBusy(
true,
_(
"Getting changes..." ) );
2581 bool authFailure =
false;
2582 bool cancelled =
false;
2585 auto outcome = std::make_shared<OUTCOME>();
2589 [aCommon, docsBefore, kiway, outcome, alive =
m_transfer.Alive(),
2592 aCommon->ClearAuthFailure();
2594 GIT_PULL_HANDLER handler( aCommon.get() );
2595 handler.SetProgressReporter( std::make_unique<KIGIT_STATUS_REPORTER>(
2597 [pane]( const wxString& aText )
2599 pane->ShowTransferProgress( aText );
2601 handler.SetWaitForActionLock(
true );
2602 handler.SetCancelToken( aToken );
2603 outcome->result = handler.PerformPull();
2604 outcome->error = handler.GetErrorStrings().empty() ? wxString() : handler.GetErrorString();
2605 outcome->authFailure = aCommon->WasAuthFailure();
2609 outcome->cancelled = handler.IsThisOperationCancelled();
2614 [aCommon, docsBefore,
result, kiway]()
2622 aCommon->NotifyRefreshListeners();
2625 [
this, aCommon, aThenPush, outcome]()
2627 onPullDone( aCommon,
static_cast<int>( outcome->result ), outcome->error, outcome->authFailure,
2628 aThenPush, outcome->cancelled );
2632 setBusy(
false,
_(
"Could not start getting changes." ) );
2637 bool aAuthFailure,
bool aThenPush,
bool aCancelled )
2646 aCommon->NotifyRefreshListeners();
2662 setBusy(
false,
_(
"Cancelled." ) );
2671 setBusy(
false,
_(
"Cancelled." ) );
2688 setBusy(
false,
_(
"Commit your changes first." ) );
2689 aCommon->NotifyRefreshListeners();
2692 if( !aError.IsEmpty() )
2698 DisplayInfoMessage(
this,
_(
"There are changes that are not committed yet, and getting changes "
2699 "would run over them. Commit them first, then get changes again. To "
2700 "set them aside instead, use Stash Changes in the Advanced Git "
2709 setBusy(
false,
_(
"Could not get changes." ) );
2710 aCommon->NotifyRefreshListeners();
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
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...
static void ShowHistoryForFile(EDA_BASE_FRAME *aParent, GIT_COMMIT_PANE *aPane, const wxString &aPath)
Open the window on its History page, narrowed to the commits that changed aPath.
static void CloseForPane(GIT_COMMIT_PANE *aPane)
Close the live window if the given pane opened it, for when that pane goes away.
static void CloseActive()
Close the live window, for when the project it was filled from goes away.
static GIT_ADVANCED_DIALOG * Active()
The live window, or null when none is open.
static void ShowForFrame(EDA_BASE_FRAME *aParent, GIT_COMMIT_PANE *aPane)
Raise the one window for this session, creating it on first use.
BranchResult SwitchToBranch(const wxString &aBranchName)
Switch to the specified branch.
CommitResult PerformCommit(const std::vector< wxString > &aFiles, const wxString &aMessage, const wxString &aAuthorName, const wxString &aAuthorEmail)
wxString GetErrorString() const
CommitResult PerformAmend(const std::vector< wxString > &aFiles, const wxString &aMessage, const wxString &aAuthorName, const wxString &aAuthorEmail)
wxCheckBox * m_signOffCheckbox
wxTextCtrl * m_newBranchName
wxStaticBoxSizer * m_optionsSizer
wxStaticText * m_summaryCount
STD_BITMAP_BUTTON * m_settingsButton
wxButton * m_showDiffButton
wxCheckBox * m_amendCheckbox
wxStaticText * m_branchName
wxBoxSizer * m_changesRow
FILE_CHECKLIST_CTRL * m_checklist
wxTextCtrl * m_description
wxStaticText * m_changesLabel
GIT_COMMIT_PANE_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(500, 360), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
wxStaticText * m_emptyLabel
SPLIT_BUTTON * m_commitButton
wxCheckBox * m_newBranchCheckbox
wxChoice * m_recentMessages
wxString abandonLabel() const
The label the network button carries while an operation is in progress.
void submitPull(std::shared_ptr< KIGIT_COMMON > aCommon, bool aThenPush)
aThenPush pushes after a successful pull, which belongs only to the rejected-push recovery flow.
void updateCommitButton()
Set what the commit buttons say, and whether they are enabled, for the current state.
void onPull(wxCommandEvent &aEvent)
void onMessageChanged(wxCommandEvent &aEvent)
void resolvePullConflicts(std::shared_ptr< KIGIT_COMMON > aCommon, bool aThenPush)
Resolve a conflicted pull file by file, using the visual merge tool for design files,...
void ShowTransferProgress(const wxString &aText)
Show a line of transfer progress. Called from the UI thread by a KIGIT_STATUS_REPORTER.
void SetTransferBusy(bool aBusy, const wxString &aStatus=wxEmptyString)
Put the panel into the state it uses while a transfer runs, for one started elsewhere.
void Pull()
Get changes from the remote, as the Get Changes button does.
wxString GetCommitMessage() const
The message as typed. A second form on the same project shows the same text.
void onNewBranchToggled(wxCommandEvent &aEvent)
void abandonOperation(std::shared_ptr< KIGIT_COMMON > aCommon)
Drop an in-progress merge or rebase and return the project to its last commit.
void BeginAmend(const wxString &aExpectedCommit=wxEmptyString)
Prepare the amend form. A history entry supplies its hash and starts with no files selected.
void finishCommit(std::shared_ptr< KIGIT_COMMON > aCommon, const COMMIT_REQUEST &aRequest)
bool m_inAdvancedWindow
True on the advanced window's Changes page, false in the docked panel.
std::function< void(const wxString &)> m_fileSelectedHandler
void setDescription(const wxString &aText)
bool m_hasUnpushedCommits
void startPush(std::shared_ptr< KIGIT_COMMON > aCommon, const wxString &aBranch, const wxString &aHead)
void discardFile(std::shared_ptr< KIGIT_COMMON > aCommon, const wxString &aPath)
KIGIT_QUERY m_refresh
Coalesce refreshes while a repository read is running, and discard stale results.
std::pair< int, int > m_lineStats
Lines the working tree adds and removes against HEAD, read at the last refresh.
void onSettings(wxCommandEvent &aEvent)
The gear's menu: the advanced window and the author.
bool transferCanStart()
False, with a message, while a transfer is running or a merge waits to be finished.
void updateMessageVisibility(bool aHasChanges)
Show the message field whenever a commit is possible, including a message-only amend.
void RefreshFileList()
Read the project's git status and repopulate the list.
bool m_editorsDirty
True when an open editor held unsaved changes at the last refresh.
void finishPullConflictPreparation(std::shared_ptr< KIGIT_COMMON > aCommon, bool aThenPush, std::shared_ptr< MERGE_PREPARATION > aPreparation)
void setBusy(bool aBusy, const wxString &aStatus)
Enable or disable the controls while a network operation runs, showing a status.
void ReloadAdvancedWindow()
Reload the advanced window when it is open, for a preferences change it also shows.
bool m_canAmend
Whether the current branch allows an amend. A refresh must not change an active amend into a commit.
wxString operationNoun() const
The noun for the operation in progress, for messages.
void onChecklistChanged(wxDataViewEvent &aEvent)
void populateBranches(const STATUS &aStatus)
Fill the branch dropdown from the completed snapshot.
void onFileActivated(wxDataViewEvent &aEvent)
bool m_upstreamAhead
True when the upstream has commits this project does not, so Get Changes has work to do.
void onPullDone(std::shared_ptr< KIGIT_COMMON > aCommon, int aResult, const wxString &aError, bool aAuthFailure, bool aThenPush, bool aCancelled)
bool m_commitButtonPushes
True while the main button reads Push, with nothing left to commit.
wxStaticText * m_summaryRemoved
void updateSummary()
Refresh the staged count in the changes heading, and the summary box where the wide layout has one.
void saveForCommit(std::shared_ptr< KIGIT_COMMON > aCommon, std::shared_ptr< COMMIT_REQUEST > aRequest)
void onCommit(wxCommandEvent &aEvent)
The split button's main half. It commits, or pushes when that is all there is to do.
void showDisabledState()
Clear the list and disable every control while version control is off.
void submitPush(std::shared_ptr< KIGIT_COMMON > aCommon)
void updateDescriptionHint()
Where the platform has no hint for a multi line box, a label lies over the description box while it i...
void onPushDone(std::shared_ptr< KIGIT_COMMON > aCommon, int aResult, const wxString &aError, bool aAuthFailure, bool aCancelled)
std::function< void(bool)> m_busyChangedHandler
Told when a transfer starts and ends. Empty unless an owning window asked.
void layoutWide()
Put the file list beside the commit controls instead of above them, for a wide host,...
KIGIT_TRANSFER m_transfer
The push or pull this panel submitted last.
std::pair< wxString, wxString > m_amendHead
The branch and commit chosen when amend started. Keep them fixed while editing.
void onChecklistContextMenu(wxDataViewEvent &aEvent)
void setStatusText(const wxString &aText)
Set the status line text and wrap it to the current panel width.
std::function< void()> m_messageChangedHandler
Told when the message is edited. Empty unless an owning window asked.
bool m_headDetached
True when HEAD is a commit rather than a branch, so nothing can be committed or pushed.
wxString m_statusText
Unwrapped status text, kept so it can be re-wrapped on resize.
std::vector< wxString > m_recentMessageTexts
The full messages behind the Recent messages dropdown, in its order after the caption.
wxString m_currentBranch
The branch a commit will land on, empty when detached or before the first commit.
void populateRecentMessages(const std::vector< wxString > &aMessages)
Fill the Recent messages dropdown from the completed snapshot.
void Push()
Push the local commits, as the Push button does.
void onShowDiff(wxCommandEvent &aEvent)
bool m_operationInProgress
True while a merge or rebase is in progress. The network button then reads Abandon.
void SetCommitMessage(const wxString &aMessage)
wxStaticText * m_summaryFiles
wxMenuItem * m_commitAndPushItem
The arrow menu's entry, relabelled with the main button.
void startPull(std::shared_ptr< KIGIT_COMMON > aCommon, bool aThenPush)
bool m_hasRepo
Whether the last completed status snapshot had a repository.
void SetFileSelectedHandler(std::function< void(const wxString &)> aHandler)
Called with the path of the file the user selects in the list, or empty when none is.
void onCommitAndPush(wxCommandEvent &aEvent)
GIT_COMMIT_PANE(EDA_BASE_FRAME *aParent)
int m_minDockWidth
The widest row's minimum width, measured before anything is hidden.
GitOperation m_operation
Which operation, so the button and the explanation can name it.
~GIT_COMMIT_PANE() override
std::set< wxString > m_shownPaths
Paths shown in the list at the last refresh, so a refresh can tell which files the user deliberately ...
void onRecentMessage(wxCommandEvent &aEvent)
void runActionQuery(std::shared_ptr< KIGIT_COMMON > aCommon, std::function< void(KIGIT_COMMON &)> aWork, std::function< void()> aDone, const wxString &aStatus)
Read an action's prerequisites off the UI thread, then resume only in the same project.
void onAmendToggled(wxCommandEvent &aEvent)
void onIdle(wxIdleEvent &aEvent)
bool m_listenerAttached
True once this panel has attached its refresh listener to a session at least once.
void onSize(wxSizeEvent &aEvent)
void syncListener()
Register a refresh listener with the project's current git session, re-registering if the session cha...
void confirmDiscardFile(std::shared_ptr< KIGIT_COMMON > aCommon, const wxString &aPath, bool aTracked, bool aConflicted, wxString aRoot)
KIGIT_QUERY m_actionQuery
EDITOR_SNAPSHOT m_docsBeforePull
On-disk identity of the open documents, taken before a pull so only the ones the merge rewrote are re...
wxStaticText * m_descriptionHint
The description box's hint, a label laid over it while it is empty.
void updateSummaryCount()
Show how many of the summary's 72 columns are left.
void applyStatus(const STATUS &aStatus)
Apply a completed repository snapshot on the UI thread.
void finishAbandonOperation(std::shared_ptr< KIGIT_COMMON > aCommon, const std::vector< wxString > &aOutside)
std::weak_ptr< KIGIT_COMMON > m_listenerSession
The git session this panel registered a refresh listener with, weak so it does not keep the session a...
void doCommit(bool aAndPush)
Commit the checked files (or amend the last commit when the amend box is ticked), then push when aAnd...
wxStaticText * m_summaryAdded
GitUserConfig GetUserConfig()
Get user configuration (name and email) from git config Falls back to common settings if not found in...
A private scratch directory for extracting committed file versions to diff, removed when this object ...
const wxString & Root() const
The scratch directory, for callers that lay out their own files (e.g. footprint libraries).
wxColour ToColour() const
static wxString LfsRefusal(KIGIT_COMMON *aCommon)
Return a refusal message for LFS files or an unknown project folder, otherwise empty.
The project's git session.
std::pair< int, int > GetWorkingTreeLineStats(const std::set< wxString > &aPaths) const
std::shared_ptr< std::atomic< bool > > CANCEL_TOKEN
One token per operation.
wxString GetLastCommitMessage() const
static wxString GetLastGitError()
wxString GetCurrentBranchName() const
wxString GetGitRootDirectory() const
bool IsOperationInProgress() const
True while a merge, rebase, cherry-pick or revert is in progress, which refuses commits.
bool IsPullRebaseConfigured() const
True when the repository is set to rebase rather than merge when getting changes.
bool HasUpstreamCommits() const
True when the upstream branch has commits the current branch does not.
git_repository * GetRepo() const
std::vector< wxString > GetRecentCommitMessages(size_t aMax) const
bool CreateBranch(const wxString &aName, const wxString &aFrom=wxEmptyString)
bool GetMergeConflicts(const wxString &aStageDir, wxString &aHeadOid, wxString &aTheirsOid, std::vector< KIGIT_CONFLICT_SIDES > &aConflicts, wxString &aError)
Recompute the merge in memory and extract each conflicted file's three versions into aStageDir,...
bool StartUnbornBranch(const wxString &aName)
Point HEAD at a branch that does not exist yet, so the first commit creates it.
bool IsHeadUnborn() const
True while the repository has no commits, so HEAD names a branch that does not exist yet.
wxString GetHeadCommitId() const
Full commit ID at HEAD, or empty for an unborn or missing HEAD.
bool HasPushAndPullRemote() const
bool IsPathInHead(const wxString &aPath)
True when the working directory relative path exists in the HEAD commit's tree.
bool HasLocalCommits() const
True when HEAD has commits its upstream does not, or has no upstream yet.
GitOperation OperationInProgress() const
Which operation that is, so the panel can name it.
std::vector< KIGIT_BRANCH_INFO > GetBranches() const
Local and remote branches with their tip commits.
bool DeleteBranch(const wxString &aName)
bool IsPathConflicted(const wxString &aPath) const
True while aPath is one of the files a merge or rebase stopped on.
bool IsHeadDetached() const
True when HEAD points at a commit rather than a branch, as after checking out a tag.
wxString GetErrorString()
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
A wrapper for reporting to a wxString object.
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
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.
@ SuccessIndexStale
Committed, but the staged list was left as another program had it. Never retry on this.
static wxString amendRefusal(KIGIT_COMMON &aCommon, const std::pair< wxString, wxString > &aExpectedHead)
static bool moveToNewBranch(KIGIT_COMMON &aCommon, const wxString &aName, wxString &aError, wxString &aDetail)
static std::pair< wxString, wxString > commitHead(git_repository *aRepo)
static void dropNewBranch(KIGIT_COMMON &aCommon, const wxString &aName, const wxString &aPrevious)
static bool reserveRecoveryFile(const wxString &aPreferredPath, wxString &aPathOut, wxString &aCreatedDir)
@ Busy
Another git operation held the action lock, so nothing was pushed.
bool PrepareEditorsForGitOperation(EDA_BASE_FRAME *aFrame, GIT_EDITOR_PREP aPurpose, bool *aSavedEditors)
Offer to save unsaved schematic and board edits before a git operation.
std::vector< wxString > ChangedTrackedFilesOutsideProject(KIGIT_COMMON &aCommon, const wxString &aProjectPath)
Repository relative paths of tracked files changed outside the project directory, which the checklist...
EDITOR_SNAPSHOT CaptureOpenEditorDocs(KIWAY &aKiway)
Record the on-disk identity of the open documents, before an operation that may rewrite them.
std::vector< FILE_CHECKLIST_ENTRY > BuildGitChangeEntries(KIGIT_COMMON &aCommon, const wxString &aProjectPath, bool aDarkTheme)
Build the file checklist entries for the project's changed files, transient files filtered out and ea...
void ReloadOpenEditors(KIWAY &aKiway, const EDITOR_SNAPSHOT &aBefore)
Tell the open board and schematic editors to reload from disk.
GIT_WORKTREE_STATUS ReadGitWorktreeStatus(KIGIT_COMMON &aCommon, const wxString &aProjectPath)
Read tracked changes and Git LFS using a captured project path.
bool DocumentEditorsDirty(KIWAY &aKiway)
Unsaved schematic or board edits can be saved for a commit, even with no changes on disk.
void ShowGitFileDiff(EDA_BASE_FRAME *aFrame, const wxString &aRelativePath)
Open the diff for a changed file, comparing the committed version at HEAD with the working copy.
bool ProjectSafeToRewrite(EDA_BASE_FRAME *aFrame, wxWindow *aParent, const GIT_WORKTREE_STATUS &aStatus, bool aAllowTrackedChanges)
Combine the repository snapshot with live editor state and explain any refusal.
bool EditorsDirty(KIWAY &aKiway)
Unsaved document or library edits block operations that rewrite files. Call on the UI thread.
@ COMMIT
Saving dirty editors for a commit also saves the project file.
static const int ERR_CANCELLED
The user cancelled the operation.
int DispatchMerge(KIWAY &aKiway, DOC_KIND aKind, const wxString &aAncestor, const wxString &aOurs, const wxString &aTheirs, const wxString &aOutput, bool aInteractive, bool aSingleFile, REPORTER *aReporter)
Run a 3-way document/library merge by calling the owning kiface's KIFACE_MERGE_DOCUMENT function expo...
DOC_KIND DocKindFromExtension(const wxString &aPath)
Map a path's extension to a DOC_KIND (.kicad_pcb -> PCB, .kicad_sch -> SCH, .kicad_sym -> SYM_LIB,...
DOC_KIND
Document type a diff/merge entry point should route to, derived from a file path's extension.
void SortBranchesForDisplay(std::vector< KIGIT_BRANCH_INFO > &aBranches)
Put the current branch first, then local and remote branches with the newest tips first.
void SplitCommitMessage(const wxString &aMessage, wxString &aSummary, wxString &aDescription)
Split a commit message into its first line and the description after the first blank line.
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 ...
GIT_IDENTITY ReadGitIdentity(KIGIT_COMMON &aCommon)
Read on a worker while holding the session's action lock.
bool EnsureGitIdentity(wxWindow *aParent, EDA_BASE_FRAME *aFrame, const std::shared_ptr< KIGIT_COMMON > &aCommon, const GIT_IDENTITY &aIdentity)
Open the same editor when the worker snapshot has no usable Git identity.
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.
wxString ComposeCommitMessage(const wxString &aSummary, const wxString &aDescription, const wxString &aSignOff)
Join a summary line, an optional description and an optional sign-off into one commit message.
void ShowBusyMessage(wxWindow *aParent)
Show a busy message for a user action. UI thread only.
std::unique_lock< std::mutex > TryLockAction(KIGIT_COMMON &aCommon, wxWindow *aParent)
Try the action lock and show a busy message if it is unavailable.
bool EditGitIdentity(wxWindow *aParent, EDA_BASE_FRAME *aFrame, const std::shared_ptr< KIGIT_COMMON > &aCommon, const GIT_IDENTITY &aIdentity)
Edit the repository's author and refresh Git views after saving.
void CallOnUi(std::function< void()> aFn)
Run aFn on the UI thread.
PGM_BASE & Pgm()
The global program "get" accessor.
What the open editors and the project settings looked like on disk before an operation that may rewri...
One file row in a FILE_CHECKLIST_CTRL.
wxString path
Row identity.
std::pair< wxString, wxString > head
std::vector< wxString > files
KIGIT::GIT_IDENTITY identity
std::vector< KIGIT_CONFLICT_SIDES > conflicts
KIGIT::GIT_IDENTITY identity
std::unique_ptr< GIT_DIFF_WORKSPACE > workspace
std::pair< int, int > lineStats
std::vector< KIGIT_BRANCH_INFO > branches
std::vector< FILE_CHECKLIST_ENTRY > entries
std::vector< wxString > recentMessages
Repository state used before an operation that requires a clean working tree.
Diff colors with defaults for light backgrounds.
static DIFF_COLOR_THEME ForText(bool aDarkTheme)
Resolve change colors for UI text. Pass the desktop theme captured on the UI thread.
Repository values and whether Git can use them to record a commit.
wxString theirsFile
Extracted remote version, an empty file when absent.
wxString ancestorFile
Extracted common-ancestor version, an empty file when absent.
wxString oursFile
Extracted local version, an empty file when absent.
bool hasOurs
False when the local side deleted the file.
wxString path
Repository-relative path of the conflicted file.
bool hasTheirs
False when the remote side deleted the file.
bool hasAncestor
False when the file did not exist in the common ancestor.
IbisParser parser & reporter
wxString result
Test unit parsing edge cases and error handling.
Definition of file extensions used in Kicad.