31#include <wx/choicdlg.h>
33#include <wx/filename.h>
34#include <wx/richmsgdlg.h>
35#include <wx/weakref.h>
74 aWord =
_(
"Modified" );
85 aWord =
_(
"Deleted" );
90 aWord =
_(
"Conflict" );
94 default:
return false;
102 std::vector<FILE_CHECKLIST_ENTRY> entries;
112 std::map<wxString, FileStatus> statusMap = handler.
GetFileStatus( *prefix,
true );
115 for(
const auto& [
path, fileStatus] : statusMap )
128 wxString relativePath =
path;
130 if( relativePath.StartsWith( workDir ) )
131 relativePath = relativePath.Mid( workDir.length() );
134 relativePath.Replace( wxS(
"\\" ), wxS(
"/" ) );
140 bool untracked = ( fileStatus.gitStatus & GIT_STATUS_WT_NEW )
141 && !( fileStatus.gitStatus & GIT_STATUS_INDEX_NEW );
146 const wxString pathPrefix = relativePath.Left( prefix->length() );
149 bool inProject = pathPrefix.CmpNoCase( *prefix ) == 0;
151 bool inProject = pathPrefix == *prefix;
163 entry.
path = relativePath;
164 entry.
label = wxFileName( relativePath ).GetFullName();
169 entries.push_back( entry );
187const EDITOR_DOC g_editorDocs[] = {
195bool makeFileStamp(
const wxFileName& aFn,
FILE_STAMP& aOut )
197 if( !aFn.IsOk() || !aFn.FileExists() )
200 wxString
path = aFn.GetFullPath();
203 aOut.
modified = aFn.GetModificationTime().GetValue().GetValue();
207 if( stat(
path.fn_str(), &st ) != 0 )
211 aOut.
modified =
static_cast<long long>( st.st_mtimespec.tv_sec ) * 1000000000LL + st.st_mtimespec.tv_nsec;
213 aOut.
modified =
static_cast<long long>( st.st_mtim.tv_sec ) * 1000000000LL + st.st_mtim.tv_nsec;
217 aOut.
size =
static_cast<long long>( aFn.GetSize().GetValue() );
227 if( makeFileStamp( aFn, stamp ) )
228 aSnapshot.
files[aFn.GetFullPath()] = stamp;
249 if( scanRoot.IsEmpty() )
252 if( !scanRoot.IsEmpty() )
254 wxArrayString sheetFiles;
255 wxDir::GetAllFiles( scanRoot, &sheetFiles, wxS(
"*.kicad_sch" ), wxDIR_FILES | wxDIR_DIRS );
258 for(
const wxString& sheet : sheetFiles )
259 addFileToSnapshot(
result, wxFileName( sheet ) );
280 addFileToSnapshot(
result, prl );
288 std::set<wxString> changed;
290 for(
const auto& [
path, stamp] : aBefore.
files )
294 if( it == aAfter.
files.end() || it->second != stamp )
295 changed.insert(
path );
298 for(
const auto& [
path, stamp] : aAfter.
files )
301 changed.insert(
path );
314 for(
const EDITOR_DOC& doc : g_editorDocs )
315 before.
editors[doc.frameType] = documentSnapshot( aKiway, doc.frameType );
317 before.
settings = settingsSnapshot();
327 bool settingsChanged = !changedFilesBetween( aBefore.
settings, settingsSnapshot() ).empty();
329 if( settingsChanged )
339 for(
const EDITOR_DOC& doc : g_editorDocs )
341 if( !aKiway.
Player( doc.frameType,
false ) )
346 auto it = aBefore.
editors.find( doc.frameType );
348 std::set<wxString> changed = changedFilesBetween( before, documentSnapshot( aKiway, doc.frameType ) );
350 if( changed.empty() )
354 if( settingsChanged )
356 std::string settingsPayload;
357 aKiway.
ExpressMail( doc.frameType, doc.settingsMail, settingsPayload );
367 for(
const wxString&
path : changed )
368 joined +=
path + wxS(
"\n" );
370 std::string payload = joined.utf8_string();
371 aKiway.
ExpressMail( doc.frameType, doc.reloadMail, payload );
381struct WORKTREE_QUERY_CONTEXT
383 WORKTREE_QUERY_CONTEXT( EDA_BASE_FRAME* aFrame, wxWindow* aParent ) :
385 parent( aParent ? aParent : aFrame ),
386 session( aFrame->
Prj().GetGitCommon() )
392 return frame.get() && parent.get() && !frame->IsBeingDeleted() && !parent->IsBeingDeleted();
395 bool IsCurrent()
const
397 return IsAlive() && !session->IsShuttingDown() && frame->Prj().GetGitCommon() == session
399 && !frame->Prj().GetLocalSettings().m_GitIntegrationDisabled;
402 wxWeakRef<EDA_BASE_FRAME> frame;
403 wxWeakRef<wxWindow> parent;
404 std::shared_ptr<KIGIT_COMMON> session;
412 std::function<
void()> aFinished = {} )
416 GIT_WORKTREE_STATUS worktree;
417 wxString currentBranch;
421 auto context = std::make_shared<WORKTREE_QUERY_CONTEXT>( aFrame, aParent );
422 auto outcome = std::make_shared<OUTCOME>();
428 outcome->currentBranch = aCommon.GetCurrentBranchName();
429 outcome->worktree = ReadGitWorktreeStatus( aCommon, projectPath );
431 [context, outcome, aReady, aFinished]()
435 if( !context->IsCurrent() )
440 if( !ProjectSafeToRewrite( context->frame, context->parent, outcome->worktree ) )
443 aReady( outcome->currentBranch, outcome->worktree );
448 if( aFinished && context->IsAlive() )
461 checkProjectWorktree( aFrame, aParent, aQuery,
470 std::function<
void()> aFinished )
472 if( aBranch.IsEmpty() )
480 auto context = std::make_shared<WORKTREE_QUERY_CONTEXT>( aFrame, aParent );
482 checkProjectWorktree(
483 aFrame, aParent, aQuery,
486 const wxWeakRef<EDA_BASE_FRAME>& frame = context->frame;
487 const wxWeakRef<wxWindow>& parent = context->parent;
488 const std::shared_ptr<KIGIT_COMMON>& common = context->session;
490 if( aBranch == aCurrentBranch )
493 if( !
IsOK( parent, wxString::Format(
_(
"Switch to branch '%s'? This replaces the project files "
494 "on disk with that branch's version." ),
501 if( !context->IsCurrent() )
511 if( !context->IsCurrent() )
518 wxString resultError;
523 if( !lock.owns_lock() )
533 common->UpdateCurrentBranchInfo();
544 DisplayErrorMessage( parent, wxString::Format(
_(
"Could not switch to branch '%s'." ), aBranch ),
550 common->NotifyRefreshListeners();
552 std::move( aFinished ) );
559enum class EDITOR_SCOPE
571 std::optional<MAIL_T> saveMail;
575const GIT_EDITOR g_gitEditors[] = {
584std::vector<const GIT_EDITOR*> dirtyEditors(
KIWAY& aKiway, EDITOR_SCOPE aScope )
586 std::vector<const GIT_EDITOR*> dirty;
588 for(
const GIT_EDITOR&
editor : g_gitEditors )
590 bool document =
editor.saveMail.has_value();
592 if( ( aScope == EDITOR_SCOPE::DOCUMENTS && !document ) || ( aScope == EDITOR_SCOPE::LIBRARIES && document ) )
600 dirty.push_back( &
editor );
607std::vector<wxString> dirtyEditorNames(
KIWAY& aKiway, EDITOR_SCOPE aScope )
609 std::vector<wxString> names;
611 for(
const GIT_EDITOR*
editor : dirtyEditors( aKiway, aScope ) )
612 names.push_back( wxGetTranslation(
editor->label ) );
622 return !dirtyEditors( aKiway, EDITOR_SCOPE::DOCUMENTS ).empty();
628 return !dirtyEditors( aKiway, EDITOR_SCOPE::ALL ).empty();
634 return dirtyEditorNames( aKiway, EDITOR_SCOPE::LIBRARIES );
640 std::vector<wxString> paths;
648 for(
const auto& [
path, fileStatus] : handler.
GetFileStatus( wxEmptyString,
true ) )
652 if( ( fileStatus.gitStatus & ~( GIT_STATUS_WT_NEW | GIT_STATUS_IGNORED ) ) == GIT_STATUS_CURRENT )
660 wxString relativePath =
path;
662 if( relativePath.StartsWith( workDir ) )
663 relativePath = relativePath.Mid( workDir.length() );
665 paths.push_back( relativePath );
691 bool aAllowTrackedChanges )
const
693 if( !aUnsavedEditors.empty() )
697 for(
const wxString&
name : aUnsavedEditors )
698 listed += wxString::Format( wxS(
"\n - %s" ),
name );
700 wxString
message = aAllowTrackedChanges
701 ?
_(
"The following editors have unsaved changes:%s\n\n"
702 "Save or discard those changes before stashing." )
703 :
_(
"The following editors have unsaved changes:%s\n\n"
704 "Save or discard those changes first. If saving leaves uncommitted "
705 "files, commit or stash them before trying again." );
707 return wxString::Format(
message, listed );
711 return _(
"Could not check the working files. Try again before rewriting project files." );
717 return wxEmptyString;
724 listed += wxS(
"\n " ) +
path;
726 return wxString::Format(
_(
"There are uncommitted changes elsewhere in this repository:%s\n\n"
727 "Commit or discard them from that project first, then try again." ),
731 return _(
"There are changes that are not committed yet, and this would run over them. "
732 "Commit them first, or set them aside with Stash Changes." );
737 bool aAllowTrackedChanges )
739 std::vector<wxString> unsaved = dirtyEditorNames( aFrame->
Kiway(), EDITOR_SCOPE::ALL );
753 *aSavedEditors =
false;
760 if( !libraryEditors.empty() )
764 for(
const wxString&
name : libraryEditors )
765 listed += wxString::Format( wxS(
"\n - %s" ),
name );
767 DisplayInfoMessage( aFrame, wxString::Format(
_(
"The following editors have unsaved library "
768 "changes:%s\n\nThis rewrites files on disk. Save or "
769 "discard those changes first, then try again." ),
775 std::vector<const GIT_EDITOR*> dirty = dirtyEditors( aFrame->
Kiway(), EDITOR_SCOPE::DOCUMENTS );
781 for(
const GIT_EDITOR*
editor : dirty )
782 listed += wxString::Format( wxS(
"\n - %s" ), wxGetTranslation(
editor->label ) );
785 long style = wxYES_DEFAULT | wxICON_WARNING | wxCENTER;
789 message = wxString::Format(
_(
"The following editors have unsaved changes:%s\n\n"
790 "This rewrites those files on disk. They must be saved "
791 "first, otherwise the next save would overwrite what "
798 message = wxString::Format(
_(
"The following editors have unsaved changes:%s\n\n"
799 "Save them and review the updated file list before committing?" ),
801 style |= wxYES_NO | wxCANCEL;
804 wxRichMessageDialog dlg( aFrame,
message,
_(
"Unsaved Changes" ), style );
807 dlg.SetYesNoLabels(
_(
"&Save and Continue" ),
_(
"Cancel" ) );
809 dlg.SetYesNoCancelLabels(
_(
"&Save and Review" ),
_(
"Commit &Anyway" ),
_(
"Cancel" ) );
811 int answer = dlg.ShowModal();
813 if( answer == wxID_CANCEL )
820 if( answer == wxID_YES )
822 for(
const GIT_EDITOR*
editor : dirty )
827 if( payload !=
"success" )
830 aFrame, wxString::Format(
_(
"Could not save %s." ), wxGetTranslation(
editor->label ) ) );
840 *aSavedEditors =
true;
850 m_root.MakeDirectory( wxS(
"kicad_gitdiff_" ), 0700 );
858 return wxEmptyString;
860 const wxString sep = wxFileName::GetPathSeparator();
866 wxString dir =
Root() + sep + wxString::Format( wxS(
"s%d" ),
m_slot++ );
869 return wxEmptyString;
871 wxString
path = dir + sep + aRelPath;
873 return wxFileName::FileExists(
path ) ?
path : wxString( wxEmptyString );
876 wxString
path =
Root() + sep + wxString::Format( wxS(
"s%d_" ),
m_slot++ ) + wxFileName( aRelPath ).GetFullName();
879 return wxEmptyString;
893struct MODAL_GIT_DIFF_REQUEST
895 MODAL_GIT_DIFF_REQUEST(
EDA_BASE_FRAME* aFrame, wxWindow* aParent ) :
898 session( aFrame->
Prj().GetGitCommon() )
902 bool IsCurrent()
const
904 return frame.get() && parent.get() && !frame->IsBeingDeleted() && !parent->IsBeingDeleted()
905 && frame->Prj().GetGitCommon() == session && !session->IsShuttingDown();
908 wxWeakRef<EDA_BASE_FRAME> frame;
909 wxWeakRef<wxWindow> parent;
910 std::shared_ptr<KIGIT_COMMON> session;
915struct MODAL_GIT_DIFF_RESULT
917 std::unique_ptr<GIT_DIFF_WORKSPACE> workspace;
918 GIT_FILE_SIDES sides;
919 std::vector<KIGIT::TEXT_DIFF_LINE> textLines;
924std::string readDiffText(
const wxString& aPath )
926 if( aPath.IsEmpty() )
929 std::ifstream in( aPath.fn_str(), std::ios::binary );
930 return std::string( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
936void loadModalGitDiff(
const std::shared_ptr<MODAL_GIT_DIFF_REQUEST>& aRequest,
const wxString& aRelativePath,
937 const wxString& aCommitHash )
939 auto result = std::make_shared<MODAL_GIT_DIFF_RESULT>();
940 result->sides.error =
_(
"Could not compare this file." );
946 result->workspace = std::make_unique<GIT_DIFF_WORKSPACE>();
948 if( !result->workspace->IsOk() )
950 result->sides.error = _(
"Could not create a temporary directory for the diff." );
955 aCommitHash.IsEmpty()
965 std::string before = readDiffText( result->sides.before );
966 std::string after = readDiffText( result->sides.after );
968 if( KIGIT::TextLooksBinary( before ) || KIGIT::TextLooksBinary( after ) )
970 result->sides.error = wxString::Format(
971 _(
"'%s' is not a plain text file. KiCad compares design files and text files "
972 "only, so there is no comparison to show." ),
983 [aRequest,
result, aRelativePath, aCommitHash]()
985 if( !aRequest->IsCurrent() )
988 wxWindow* parent = aRequest->parent.get();
992 if( !
result->sides.error.IsEmpty() )
1005 wxString beforeLabel = aCommitHash.IsEmpty()
1006 ?
_(
"BASE (HEAD)" )
1007 : wxString::
Format(
_(
"BASE (parent of %s)" ), aCommitHash );
1008 wxString afterLabel = aCommitHash.IsEmpty() ?
_(
"WORKING (changes)" )
1009 : wxString::Format(
_(
"WORKING (%s)" ), aCommitHash );
1015 result->sides.after, beforeLabel, afterLabel, parent, &
reporter );
1017 if( aRequest->IsCurrent() &&
reporter.HasMessage() )
1027 auto request = std::make_shared<MODAL_GIT_DIFF_REQUEST>( aFrame, aFrame );
1028 loadModalGitDiff( request, aRelativePath, wxEmptyString );
1051 if( inHead && aSides.
before.IsEmpty() )
1053 aSides.
error = wxString::Format(
_(
"Could not read the committed version of '%s'." ), aRelativePath );
1059 if( !wxFileName::FileExists( aSides.
after ) )
1060 aSides.
after.clear();
1067 const wxString& aRelativePath )
1069 auto request = std::make_shared<MODAL_GIT_DIFF_REQUEST>( aFrame, aParentWindow ? aParentWindow : aFrame );
1073 if( !aRelativePath.IsEmpty() )
1075 loadModalGitDiff( request, aRelativePath, aCommitHash );
1079 auto changedPaths = std::make_shared<std::vector<wxString>>();
1085 git_repository* repo = aCommon.GetRepo();
1087 git_object* commitObj = nullptr;
1090 if( git_revparse_single( &commitObj, repo, aCommitHash.utf8_string().c_str() ) == GIT_OK )
1092 if( git_object_type( commitObj ) == GIT_OBJECT_COMMIT
1093 && git_commit_parentcount( reinterpret_cast<git_commit*>( commitObj ) ) > 0 )
1095 parentRef = aCommitHash + wxT(
"^" );
1098 git_object_free( commitObj );
1106 if( !request->IsCurrent() )
1109 wxWindow* parent = request->parent.get();
1121 wxArrayString choices;
1124 choices.Add(
path );
1126 int sel = wxGetSingleChoiceIndex(
_(
"This commit changed more than one file. "
1127 "Select one to compare:" ),
1128 _(
"Select File to Compare" ), choices, parent );
1133 chosen = ( *changedPaths )[sel];
1137 if( request->IsCurrent() )
1138 loadModalGitDiff( request, chosen, aCommitHash );
1155 git_object* commitObj =
nullptr;
1156 unsigned parentCount = 0;
1158 if( git_revparse_single( &commitObj, repo, aCommitHash.utf8_string().c_str() ) == GIT_OK )
1160 if( git_object_type( commitObj ) == GIT_OBJECT_COMMIT )
1161 parentCount = git_commit_parentcount(
reinterpret_cast<git_commit*
>( commitObj ) );
1163 if( parentCount > 0 )
1164 parentRef = aCommitHash + wxT(
"^" );
1166 git_object_free( commitObj );
1171 if( file.path != aRelativePath )
1182 wxString parentPath = file.oldPath.IsEmpty() ? file.path : file.oldPath;
1189 if( ( hasParent && aSides.
before.IsEmpty() ) || ( hasCommit && aSides.
after.IsEmpty() ) )
1191 aSides.
error =
_(
"Could not read this commit's file contents." );
1200 if( parentCount > 2 )
1203 aSides.
after = aWorkspace.
Materialize( repo, aCommitHash + wxT(
"^3" ), aRelativePath, aSides.
kind );
1205 if( !aSides.
after.IsEmpty() )
1209 aSides.
error = wxString::Format(
_(
"This commit did not change '%s'." ), aRelativePath );
BITMAPS
A list of all bitmap identifiers.
Read-only line diff, for the file types that have no visual diff.
The base frame for deriving all KiCad main window classes.
SETTINGS_MANAGER * GetSettingsManager() const
virtual wxString GetCurrentFileName() const
Get the full filename + path of the currently opened file in the frame.
virtual bool IsContentModified() const
Get if the contents of the frame have been modified since the last save.
BranchResult SwitchToBranch(const wxString &aBranchName)
Switch to the specified branch.
A private scratch directory for extracting committed file versions to diff, removed when this object ...
bool IsOk() const
True when the scratch directory was created.
const wxString & Root() const
The scratch directory, for callers that lay out their own files (e.g. footprint libraries).
wxString Materialize(git_repository *aRepo, const wxString &aRef, const wxString &aRelPath, KICAD_DIFF::DOC_KIND aKind)
Extract aRelPath at aRef into a uniquely named slot and return the path to load, or an empty string w...
std::map< wxString, FileStatus > GetFileStatus(const wxString &aPathspec=wxEmptyString, bool aChangedOnly=false)
Get detailed file status for all files in the specified path.
wxString GetWorkingDirectory()
Get the repository working directory path.
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
wxColour ToColour() const
static bool WriteBlobToFile(git_repository *aRepo, const wxString &aRef, const wxString &aPath, const wxString &aDestPath)
Write a file as it exists at a git ref to a path on disk, so a committed version can be diffed.
static bool IsTransientGitFile(const wxFileName &aFile)
Match a project-relative path against the shared transient patterns.
static bool WriteTreeToDir(git_repository *aRepo, const wxString &aRef, const wxString &aDestDir)
Check out a whole tree into a directory, leaving the repository's working copy and index alone.
static bool IsWithinProjectPath(const wxString &aAbsPath, const wxString &aProjectPath)
Test whether an absolute file path lives inside the current project directory.
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.
bool HasUncommittedTrackedChanges(bool *aReadOk=nullptr) const
std::optional< wxString > GetProjectPrefix() const
Project folder relative to the repository, with a trailing slash.
static wxString GetLastGitError()
git_repository * GetRepo() const
wxString GetProjectDir() const
Return the path set by SetProjectDir(), or the repository's working directory.
bool IsPathInHead(const wxString &aPath)
True when the working directory relative path exists in the HEAD commit's tree.
wxString GetErrorString()
Read repository state without blocking the UI.
void Run(std::shared_ptr< KIGIT_COMMON > aSession, std::function< void(KIGIT_COMMON &)> aWork, std::function< void()> aDone)
Run aWork under the session's action lock on a worker, then aDone on the UI thread.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
virtual void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=nullptr, bool aFromOtherThread=false)
Send aPayload to aDestination from aSource.
virtual COMMON_SETTINGS * GetCommonSettings() const
virtual SETTINGS_MANAGER & GetSettingsManager() const
bool LoadFromFile(const wxString &aDirectory="") override
Loads the backing file from disk and then calls Load()
Container for project specific data.
virtual std::shared_ptr< KIGIT_COMMON > GetGitCommon() const
This project's Git session, created empty on first use.
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
virtual const wxString GetProjectPath() const
Return the full path of the project.
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
virtual PROJECT_FILE & GetProjectFile() const
bool SaveProject(const wxString &aFullPath=wxEmptyString, PROJECT *aProject=nullptr)
Save a loaded project.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
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.
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
@ FRAME_SCH_SYMBOL_EDITOR
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...
void ShowGitCommitDiff(EDA_BASE_FRAME *aFrame, const wxString &aCommitHash, wxWindow *aParentWindow, const wxString &aRelativePath)
Open the diff for what a commit changed, comparing it against its parent.
EDITOR_SNAPSHOT CaptureOpenEditorDocs(KIWAY &aKiway)
Record the on-disk identity of the open documents, before an operation that may rewrite them.
bool MaterializeWorkingFile(KIGIT_COMMON &aCommon, GIT_DIFF_WORKSPACE &aWorkspace, const wxString &aRelativePath, GIT_FILE_SIDES &aSides)
Write the committed version of a file into aWorkspace and name its working copy beside it.
bool MaterializeCommitFile(KIGIT_COMMON &aCommon, GIT_DIFF_WORKSPACE &aWorkspace, const wxString &aCommitHash, const wxString &aRelativePath, GIT_FILE_SIDES &aSides)
Write both sides of one file a commit changed into aWorkspace, the parent's version and the commit's.
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.
std::vector< wxString > LibraryEditorsWithUnsavedChanges(KIWAY &aKiway)
Names of the library editors holding unsaved changes.
void ProjectClearOfUncommittedWork(EDA_BASE_FRAME *aFrame, wxWindow *aParent, KIGIT_QUERY &aQuery, std::function< void(const GIT_WORKTREE_STATUS &)> aReady)
Check the working tree and Git LFS on a worker, then run aReady on the UI thread when the operation c...
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.
static bool statusToDisplay(KIGIT_COMMON::GIT_STATUS aStatus, wxString &aWord, BITMAPS &aIcon, wxColour &aColour, const KICAD_DIFF::DIFF_COLOR_THEME &aTheme)
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.
void SwitchProjectToBranch(EDA_BASE_FRAME *aFrame, wxWindow *aParent, const wxString &aBranch, KIGIT_QUERY &aQuery, std::function< void()> aFinished)
Check the working tree on a worker, then confirm switching to aBranch and recheck unsaved editors.
bool EditorsDirty(KIWAY &aKiway)
Unsaved document or library edits block operations that rewrite files. Call on the UI thread.
GIT_EDITOR_PREP
What the caller is about to do, which decides the wording and whether the project file is written.
@ COMMIT
Saving dirty editors for a commit also saves the project file.
@ WORKING_TREE
The working files are about to be rewritten, by a pull, branch switch or stash.
static const std::string ProjectLocalSettingsFileExtension
Some functions to handle hotkeys in KiCad.
static std::vector< std::string > changedPaths(git_repository *aRepo)
MAIL_T
The set of mail types sendable via KIWAY::ExpressMail() and supplied as the aCommand parameter to tha...
@ MAIL_SCH_PROJECT_SETTINGS_CHANGED
@ MAIL_PCB_PROJECT_SETTINGS_CHANGED
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.
int DispatchOpenDiffDialog(KIWAY &aKiway, DOC_KIND aKind, const wxString &aFileA, const wxString &aFileB, const wxString &aLabelA, const wxString &aLabelB, wxWindow *aParent, REPORTER *aReporter)
Open DIALOG_KICAD_DIFF on two files by calling the owning kiface's KIFACE_OPEN_DIFF_DIALOG function e...
@ ALL
All except INITIAL_ADD.
std::vector< CHANGED_FILE > CompareRefs(git_repository *aRepo, const wxString &aBaseRef, const wxString &aHeadRef)
Compare two git refs (branch / tag / commit OID) within a repository and return the per-file change l...
std::vector< wxString > SplitTextLines(const std::string &aContent)
Tolerates CRLF endings and content that is not valid UTF-8.
std::unique_lock< std::mutex > TryLockAction(KIGIT_COMMON &aCommon, wxWindow *aParent)
Try the action lock and show a busy message if it is unavailable.
std::vector< TEXT_DIFF_LINE > BuildTextDiff(const std::vector< wxString > &aOld, const std::vector< wxString > &aNew)
Every line of both sides appears once. There are no hunks and nothing is elided.
PGM_BASE & Pgm()
The global program "get" accessor.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
The files making up one open document, each with its on-disk identity.
std::map< wxString, FILE_STAMP > files
What the open editors and the project settings looked like on disk before an operation that may rewri...
std::map< FRAME_T, DOCUMENT_SNAPSHOT > editors
DOCUMENT_SNAPSHOT settings
One file row in a FILE_CHECKLIST_CTRL.
wxString label
Shown in place of the path.
wxString path
Row identity.
wxColour statusColour
Text colour for the status, or the default when not set.
wxString status
Free text, e.g. "Modified".
On-disk identity of one file, used to tell whether a git operation rewrote it.
long long modified
Modification time, nanoseconds where the platform provides them.
The two sides of one file, written out for a comparison.
KICAD_DIFF::DOC_KIND kind
wxString before
Empty when the file did not exist on that side.
wxString error
Why the sides could not be written, when they could not.
Repository state used before an operation that requires a clean working tree.
std::vector< wxString > outsideProject
wxString RefusalMessage(const std::vector< wxString > &aUnsavedEditors, bool aAllowTrackedChanges=false) const
Empty when rewriting is allowed.
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.
IbisParser parser & reporter
wxString result
Test unit parsing edge cases and error handling.
Definition of file extensions used in Kicad.