35#include <wx/datetime.h>
39#include <wx/filename.h>
40#include <wx/stdpaths.h>
49 LIBGIT2_SCOPE() { git_libgit2_init(); }
50 ~LIBGIT2_SCOPE() { git_libgit2_shutdown(); }
54struct SCOPED_BOOL_OVERRIDE
56 explicit SCOPED_BOOL_OVERRIDE(
bool& aFlag ) : m_flag( aFlag ), m_original( aFlag ) {}
57 ~SCOPED_BOOL_OVERRIDE() { m_flag = m_original; }
66struct SCOPED_BACKUP_LOCATION_OVERRIDE
68 explicit SCOPED_BACKUP_LOCATION_OVERRIDE(
BACKUP_LOCATION& aLocation ) :
69 m_location( aLocation ), m_original( aLocation )
73 ~SCOPED_BACKUP_LOCATION_OVERRIDE() { m_location = m_original; }
80struct SCOPED_BACKUP_FORMAT_OVERRIDE
82 explicit SCOPED_BACKUP_FORMAT_OVERRIDE(
BACKUP_FORMAT& aFormat ) :
83 m_format( aFormat ), m_original( aFormat )
87 ~SCOPED_BACKUP_FORMAT_OVERRIDE() { m_format = m_original; }
96struct SCOPED_PROJECT_LOAD
98 SCOPED_PROJECT_LOAD( SETTINGS_MANAGER& aMgr,
const wxString& aProjectFile ) : m_mgr( aMgr )
100 m_mgr.LoadProject( aProjectFile.ToStdString() );
103 ~SCOPED_PROJECT_LOAD() { m_mgr.UnloadProject( &m_mgr.Prj(),
false ); }
105 SETTINGS_MANAGER& m_mgr;
109void writeTextFile(
const wxString& aPath,
const wxString& aContents )
111 wxFFile f( aPath, wxT(
"w" ) );
113 f.Write( aContents );
134 std::vector<HISTORY_FILE_DATA> fileData;
138 BOOST_CHECK_NO_THROW( board.
SaveToHistory( wxS(
"/tmp/anywhere" ), fileData ) );
139 BOOST_CHECK( fileData.empty() );
151 wxString tempDir = wxStandardPaths::Get().GetTempDir();
152 wxString projectPath = tempDir + wxFileName::GetPathSeparator() + wxS(
"pcb_autosave.kicad_pro" );
159 std::vector<HISTORY_FILE_DATA> fileData;
163 BOOST_CHECK( fileData.empty() );
180 LIBGIT2_SCOPE libgit;
183 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
184 backupEnabled =
true;
191 wxString boardPath =
path + wxFileName::GetPathSeparator() + wxS(
"stray.kicad_pcb" );
192 writeTextFile( boardPath, wxS(
"(kicad_pcb (version 20240108))\n" ) );
196 BOOST_CHECK( !history.
Init(
path ) );
198 BOOST_CHECK( !history.
TagSave(
path, wxS(
"pcb" ) ) );
201 wxString historyDir =
path + wxFileName::GetPathSeparator() + wxS(
".history" );
202 BOOST_CHECK( !wxDirExists( historyDir ) );
215 LIBGIT2_SCOPE libgit;
218 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
219 backupEnabled =
true;
224 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"subdirs.kicad_pro" ), wxS(
"{}\n" ) );
225 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"subdirs.kicad_pcb" ),
226 wxS(
"(kicad_pcb (version 20240108))\n" ) );
230 wxString subDir =
path + wxFileName::GetPathSeparator() + wxS(
"libs" );
231 BOOST_REQUIRE( wxFileName::Mkdir( subDir, 0777, wxPATH_MKDIR_FULL ) );
232 writeTextFile( subDir + wxFileName::GetPathSeparator() + wxS(
"fp.kicad_mod" ),
233 wxS(
"(footprint test)\n" ) );
239 wxString historyDir =
path + wxFileName::GetPathSeparator() + wxS(
".history" );
240 BOOST_CHECK( wxDirExists( historyDir ) );
241 BOOST_CHECK( !wxDirExists( subDir + wxFileName::GetPathSeparator() + wxS(
".history" ) ) );
247 writeTextFile( subDir + wxFileName::GetPathSeparator() + wxS(
"fp.kicad_mod" ),
248 wxS(
"(footprint test (modified))\n" ) );
256 BOOST_CHECK( headBefore != headAfter );
263 LIBGIT2_SCOPE libgit;
267 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
268 backupEnabled =
true;
271 const wxString& projectPath = tempProject.
PathStr();
274 projectPath + wxFileName::GetPathSeparator() + wxS(
"issue24016.kicad_pcb" );
275 writeTextFile( boardPath, wxS(
"(kicad_pcb (version 20240108))\n" ) );
279 wxString projectFile =
280 projectPath + wxFileName::GetPathSeparator() + wxS(
"issue24016.kicad_pro" );
281 writeTextFile( projectFile, wxS(
"{}\n" ) );
286 wxString headHash = history.
GetHeadHash( projectPath );
291 wxString backupsDir =
292 projectPath + wxFileName::GetPathSeparator() + wxS(
"issue24016-backups" );
293 BOOST_REQUIRE( wxFileName::Mkdir( backupsDir, 0777, wxPATH_MKDIR_FULL ) );
295 wxString zipPath = backupsDir + wxFileName::GetPathSeparator()
296 + wxS(
"issue24016-2026-04-22_120000.zip" );
297 writeTextFile( zipPath, wxS(
"pretend-zip-contents" ) );
299 writeTextFile( boardPath, wxS(
"(kicad_pcb (version 20240108) (dirty yes))\n" ) );
303 BOOST_CHECK_MESSAGE( wxDirExists( backupsDir ),
304 "zip backups directory must survive RestoreCommit" );
305 BOOST_CHECK_MESSAGE( wxFileExists( zipPath ),
306 ".zip archive inside the backups directory must survive RestoreCommit" );
325 LIBGIT2_SCOPE libgit;
328 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
329 backupEnabled =
true;
332 const wxString& projectPath = tempProject.
PathStr();
335 wxString parentPro = projectPath + wxFileName::GetPathSeparator() + wxS(
"projectA.kicad_pro" );
336 wxString parentPcb = projectPath + wxFileName::GetPathSeparator() + wxS(
"projectA.kicad_pcb" );
337 writeTextFile( parentPro, wxS(
"{}\n" ) );
338 writeTextFile( parentPcb, wxS(
"(kicad_pcb (version 20240108))\n" ) );
343 wxString headHash = history.
GetHeadHash( projectPath );
349 wxString nestedDir = projectPath + wxFileName::GetPathSeparator() + wxS(
"projectB" );
350 BOOST_REQUIRE( wxFileName::Mkdir( nestedDir, 0777, wxPATH_MKDIR_FULL ) );
352 wxString nestedPro = nestedDir + wxFileName::GetPathSeparator() + wxS(
"projectB.kicad_pro" );
353 wxString nestedPcb = nestedDir + wxFileName::GetPathSeparator() + wxS(
"projectB.kicad_pcb" );
354 wxString nestedSch = nestedDir + wxFileName::GetPathSeparator() + wxS(
"projectB.kicad_sch" );
355 writeTextFile( nestedPro, wxS(
"{ \"nested\": true }\n" ) );
356 writeTextFile( nestedPcb, wxS(
"(kicad_pcb (version 20240108) (nested yes))\n" ) );
357 writeTextFile( nestedSch, wxS(
"(kicad_sch (version 20240108))\n" ) );
360 writeTextFile( parentPcb, wxS(
"(kicad_pcb (version 20240108) (dirty yes))\n" ) );
365 BOOST_CHECK_MESSAGE( wxDirExists( nestedDir ),
366 "nested project directory must survive RestoreCommit" );
367 BOOST_CHECK_MESSAGE( wxFileExists( nestedPro ),
368 "nested .kicad_pro must survive RestoreCommit" );
369 BOOST_CHECK_MESSAGE( wxFileExists( nestedPcb ),
370 "nested .kicad_pcb must survive RestoreCommit" );
371 BOOST_CHECK_MESSAGE( wxFileExists( nestedSch ),
372 "nested .kicad_sch must survive RestoreCommit" );
375 BOOST_CHECK( wxFileExists( parentPro ) );
376 BOOST_CHECK( wxFileExists( parentPcb ) );
387 LIBGIT2_SCOPE libgit;
390 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
391 backupEnabled =
true;
394 const wxString& projectPath = tempProject.
PathStr();
396 wxString boardPath = projectPath + wxFileName::GetPathSeparator() + wxS(
"rb.kicad_pcb" );
397 wxString projectFile = projectPath + wxFileName::GetPathSeparator() + wxS(
"rb.kicad_pro" );
398 writeTextFile( projectFile, wxS(
"{}\n" ) );
399 writeTextFile( boardPath, wxS(
"(kicad_pcb (version 20240108))\n" ) );
404 wxString headHash = history.
GetHeadHash( projectPath );
408 writeTextFile( boardPath, wxS(
"(kicad_pcb (version 20240108) (dirty yes))\n" ) );
414 wxString parentDir = wxFileName( projectPath ).GetPath();
415 wxString leafPrefix = wxFileName( projectPath ).GetFullName() + wxS(
"_restore_backup_" );
417 wxDir dir( parentDir );
420 wxString retainedBackup;
421 bool foundLegacyBackup =
false;
424 for(
bool cont = dir.GetFirst( &
name, wxEmptyString, wxDIR_DIRS ); cont;
425 cont = dir.GetNext( &
name ) )
427 if(
name.StartsWith( leafPrefix ) )
429 retainedBackup = parentDir + wxFileName::GetPathSeparator() +
name;
432 BOOST_CHECK_MESSAGE(
name.Find(
':' ) == wxNOT_FOUND,
433 "retained backup directory name must not contain ':' "
434 "(Windows-illegal in path components)" );
436 else if(
name == wxFileName( projectPath ).GetFullName() + wxS(
"_restore_backup" ) )
438 foundLegacyBackup =
true;
443 !retainedBackup.IsEmpty(),
444 "RestoreCommit must retain a timestamped _restore_backup_<ts>/ sibling directory" );
447 "RestoreCommit must not leave the legacy non-timestamped _restore_backup/ behind" );
450 if( !retainedBackup.IsEmpty() && wxDirExists( retainedBackup ) )
451 wxFileName::Rmdir( retainedBackup, wxPATH_RMDIR_RECURSIVE );
460 LIBGIT2_SCOPE libgit;
463 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
464 backupEnabled =
true;
469 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"p.kicad_pro" ), wxS(
"{}\n" ) );
470 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"p.kicad_pcb" ),
471 wxS(
"(kicad_pcb (version 20240108))\n" ) );
472 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"p.kicad_sch" ),
473 wxS(
"(kicad_sch (version 20240108))\n" ) );
475 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"passwords.txt" ), wxS(
"secret\n" ) );
476 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"datasheet.pdf" ), wxS(
"fake pdf bytes\n" ) );
477 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"notes.md" ), wxS(
"personal notes\n" ) );
479 wxString subDir =
path + wxFileName::GetPathSeparator() + wxS(
"docs" );
480 BOOST_REQUIRE( wxFileName::Mkdir( subDir, 0777, wxPATH_MKDIR_FULL ) );
481 writeTextFile( subDir + wxFileName::GetPathSeparator() + wxS(
"manual.txt" ), wxS(
"irrelevant\n" ) );
486 wxString hist =
path + wxFileName::GetPathSeparator() + wxS(
".history" );
487 git_repository* repo =
nullptr;
488 BOOST_REQUIRE_EQUAL( git_repository_open( &repo, hist.mb_str().data() ), 0 );
491 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &head_oid, repo,
"HEAD" ), 0 );
493 git_commit* head =
nullptr;
494 BOOST_REQUIRE_EQUAL( git_commit_lookup( &head, repo, &head_oid ), 0 );
496 git_tree* tree =
nullptr;
497 BOOST_REQUIRE_EQUAL( git_commit_tree( &tree, head ), 0 );
499 std::vector<std::string> committedPaths;
501 tree, GIT_TREEWALK_PRE,
502 [](
const char* root,
const git_tree_entry* entry,
void* payload ) ->
int
504 auto* paths =
static_cast<std::vector<std::string>*
>( payload );
506 if( git_tree_entry_type( entry ) == GIT_OBJECT_BLOB )
507 paths->push_back( std::string( root ) + git_tree_entry_name( entry ) );
513 git_tree_free( tree );
514 git_commit_free( head );
515 git_repository_free( repo );
517 auto contains = [&](
const std::string& s )
519 return std::find( committedPaths.begin(), committedPaths.end(), s ) != committedPaths.end();
522 BOOST_CHECK_MESSAGE( contains(
"p.kicad_pro" ),
"kicad_pro must be committed" );
523 BOOST_CHECK_MESSAGE( contains(
"p.kicad_pcb" ),
"kicad_pcb must be committed" );
524 BOOST_CHECK_MESSAGE( contains(
"p.kicad_sch" ),
"kicad_sch must be committed" );
526 BOOST_CHECK_MESSAGE( !contains(
"passwords.txt" ),
"passwords.txt must NOT appear in history" );
527 BOOST_CHECK_MESSAGE( !contains(
"datasheet.pdf" ),
"datasheet.pdf must NOT appear in history" );
528 BOOST_CHECK_MESSAGE( !contains(
"notes.md" ),
"notes.md must NOT appear in history" );
529 BOOST_CHECK_MESSAGE( !contains(
"docs/manual.txt" ),
"subdirectory user content must NOT appear in history" );
539 LIBGIT2_SCOPE libgit;
542 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
543 backupEnabled =
true;
548 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"p.kicad_pro" ), wxS(
"{}\n" ) );
549 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"p.kicad_pcb" ),
550 wxS(
"(kicad_pcb (version 20240108))\n" ) );
555 std::string inMemoryContent =
"(kicad_pcb (version 20240108))\n";
557 auto saver = [&inMemoryContent](
const wxString&, std::vector<HISTORY_FILE_DATA>& aFileData )
561 entry.
content = inMemoryContent;
562 aFileData.push_back( std::move( entry ) );
571 wxString histDir =
path + wxFileName::GetPathSeparator() + wxS(
".history" );
572 BOOST_CHECK( wxDirExists( histDir ) );
575 BOOST_CHECK_MESSAGE( head.IsEmpty(),
"no untagged HEAD should exist after an idle first save" );
578 inMemoryContent =
"(kicad_pcb (version 20240108) (edited yes))\n";
584 BOOST_CHECK_MESSAGE( !head.IsEmpty(),
"in-memory edits diverging from disk must produce a commit" );
598 LIBGIT2_SCOPE libgit;
601 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
602 backupEnabled =
true;
606 const wxString sep = wxFileName::GetPathSeparator();
608 writeTextFile(
path + sep + wxS(
"p.kicad_pro" ), wxS(
"{}\n" ) );
609 writeTextFile(
path + sep + wxS(
"p.kicad_pcb" ), wxS(
"(kicad_pcb (version 20240108))\n" ) );
615 auto runCount = std::make_shared<int>( 0 );
616 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
619 [runCount](
const wxString&, std::vector<HISTORY_FILE_DATA>& aFileData )
623 HISTORY_FILE_DATA entry;
624 entry.relativePath = wxS(
"p.kicad_pcb" );
625 entry.content =
"(kicad_pcb (version 20240108) (edited yes))\n";
626 aFileData.push_back( std::move( entry ) );
628 board->GetHistoryLifetimeToken() );
640 BOOST_CHECK_MESSAGE( *runCount == 1,
"commit runner invoked a saver whose board was destroyed" );
643 BOOST_CHECK_MESSAGE( *runCount == 1,
"autosave-file runner invoked a saver whose board was destroyed" );
654 LIBGIT2_SCOPE libgit;
657 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
658 backupEnabled =
true;
663 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"p.kicad_pro" ), wxS(
"{}\n" ) );
664 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"p.kicad_pcb" ),
665 wxS(
"(kicad_pcb (version 20240108))\n" ) );
669 std::string inMemoryContent =
"(kicad_pcb (version 20240108))\n";
671 auto saver = [&inMemoryContent](
const wxString&, std::vector<HISTORY_FILE_DATA>& aFileData )
675 entry.
content = inMemoryContent;
676 aFileData.push_back( std::move( entry ) );
685 BOOST_CHECK_MESSAGE( !head.IsEmpty(),
"manual save on a fresh project must commit even when staged matches disk" );
696 LIBGIT2_SCOPE libgit;
699 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
700 backupEnabled =
true;
703 SCOPED_BACKUP_FORMAT_OVERRIDE restoreFormat( format );
709 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"p.kicad_pro" ), wxS(
"{}\n" ) );
710 writeTextFile(
path + wxFileName::GetPathSeparator() + wxS(
"p.kicad_pcb" ),
711 wxS(
"(kicad_pcb (version 20240108))\n" ) );
715 auto saver = [](
const wxString&, std::vector<HISTORY_FILE_DATA>& aFileData )
719 entry.
content =
"(kicad_pcb (version 20240108) (edited yes))\n";
720 aFileData.push_back( std::move( entry ) );
729 "zip backup format must not create incremental autosave commits" );
740 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
741 backupEnabled =
true;
744 SCOPED_BACKUP_FORMAT_OVERRIDE restoreFormat( format );
748 SCOPED_BACKUP_LOCATION_OVERRIDE restoreLocation(
location );
753 const wxString sep = wxFileName::GetPathSeparator();
755 writeTextFile(
path + sep + wxS(
"p.kicad_pro" ), wxS(
"{}\n" ) );
758 SCOPED_PROJECT_LOAD loadedProject( mgr,
path + sep + wxS(
"p.kicad_pro" ) );
762 auto saver = [](
const wxString&, std::vector<HISTORY_FILE_DATA>& aFileData )
766 entry.
content =
"(kicad_pcb (version 20240108) (edited yes))\n";
767 aFileData.push_back( std::move( entry ) );
774 wxString autosavePath =
path + sep + wxS(
"_autosave-p.kicad_pcb" );
775 BOOST_CHECK_MESSAGE( wxFileExists( autosavePath ),
776 "zip backup format must write autosave recovery files" );
787 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
788 backupEnabled =
true;
791 SCOPED_BACKUP_LOCATION_OVERRIDE restoreLocation(
location );
796 const wxString sep = wxFileName::GetPathSeparator();
800 writeTextFile(
path + sep + wxS(
"p.kicad_pro" ), wxS(
"{}\n" ) );
803 SCOPED_PROJECT_LOAD loadedProject( mgr,
path + sep + wxS(
"p.kicad_pro" ) );
805 const wxString sourcePath =
path + sep + wxS(
"p.kicad_pcb" );
806 const wxString autosavePath =
path + sep + wxS(
"_autosave-p.kicad_pcb" );
807 const wxString boardContent = wxS(
"(kicad_pcb (version 20240108))\n" );
810 writeTextFile( sourcePath, boardContent );
811 writeTextFile( autosavePath, boardContent );
816 wxDateTime srcMtime = wxFileName( sourcePath ).GetModificationTime();
817 wxDateTime newerMtime = srcMtime + wxTimeSpan::Seconds( 60 );
818 BOOST_REQUIRE( wxFileName( autosavePath ).SetTimes( &newerMtime, &newerMtime,
nullptr ) );
820 std::vector<wxString> exts{ wxS(
"kicad_pcb" ) };
823 BOOST_CHECK_MESSAGE( stale.empty(),
824 "Content-identical autosave with newer mtime was flagged stale, "
825 "triggering a spurious recovery prompt (issue 24126)" );
834 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
835 backupEnabled =
true;
838 SCOPED_BACKUP_LOCATION_OVERRIDE restoreLocation(
location );
843 const wxString sep = wxFileName::GetPathSeparator();
845 writeTextFile(
path + sep + wxS(
"p.kicad_pro" ), wxS(
"{}\n" ) );
848 SCOPED_PROJECT_LOAD loadedProject( mgr,
path + sep + wxS(
"p.kicad_pro" ) );
850 const wxString sourcePath =
path + sep + wxS(
"p.kicad_pcb" );
851 const wxString autosavePath =
path + sep + wxS(
"_autosave-p.kicad_pcb" );
854 writeTextFile( sourcePath, wxS(
"(kicad_pcb (version 20240108))\n" ) );
855 writeTextFile( autosavePath, wxS(
"(kicad_pcb (version 20240108) (edited yes))\n" ) );
859 wxDateTime srcMtime = wxFileName( sourcePath ).GetModificationTime();
860 wxDateTime newerMtime = srcMtime + wxTimeSpan::Seconds( 60 );
861 BOOST_REQUIRE( wxFileName( autosavePath ).SetTimes( &newerMtime, &newerMtime,
nullptr ) );
863 std::vector<wxString> exts{ wxS(
"kicad_pcb" ) };
866 BOOST_CHECK_MESSAGE( stale.size() == 1,
867 "Genuinely divergent autosave must still be flagged stale for recovery" );
873 LIBGIT2_SCOPE libgit;
876 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
877 backupEnabled =
true;
881 const wxString sep = wxFileName::GetPathSeparator();
883 writeTextFile(
path + sep + wxS(
"trim.kicad_pro" ), wxS(
"{}\n" ) );
884 writeTextFile(
path + sep + wxS(
"trim.kicad_pcb" ), wxS(
"(kicad_pcb (version 20240108))\n" ) );
885 writeTextFile(
path + sep + wxS(
"trim.kicad_sch" ), wxS(
"(kicad_sch (version 20240108))\n" ) );
890 writeTextFile(
path + sep + wxS(
"trim.kicad_pcb" ), wxS(
"(kicad_pcb (version 20240108) (net 1))\n" ) );
895 writeTextFile(
path + sep + wxS(
"trim.kicad_sch" ), wxS(
"(kicad_sch (version 20240108) (mod))\n" ) );
898 wxString historyDir =
path + sep + wxS(
".history" );
899 git_repository* repo =
nullptr;
900 BOOST_REQUIRE( git_repository_open( &repo, historyDir.mb_str().data() ) == 0 );
903 git_commit* head =
nullptr;
904 git_tree* tree =
nullptr;
905 BOOST_REQUIRE( git_reference_name_to_id( &headOid, repo,
"HEAD" ) == 0 );
906 BOOST_REQUIRE( git_commit_lookup( &head, repo, &headOid ) == 0 );
909 BOOST_CHECK_MESSAGE( git_tree_entry_byname( tree,
"trim.kicad_sch" ),
910 "schematic missing from snapshot after trim" );
911 BOOST_CHECK_MESSAGE( git_tree_entry_byname( tree,
"trim.kicad_pcb" ),
"board dropped from snapshot after trim" );
912 BOOST_CHECK_MESSAGE( git_tree_entry_byname( tree,
"trim.kicad_pro" ),
913 "project file dropped from snapshot after trim" );
915 git_tree_free( tree );
916 git_commit_free( head );
917 git_repository_free( repo );
Information pertinent to a Pcbnew printed circuit board.
void SaveToHistory(const wxString &aProjectPath, std::vector< HISTORY_FILE_DATA > &aFileData)
Serialize board into HISTORY_FILE_DATA for non-blocking history commit.
void SetProject(PROJECT *aProject, bool aReferenceOnly=false)
Link a board to a given project.
const wxString & GetFileName() const
PROJECT * GetProject() const
wxString PathStr() const
Get the path to the temporary directory as a wxString.
Simple local history manager built on libgit2.
bool EnforceSizeLimit(const wxString &aProjectPath, size_t aMaxBytes, PROGRESS_REPORTER *aReporter=nullptr)
Enforce total size limit by rebuilding trimmed history keeping newest commits whose cumulative unique...
bool TagSave(const wxString &aProjectPath, const wxString &aFileType)
Tag a manual save in the local history repository.
bool RunRegisteredSaversAndCommit(const wxString &aProjectPath, const wxString &aTitle, const wxString &aTagFileType=wxEmptyString)
Run all registered savers and, if any staged changes differ from HEAD, create a commit.
std::vector< std::pair< wxString, wxString > > FindStaleAutosaveFiles(const wxString &aProjectPath, const std::vector< wxString > &aExtensions) const
Enumerate autosave files newer than their corresponding source files for the project at aProjectPath,...
wxString GetHeadHash(const wxString &aProjectPath)
Return the current head commit hash.
void WaitForPendingSave()
Block until any pending background save completes.
bool RestoreCommit(const wxString &aProjectPath, const wxString &aHash, wxWindow *aParent=nullptr, bool aConfirm=true)
Restore the project files to the state recorded by the given commit hash.
bool Init(const wxString &aProjectPath)
Initialize the local history repository for the given project path.
bool CommitSnapshot(const std::vector< wxString > &aFiles, const wxString &aTitle)
Commit the given files to the local history repository.
bool RunRegisteredSaversAsAutosaveFiles(const wxString &aProjectPath)
Run all registered savers and write their output to autosave files instead of committing to the local...
bool CommitFullProjectSnapshot(const wxString &aProjectPath, const wxString &aTitle)
Commit a snapshot of the entire project directory (excluding the .history directory and ignored trans...
void RegisterSaver(const void *aSaverObject, const std::function< void(const wxString &, std::vector< HISTORY_FILE_DATA > &)> &aSaver, const std::weak_ptr< void > &aLifetime={})
Register a saver callback invoked during autosave history commits.
void UnregisterSaver(const void *aSaverObject)
Unregister a previously registered saver callback.
virtual COMMON_SETTINGS * GetCommonSettings() const
virtual SETTINGS_MANAGER & GetSettingsManager() const
virtual const wxString GetProjectPath() const
Return the full path of the project.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
@ ZIP
Zip archive snapshots; autosave uses recovery files.
@ PROJECT_DIR
Inside the project directory (default)
PGM_BASE & Pgm()
The global program "get" accessor.
BACKUP_LOCATION location
Where backups, history, and autosave files live.
BACKUP_FORMAT format
Backup format (incremental git history vs zip archives)
bool enabled
Automatically back up the project when files are saved.
Data produced by a registered saver on the UI thread, consumed by either the background local-history...
std::string content
Serialized content (mutually exclusive with sourcePath)
wxString relativePath
Destination path relative to the project root.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(SaveToHistoryWithNullProjectDoesNotCrash)
Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23737.
BOOST_CHECK_EQUAL(result, "25.4")