34#include <wx/filename.h>
37#include <wx/stdpaths.h>
43 const std::vector<wxString>& aRefList ) :
47 m_listFiles->AppendColumn(
_(
"Path" ), wxLIST_FORMAT_LEFT, 400 );
48 m_listFiles->AppendColumn(
_(
"Status" ), wxLIST_FORMAT_LEFT, 100 );
49 m_listFiles->AppendColumn(
_(
"Old Path" ), wxLIST_FORMAT_LEFT, 200 );
51 for(
const wxString& ref : aRefList )
57 if( !aRefList.empty() )
59 m_comboBase->SetSelection( 0 );
60 m_comboHead->SetSelection( aRefList.size() > 1 ? 1 : 0 );
69 EndModal( wxID_CANCEL );
92 wxMessageBox(
_(
"No git repository available." ),
_(
"Compare Branches" ),
93 wxOK | wxICON_ERROR,
this );
100 if( base.IsEmpty() || head.IsEmpty() )
102 wxMessageBox(
_(
"Select base and head refs to compare." ),
103 _(
"Compare Branches" ), wxOK | wxICON_INFORMATION,
this );
110 [
this](
const wxString& aRef ) ->
bool
117 wxString::Format(
_(
"Could not resolve ref '%s' to a commit or tree." ),
119 _(
"Compare Branches" ), wxOK | wxICON_ERROR,
this );
126 if( !validateRef( base ) || !validateRef( head ) )
133 m_listFiles->InsertItem( 0,
_(
"No changes between selected refs." ) );
141 long idx =
m_listFiles->InsertItem( row++, f.path );
145 if( !f.oldPath.IsEmpty() )
158bool writeRefBlobToFile( git_repository* aRepo,
const wxString& aRef,
const wxString& aPath,
159 const wxString& aDestPath )
161 if( aRef.IsEmpty() || aPath.IsEmpty() )
169 git_tree_entry* entry =
nullptr;
171 if( git_tree_entry_bypath( &entry, treePtr.get(), aPath.ToUTF8().data() ) != 0 )
174 std::unique_ptr<git_tree_entry,
decltype( &git_tree_entry_free )> entryPtr(
175 entry, &git_tree_entry_free );
177 if( git_tree_entry_type( entry ) != GIT_OBJECT_BLOB )
180 git_object* blobObj =
nullptr;
182 if( git_tree_entry_to_object( &blobObj, aRepo, entry ) != 0 )
186 git_blob* blob =
reinterpret_cast<git_blob*
>( blobObj );
188 wxFile out( aDestPath, wxFile::write );
190 if( !out.IsOpened() )
193 const void* raw = git_blob_rawcontent( blob );
194 git_off_t size = git_blob_rawsize( blob );
196 return out.Write( raw,
static_cast<size_t>( size ) ) ==
static_cast<size_t>( size );
208bool isPrettyFootprintChange(
const wxString& aPath )
213 wxFileName fn( aPath );
214 const wxArrayString& dirs = fn.GetDirs();
216 return !dirs.IsEmpty() && dirs.Last().EndsWith( wxS(
".pretty" ) );
228 if( !path.IsEmpty() )
229 wxFileName::Rmdir( path, wxPATH_RMDIR_RECURSIVE );
238 if( aListIndex < 0 || aListIndex >=
static_cast<long>(
m_changedFiles.size() ) )
244 bool prettyFootprint = isPrettyFootprintChange( file.
path );
246 if( prettyFootprint )
251 wxMessageBox( wxString::Format(
_(
"No diff handler for '%s'." ), file.
path ),
252 _(
"Compare File" ), wxOK | wxICON_INFORMATION,
this );
262 const wxString tempBase =
263 wxStandardPaths::Get().GetTempDir() + wxFileName::GetPathSeparator()
264 + wxS(
"kicad_diff_" )
265 + wxString::Format( wxS(
"%lu_" ),
266 static_cast<unsigned long>( wxGetProcessId() ) );
268 const unsigned millis =
static_cast<unsigned>( wxGetLocalTimeMillis().GetLo() );
271 for(
int attempt = 0; attempt < 16 && tmpRoot.IsEmpty(); ++attempt )
273 wxString candidate = tempBase + wxString::Format( wxS(
"%d_%u" ), attempt, millis );
275 if( wxFileName::Mkdir( candidate, 0700, 0 ) )
279 if( tmpRoot.IsEmpty() )
281 wxMessageBox(
_(
"Could not create a temporary directory." ),
_(
"Compare File" ),
282 wxOK | wxICON_ERROR,
this );
286 TempDirGuard tempGuard{ tmpRoot };
295 if( prettyFootprint )
300 wxFileName srcFn( file.
path );
301 const wxString fpFile = srcFn.GetFullName();
303 wxString dirA = tmpRoot + wxFileName::GetPathSeparator() + wxS(
"base.pretty" );
304 wxString dirB = tmpRoot + wxFileName::GetPathSeparator() + wxS(
"head.pretty" );
306 if( hasBase && !wxFileName::Mkdir( dirA, 0700, wxPATH_MKDIR_FULL ) )
311 if( hasHead && !wxFileName::Mkdir( dirB, 0700, wxPATH_MKDIR_FULL ) )
318 wxString blobPath = dirA + wxFileName::GetPathSeparator() + fpFile;
320 if( !writeRefBlobToFile(
m_repo, base, basePath, blobPath ) )
322 wxMessageBox( wxString::Format(
_(
"Could not extract %s from %s." ),
324 _(
"Compare File" ), wxOK | wxICON_ERROR,
this );
333 wxString blobPath = dirB + wxFileName::GetPathSeparator() + fpFile;
335 if( !writeRefBlobToFile(
m_repo, head, file.
path, blobPath ) )
337 wxMessageBox( wxString::Format(
_(
"Could not extract %s from %s." ),
339 _(
"Compare File" ), wxOK | wxICON_ERROR,
this );
348 wxFileName srcName( file.
path );
352 pathA = tmpRoot + wxFileName::GetPathSeparator() + wxS(
"base_" )
353 + srcName.GetFullName();
355 if( !writeRefBlobToFile(
m_repo, base, basePath, pathA ) )
357 wxMessageBox( wxString::Format(
_(
"Could not extract %s from %s." ),
359 _(
"Compare File" ), wxOK | wxICON_ERROR,
this );
366 pathB = tmpRoot + wxFileName::GetPathSeparator() + wxS(
"head_" )
367 + srcName.GetFullName();
369 if( !writeRefBlobToFile(
m_repo, head, file.
path, pathB ) )
371 wxMessageBox( wxString::Format(
_(
"Could not extract %s from %s." ),
373 _(
"Compare File" ), wxOK | wxICON_ERROR,
this );
381 const wxString labelA = wxString::Format( wxS(
"%s:%s" ), base, basePath );
382 const wxString labelB = wxString::Format( wxS(
"%s:%s" ), head, file.
path );
389 wxMessageBox(
reporter.GetMessages(),
_(
"Compare File" ),
390 wxOK | wxICON_INFORMATION,
this );
DIALOG_GIT_MR_REVIEW_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Compare Branches"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(800, 550), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void OnFileActivated(wxListEvent &aEvent) override
DIALOG_GIT_MR_REVIEW(wxWindow *aParent, git_repository *aRepo, const std::vector< wxString > &aRefList)
void OnCompareClick(wxCommandEvent &aEvent) override
std::vector< KIGIT::CHANGED_FILE > m_changedFiles
void openFileDiff(long aListIndex)
void OnClose(wxCloseEvent &aEvent) override
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
A wrapper for reporting to a wxString object.
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...
std::unique_ptr< git_tree, decltype([](git_tree *aTree) { git_tree_free(aTree); })> GitTreePtr
A unique pointer for git_tree objects with automatic cleanup.
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...
git_tree * ResolveRefToTree(git_repository *aRepo, const wxString &aRef)
Resolve a string ref (branch name, short OID, full OID, tag) to its tree.
const char * FileChangeStatusToString(FILE_CHANGE_STATUS aStatus)
std::unique_ptr< git_object, decltype([](git_object *aObject) { git_object_free(aObject); })> GitObjectPtr
A unique pointer for git_object objects with automatic cleanup.
FILE_CHANGE_STATUS status
IbisParser parser & reporter