KiCad PCB EDA Suite
Loading...
Searching...
No Matches
kicad_git_common.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include "kicad_git_common.h"
25
26#include <kiplatform/secrets.h>
27
28#include <wx/filename.h>
29#include <wx/log.h>
30#include <wx/textfile.h>
31#include <wx/utils.h>
32#include <map>
33#include <vector>
34
35KIGIT_COMMON::KIGIT_COMMON( git_repository* aRepo ) :
36 m_repo( aRepo ), m_connType( GIT_CONN_TYPE::GIT_CONN_LOCAL ), m_testedTypes( 0 )
37{}
38
39
41{}
42
43
44git_repository* KIGIT_COMMON::GetRepo() const
45{
46 return m_repo;
47}
48
50{
51 wxCHECK( m_repo, wxEmptyString );
52 git_reference* head = nullptr;
53
54 int retval = git_repository_head( &head, m_repo );
55
56 if( retval && retval != GIT_EUNBORNBRANCH && retval != GIT_ENOTFOUND )
57 return wxEmptyString;
58
59 git_reference *branch;
60
61 if( git_reference_resolve( &branch, head ) )
62 {
63 git_reference_free( head );
64 return wxEmptyString;
65 }
66
67 git_reference_free( head );
68 const char* branchName = "";
69
70 if( git_branch_name( &branchName, branch ) )
71 {
72 git_reference_free( branch );
73 return wxEmptyString;
74 }
75
76 git_reference_free( branch );
77
78 return wxString( branchName );
79}
80
81
82std::vector<wxString> KIGIT_COMMON::GetBranchNames() const
83{
84 if( !m_repo )
85 return {};
86
87 std::vector<wxString> branchNames;
88 std::map<git_time_t, wxString> branchNamesMap;
89 wxString firstName;
90
91 git_branch_iterator* branchIterator = nullptr;
92
93 if( git_branch_iterator_new( &branchIterator, m_repo, GIT_BRANCH_LOCAL ) )
94 return branchNames;
95
96 git_reference* branchReference = nullptr;
97 git_branch_t branchType;
98
99 while( git_branch_next( &branchReference, &branchType, branchIterator ) != GIT_ITEROVER )
100 {
101 const char* branchName = "";
102
103 if( git_branch_name( &branchName, branchReference ) )
104 continue;
105
106 const git_oid* commitId = git_reference_target( branchReference );
107
108 git_commit* commit = nullptr;
109
110 if( git_commit_lookup( &commit, m_repo, commitId ) )
111 continue;
112
113 git_time_t commitTime = git_commit_time( commit );
114
115 if( git_branch_is_head( branchReference ) )
116 firstName = branchName;
117 else
118 branchNamesMap.emplace( commitTime, branchName );
119
120 git_commit_free( commit );
121 git_reference_free( branchReference );
122 }
123
124 git_branch_iterator_free( branchIterator );
125
126 // Add the current branch to the top of the list
127 if( !firstName.IsEmpty() )
128 branchNames.push_back( firstName );
129
130 // Add the remaining branches in order from newest to oldest
131 for( auto rit = branchNamesMap.rbegin(); rit != branchNamesMap.rend(); ++rit )
132 branchNames.push_back( rit->second );
133
134 return branchNames;
135}
136
137
138std::vector<wxString> KIGIT_COMMON::GetProjectDirs()
139{
140 wxCHECK( m_repo, {} );
141 std::vector<wxString> projDirs;
142
143 git_oid oid;
144 git_commit* commit;
145 git_tree *tree;
146
147 if( git_reference_name_to_id( &oid, m_repo, "HEAD" ) != GIT_OK )
148 {
149 wxLogError( "An error occurred: %s", git_error_last()->message );
150 return projDirs;
151 }
152
153 if( git_commit_lookup( &commit, m_repo, &oid ) != GIT_OK )
154 {
155 wxLogError( "An error occurred: %s", git_error_last()->message );
156 return projDirs;
157 }
158
159 if( git_commit_tree( &tree, commit ) != GIT_OK )
160 {
161 wxLogError( "An error occurred: %s", git_error_last()->message );
162 return projDirs;
163 }
164
165 // Define callback
166 git_tree_walk(
167 tree, GIT_TREEWALK_PRE,
168 []( const char* root, const git_tree_entry* entry, void* payload )
169 {
170 std::vector<wxString>* prjs = static_cast<std::vector<wxString>*>( payload );
171 wxFileName root_fn( git_tree_entry_name( entry ) );
172
173 root_fn.SetPath( root );
174
175 if( git_tree_entry_type( entry ) == GIT_OBJECT_BLOB
176 && ( ( root_fn.GetExt() == "kicad_pro" ) || ( root_fn.GetExt() == "pro" ) ) )
177 {
178 prjs->push_back( root_fn.GetFullPath() );
179 }
180
181 return 0; // continue walking
182 },
183 &projDirs );
184
185 git_tree_free( tree );
186 git_commit_free( commit );
187
188 std::sort( projDirs.begin(), projDirs.end(),
189 []( const wxString& a, const wxString& b )
190 {
191 int a_freq = a.Freq( wxFileName::GetPathSeparator() );
192 int b_freq = b.Freq( wxFileName::GetPathSeparator() );
193
194 if( a_freq == b_freq )
195 return a < b;
196 else
197 return a_freq < b_freq;
198
199 } );
200
201 return projDirs;
202}
203
204
205std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles() const
206{
207 auto get_modified_files = [&]( git_oid* from_oid, git_oid* to_oid ) -> std::set<wxString>
208 {
209 std::set<wxString> modified_set;
210 git_revwalk* walker = nullptr;
211
212 if( git_revwalk_new( &walker, m_repo ) != GIT_OK )
213 return modified_set;
214
215 if( ( git_revwalk_push( walker, from_oid ) != GIT_OK )
216 || ( git_revwalk_hide( walker, to_oid ) != GIT_OK ) )
217 {
218 git_revwalk_free( walker );
219 return modified_set;
220 }
221
222 git_oid oid;
223 git_commit* commit;
224
225 // iterate over all local commits not in remote
226 while( git_revwalk_next( &oid, walker ) == GIT_OK )
227 {
228 if( git_commit_lookup( &commit, m_repo, &oid ) != GIT_OK )
229 continue;
230
231 git_tree *tree, *parent_tree = nullptr;
232 if( git_commit_tree( &tree, commit ) != GIT_OK )
233 {
234 git_commit_free( commit );
235 continue;
236 }
237
238 // get parent commit tree to diff against
239 if( !git_commit_parentcount( commit ) )
240 {
241 git_tree_free( tree );
242 git_commit_free( commit );
243 continue;
244 }
245
246
247 git_commit* parent;
248 if( git_commit_parent( &parent, commit, 0 ) != GIT_OK )
249 {
250 git_tree_free( tree );
251 git_commit_free( commit );
252 continue;
253 }
254
255
256 if( git_commit_tree( &parent_tree, parent ) != GIT_OK )
257 {
258 git_tree_free( tree );
259 git_commit_free( commit );
260 git_commit_free( parent );
261 continue;
262 }
263
264
265 git_diff* diff;
266 git_diff_options diff_opts;
267 git_diff_init_options( &diff_opts, GIT_DIFF_OPTIONS_VERSION );
268
269 if( git_diff_tree_to_tree( &diff, m_repo, parent_tree, tree, &diff_opts ) == GIT_OK )
270 {
271 size_t num_deltas = git_diff_num_deltas( diff );
272
273 for( size_t i = 0; i < num_deltas; ++i )
274 {
275 const git_diff_delta* delta = git_diff_get_delta( diff, i );
276 modified_set.insert( delta->new_file.path );
277 }
278
279 git_diff_free( diff );
280 }
281
282 git_tree_free( parent_tree );
283 git_commit_free( parent );
284 git_tree_free( tree );
285 git_commit_free( commit );
286 }
287
288 git_revwalk_free( walker );
289
290 return modified_set;
291 };
292
293 std::pair<std::set<wxString>,std::set<wxString>> modified_files;
294
295 if( !m_repo )
296 return modified_files;
297
298 git_reference* head = nullptr;
299 git_reference* remote_head = nullptr;
300
301 if( git_repository_head( &head, m_repo ) != GIT_OK )
302 return modified_files;
303
304 if( git_branch_upstream( &remote_head, head ) != GIT_OK )
305 {
306 git_reference_free( head );
307 return modified_files;
308 }
309
310 git_oid head_oid = *git_reference_target( head );
311 git_oid remote_oid = *git_reference_target( remote_head );
312
313 git_reference_free( head );
314 git_reference_free( remote_head );
315
316 modified_files.first = get_modified_files( &head_oid, &remote_oid );
317 modified_files.second = get_modified_files( &remote_oid, &head_oid );
318
319 return modified_files;
320}
321
322
324{
325 if( !m_repo )
326 return false;
327
328 git_reference* head = nullptr;
329 git_reference* remote_head = nullptr;
330
331 if( git_repository_head( &head, m_repo ) != GIT_OK )
332 return false;
333
334 if( git_branch_upstream( &remote_head, head ) != GIT_OK )
335 {
336 git_reference_free( head );
337 return false;
338 }
339
340 git_oid head_oid = *git_reference_target( head );
341 git_oid remote_oid = *git_reference_target( remote_head );
342
343 git_reference_free( head );
344 git_reference_free( remote_head );
345
346 git_revwalk* walker = nullptr;
347
348 if( git_revwalk_new( &walker, m_repo ) != GIT_OK )
349 return false;
350
351 if( ( git_revwalk_push( walker, &head_oid ) != GIT_OK )
352 || ( git_revwalk_hide( walker, &remote_oid ) != GIT_OK ) )
353 {
354 git_revwalk_free( walker );
355 return false;
356 }
357
358 git_oid oid;
359
360 // If we can't walk to the next commit, then we are at or behind the remote
361 if( git_revwalk_next( &oid, walker ) != GIT_OK )
362 {
363 git_revwalk_free( walker );
364 return false;
365 }
366
367 git_revwalk_free( walker );
368 return true;
369}
370
371
373{
374 wxCHECK( m_repo, false );
375 git_remote* remote = nullptr;
376
377 if( git_remote_lookup( &remote, m_repo, "origin" ) != GIT_OK )
378 {
379 return false;
380 }
381
382 // Get the URLs associated with the remote
383 const char* fetch_url = git_remote_url( remote );
384 const char* push_url = git_remote_pushurl( remote );
385
386 // If no push URL is set, libgit2 defaults to using the fetch URL for pushing
387 if( !push_url )
388 {
389 push_url = fetch_url;
390 }
391
392 // Clean up the remote object
393 git_remote_free( remote );
394
395 // Check if both URLs are valid (i.e., not NULL)
396 return fetch_url && push_url;
397}
398
399
401{
402 wxCHECK( m_repo, wxEmptyString );
403
404 wxString retval;
405 git_reference* head = nullptr;
406 git_reference* upstream = nullptr;
407
408 if( git_repository_head( &head, m_repo ) != GIT_OK )
409 return retval;
410
411 if( git_branch_upstream( &upstream, head ) == GIT_OK )
412 {
413 git_buf remote_name = GIT_BUF_INIT_CONST( nullptr, 0 );
414
415 if( git_branch_remote_name( &remote_name, m_repo, git_reference_name( upstream ) ) == GIT_OK )
416 {
417 retval = remote_name.ptr;
418 git_buf_dispose( &remote_name );
419 }
420
421 git_reference_free( upstream );
422 }
423
424 git_reference_free( head );
425
426 return retval;
427}
428
429void KIGIT_COMMON::SetSSHKey( const wxString& aKey )
430{
431 auto it = std::find( m_publicKeys.begin(), m_publicKeys.end(), aKey );
432
433 if( it != m_publicKeys.end() )
434 m_publicKeys.erase( it );
435
436 m_publicKeys.insert( m_publicKeys.begin(), aKey );
437}
438
439
441{
442 if( !m_repo )
443 return wxEmptyString;
444
445 const char *path = git_repository_path( m_repo );
446 wxString retval = path;
447 return retval;
448}
449
450
452{
453 m_publicKeys.clear();
454
455 wxFileName keyFile( wxGetHomeDir(), wxEmptyString );
456 keyFile.AppendDir( ".ssh" );
457 keyFile.SetFullName( "id_rsa" );
458
459 if( keyFile.FileExists() )
460 m_publicKeys.push_back( keyFile.GetFullPath() );
461
462 keyFile.SetFullName( "id_dsa" );
463
464 if( keyFile.FileExists() )
465 m_publicKeys.push_back( keyFile.GetFullPath() );
466
467 keyFile.SetFullName( "id_ecdsa" );
468
469 if( keyFile.FileExists() )
470 m_publicKeys.push_back( keyFile.GetFullPath() );
471
472 keyFile.SetFullName( "id_ed25519" );
473
474 if( keyFile.FileExists() )
475 m_publicKeys.push_back( keyFile.GetFullPath() );
476
477 // Parse SSH config file for hostname information
478 wxFileName sshConfig( wxGetHomeDir(), wxEmptyString );
479 sshConfig.AppendDir( ".ssh" );
480 sshConfig.SetFullName( "config" );
481
482 if( sshConfig.FileExists() )
483 {
484 wxTextFile configFile( sshConfig.GetFullPath() );
485 configFile.Open();
486
487 bool match = false;
488
489 for( wxString line = configFile.GetFirstLine(); !configFile.Eof(); line = configFile.GetNextLine() )
490 {
491 line.Trim( false ).Trim( true );
492
493 if( line.StartsWith( "Host " ) )
494 match = false;
495
496 // The difference here is that we are matching either "Hostname" or "Host" to get the
497 // match. This is because in the absence of a "Hostname" line, the "Host" line is used
498 if( line.StartsWith( "Host" ) && line.Contains( m_hostname ) )
499 match = true;
500
501 if( match && line.StartsWith( "IdentityFile" ) )
502 {
503 wxString keyPath = line.AfterFirst( ' ' ).Trim( false ).Trim( true );
504
505 // Expand ~ to home directory if present
506 if( keyPath.StartsWith( "~" ) )
507 keyPath.Replace( "~", wxGetHomeDir(), false );
508
509 // Add the public key to the beginning of the list
510 if( wxFileName::FileExists( keyPath ) )
511 SetSSHKey( keyPath );
512 }
513 }
514
515 configFile.Close();
516 }
517}
518
519
521{
522 wxCHECK( m_repo, /* void */ );
523
524 // We want to get the current branch's upstream url as well as the stored password
525 // if one exists given the url and username.
526
527 wxString remote_name = GetRemotename();
528 git_remote* remote = nullptr;
529
530 if( git_remote_lookup( &remote, m_repo, remote_name.ToStdString().c_str() ) == GIT_OK )
531 {
532 const char* url = git_remote_url( remote );
533
534 if( url )
535 m_remote = url;
536
537 git_remote_free( remote );
538 }
539
540 // Find the stored password if it exists
542
545}
546
548{
549 if( m_remote.StartsWith( "https://" ) || m_remote.StartsWith( "http://" ) )
551 else if( m_remote.StartsWith( "ssh://" ) || m_remote.StartsWith( "git@" ) || m_remote.StartsWith( "git+ssh://" ) )
553 else
555
557 {
558 wxString uri = m_remote;
559 size_t atPos = uri.find( '@' );
560
561 if( atPos != wxString::npos )
562 {
563 size_t protoEnd = uri.find( "//" );
564
565 if( protoEnd != wxString::npos )
566 {
567 wxString credentials = uri.Mid( protoEnd + 2, atPos - protoEnd - 2 );
568 size_t colonPos = credentials.find( ':' );
569
570 if( colonPos != wxString::npos )
571 {
572 m_username = credentials.Left( colonPos );
573 m_password = credentials.Mid( colonPos + 1, credentials.Length() - colonPos - 1 );
574 }
575 else
576 {
577 m_username = credentials;
578 }
579 }
580 else
581 {
582 m_username = uri.Left( atPos );
583 }
584 }
585
586 if( m_remote.StartsWith( "git@" ) )
587 {
588 // SSH format: git@hostname:path
589 size_t colonPos = m_remote.find( ':' );
590 if( colonPos != wxString::npos )
591 m_hostname = m_remote.Mid( 4, colonPos - 4 );
592 }
593 else
594 {
595 // other URL format: proto://[user@]hostname/path
596 size_t hostStart = m_remote.find( "://" ) + 2;
597 size_t hostEnd = m_remote.find( '/', hostStart );
598 wxString host;
599
600 if( hostEnd != wxString::npos )
601 host = m_remote.Mid( hostStart, hostEnd - hostStart );
602 else
603 host = m_remote.Mid( hostStart );
604
605 atPos = host.find( '@' );
606
607 if( atPos != wxString::npos )
608 m_hostname = host.Mid( atPos + 1 );
609 else
610 m_hostname = host;
611 }
612 }
613}
614
615
616extern "C" int fetchhead_foreach_cb( const char*, const char*,
617 const git_oid* aOID, unsigned int aIsMerge, void* aPayload )
618{
619 if( aIsMerge )
620 git_oid_cpy( (git_oid*) aPayload, aOID );
621
622 return 0;
623}
624
625
626extern "C" void clone_progress_cb( const char* aStr, size_t aLen, size_t aTotal, void* data )
627{
628 KIGIT_COMMON* parent = (KIGIT_COMMON*) data;
629
630 wxString progressMessage( aStr );
631 parent->UpdateProgress( aLen, aTotal, progressMessage );
632}
633
634
635extern "C" int progress_cb( const char* str, int len, void* data )
636{
637 KIGIT_COMMON* parent = (KIGIT_COMMON*) data;
638
639 wxString progressMessage( str, len );
640 parent->UpdateProgress( 0, 0, progressMessage );
641
642 return 0;
643}
644
645
646extern "C" int transfer_progress_cb( const git_transfer_progress* aStats, void* aPayload )
647{
648 KIGIT_COMMON* parent = (KIGIT_COMMON*) aPayload;
649 wxString progressMessage = wxString::Format( _( "Received %u of %u objects" ),
650 aStats->received_objects,
651 aStats->total_objects );
652
653 parent->UpdateProgress( aStats->received_objects, aStats->total_objects, progressMessage );
654
655 return 0;
656}
657
658
659extern "C" int update_cb( const char* aRefname, const git_oid* aFirst, const git_oid* aSecond,
660 void* aPayload )
661{
662 constexpr int cstring_len = 8;
663 char a_str[cstring_len + 1];
664 char b_str[cstring_len + 1];
665
666 KIGIT_COMMON* parent = (KIGIT_COMMON*) aPayload;
667 wxString status;
668
669 git_oid_tostr( b_str, cstring_len, aSecond );
670
671#if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 )
672 if( !git_oid_is_zero( aFirst ) )
673#else
674 if( !git_oid_iszero( aFirst ) )
675#endif
676 {
677 git_oid_tostr( a_str, cstring_len, aFirst );
678 status = wxString::Format( _( "* [updated] %s..%s %s" ), a_str, b_str, aRefname );
679 }
680 else
681 {
682 status = wxString::Format( _( "* [new] %s %s" ), b_str, aRefname );
683 }
684
685 parent->UpdateProgress( 0, 0, status );
686
687 return 0;
688}
689
690
691extern "C" int push_transfer_progress_cb( unsigned int aCurrent, unsigned int aTotal, size_t aBytes,
692 void* aPayload )
693{
694 long long progress = 100;
695 KIGIT_COMMON* parent = (KIGIT_COMMON*) aPayload;
696
697 if( aTotal != 0 )
698 {
699 progress = ( aCurrent * 100ll ) / aTotal;
700 }
701
702 wxString progressMessage = wxString::Format( _( "Writing objects: %lld%% (%u/%u), %zu bytes" ),
703 progress, aCurrent, aTotal, aBytes );
704 parent->UpdateProgress( aCurrent, aTotal, progressMessage );
705
706 return 0;
707}
708
709
710extern "C" int push_update_reference_cb( const char* aRefname, const char* aStatus, void* aPayload )
711{
712 KIGIT_COMMON* parent = (KIGIT_COMMON*) aPayload;
713 wxString status( aStatus );
714
715 if( !status.IsEmpty() )
716 {
717 wxString statusMessage = wxString::Format( _( "* [rejected] %s (%s)" ), aRefname, aStatus );
718 parent->UpdateProgress( 0, 0, statusMessage );
719 }
720 else
721 {
722 wxString statusMessage = wxString::Format( _( "[updated] %s" ), aRefname );
723 parent->UpdateProgress( 0, 0, statusMessage );
724 }
725
726 return 0;
727}
728
729
730extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aUsername,
731 unsigned int aAllowedTypes, void* aPayload )
732{
733 KIGIT_COMMON* parent = static_cast<KIGIT_COMMON*>( aPayload );
734
736 return GIT_PASSTHROUGH;
737
738 if( aAllowedTypes & GIT_CREDTYPE_USERNAME
739 && !( parent->TestedTypes() & GIT_CREDTYPE_USERNAME ) )
740 {
741 wxString username = parent->GetUsername().Trim().Trim( false );
742 git_cred_username_new( aOut, username.ToStdString().c_str() );
743 parent->TestedTypes() |= GIT_CREDTYPE_USERNAME;
744 }
746 && ( aAllowedTypes & GIT_CREDTYPE_USERPASS_PLAINTEXT )
747 && !( parent->TestedTypes() & GIT_CREDTYPE_USERPASS_PLAINTEXT ) )
748 {
749 wxString username = parent->GetUsername().Trim().Trim( false );
750 wxString password = parent->GetPassword().Trim().Trim( false );
751
752 git_cred_userpass_plaintext_new( aOut, username.ToStdString().c_str(),
753 password.ToStdString().c_str() );
754 parent->TestedTypes() |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
755 }
757 && ( aAllowedTypes & GIT_CREDTYPE_SSH_KEY )
758 && !( parent->TestedTypes() & GIT_CREDTYPE_SSH_KEY ) )
759 {
760 // SSH key authentication
761 wxString sshKey = parent->GetNextPublicKey();
762
763 if( sshKey.IsEmpty() )
764 {
765 parent->TestedTypes() |= GIT_CREDTYPE_SSH_KEY;
766 return GIT_PASSTHROUGH;
767 }
768
769 wxString sshPubKey = sshKey + ".pub";
770 wxString username = parent->GetUsername().Trim().Trim( false );
771 wxString password = parent->GetPassword().Trim().Trim( false );
772
773 git_cred_ssh_key_new( aOut, username.ToStdString().c_str(),
774 sshPubKey.ToStdString().c_str(),
775 sshKey.ToStdString().c_str(),
776 password.ToStdString().c_str() );
777 }
778 else
779 {
780 return GIT_PASSTHROUGH;
781 }
782
783 return GIT_OK;
784};
wxString m_username
git_repository * m_repo
std::vector< wxString > GetBranchNames() const
void updateConnectionType()
wxString GetCurrentBranchName() const
wxString GetGitRootDirectory() const
void SetSSHKey(const wxString &aSSHKey)
std::vector< wxString > GetProjectDirs()
Return a vector of project files in the repository.
GIT_CONN_TYPE GetConnType() const
wxString GetPassword() const
git_repository * GetRepo() const
wxString m_hostname
wxString m_password
wxString GetNextPublicKey()
KIGIT_COMMON(git_repository *aRepo)
std::pair< std::set< wxString >, std::set< wxString > > GetDifferentFiles() const
Return a pair of sets of files that differ locally from the remote repository The first set is files ...
std::vector< wxString > m_publicKeys
bool HasPushAndPullRemote() const
wxString GetUsername() const
GIT_CONN_TYPE m_connType
void UpdateCurrentBranchInfo()
bool HasLocalCommits() const
unsigned & TestedTypes()
virtual void UpdateProgress(int aCurrent, int aTotal, const wxString &aMessage)
wxString GetRemotename() const
#define _(s)
void clone_progress_cb(const char *aStr, size_t aLen, size_t aTotal, void *data)
int fetchhead_foreach_cb(const char *, const char *, const git_oid *aOID, unsigned int aIsMerge, void *aPayload)
int push_update_reference_cb(const char *aRefname, const char *aStatus, void *aPayload)
int update_cb(const char *aRefname, const git_oid *aFirst, const git_oid *aSecond, void *aPayload)
int transfer_progress_cb(const git_transfer_progress *aStats, void *aPayload)
int credentials_cb(git_cred **aOut, const char *aUrl, const char *aUsername, unsigned int aAllowedTypes, void *aPayload)
int push_transfer_progress_cb(unsigned int aCurrent, unsigned int aTotal, size_t aBytes, void *aPayload)
int progress_cb(const char *str, int len, void *data)
bool GetSecret(const wxString &aService, const wxString &aKey, wxString &aSecret)
constexpr int delta