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#include "kicad_git_memory.h"
26#include "git_repo_mixin.h"
27
28#include <git/git_progress.h>
30#include <kiplatform/secrets.h>
31#include <trace_helpers.h>
32
33#include <git2.h>
34#include <wx/filename.h>
35#include <wx/log.h>
36#include <wx/textfile.h>
37#include <wx/utils.h>
38#include <map>
39#include <vector>
40
41KIGIT_COMMON::KIGIT_COMMON( git_repository* aRepo ) :
43 m_nextPublicKey( 0 ), m_secretFetched( false )
44{}
45
47 // Initialize base class and member variables
48 m_repo( aOther.m_repo ),
49 m_connType( aOther.m_connType ),
50 m_remote( aOther.m_remote ),
51 m_hostname( aOther.m_hostname ),
52 m_username( aOther.m_username ),
53 m_password( aOther.m_password ),
55 // The mutex is default-initialized, not copied
57 m_publicKeys( aOther.m_publicKeys ),
60{
61}
62
65
66
67git_repository* KIGIT_COMMON::GetRepo() const
68{
69 return m_repo;
70}
71
73{
74 wxCHECK( m_repo, wxEmptyString );
75 git_reference* head = nullptr;
76
77 int retval = git_repository_head( &head, m_repo );
78
79 if( retval && retval != GIT_EUNBORNBRANCH && retval != GIT_ENOTFOUND )
80 return wxEmptyString;
81
82 KIGIT::GitReferencePtr headPtr( head );
83 git_reference* branch;
84
85 if( git_reference_resolve( &branch, head ) )
86 {
87 wxLogTrace( traceGit, "Failed to resolve branch" );
88 return wxEmptyString;
89 }
90
91 KIGIT::GitReferencePtr branchPtr( branch );
92 const char* branchName = "";
93
94 if( git_branch_name( &branchName, branch ) )
95 {
96 wxLogTrace( traceGit, "Failed to get branch name" );
97 return wxEmptyString;
98 }
99
100 return wxString( branchName );
101}
102
103
105{
106 if( !m_secretFetched )
107 {
109 {
110 wxString secret;
111
113 m_password = secret;
114 }
115
116 m_secretFetched = true;
117 }
118
119 return m_password;
120}
121
122
123std::vector<wxString> KIGIT_COMMON::GetBranchNames() const
124{
125 if( !m_repo )
126 return {};
127
128 std::vector<wxString> branchNames;
129 std::map<git_time_t, wxString> branchNamesMap;
130 wxString firstName;
131
132 git_branch_iterator* branchIterator = nullptr;
133
134 if( git_branch_iterator_new( &branchIterator, m_repo, GIT_BRANCH_LOCAL ) )
135 {
136 wxLogTrace( traceGit, "Failed to get branch iterator" );
137 return branchNames;
138 }
139
140 KIGIT::GitBranchIteratorPtr branchIteratorPtr( branchIterator );
141 git_reference* branchReference = nullptr;
142 git_branch_t branchType;
143
144 while( git_branch_next( &branchReference, &branchType, branchIterator ) != GIT_ITEROVER )
145 {
146 const char* branchName = "";
147 KIGIT::GitReferencePtr branchReferencePtr( branchReference );
148
149 if( git_branch_name( &branchName, branchReference ) )
150 {
151 wxLogTrace( traceGit, "Failed to get branch name in iter loop" );
152 continue;
153 }
154
155 const git_oid* commitId = git_reference_target( branchReference );
156
157 git_commit* commit = nullptr;
158
159 if( git_commit_lookup( &commit, m_repo, commitId ) )
160 {
161 wxLogTrace( traceGit, "Failed to get commit in iter loop" );
162 continue;
163 }
164
165 KIGIT::GitCommitPtr commitPtr( commit );
166 git_time_t commitTime = git_commit_time( commit );
167
168 if( git_branch_is_head( branchReference ) )
169 firstName = branchName;
170 else
171 branchNamesMap.emplace( commitTime, branchName );
172 }
173
174 // Add the current branch to the top of the list
175 if( !firstName.IsEmpty() )
176 branchNames.push_back( firstName );
177
178 // Add the remaining branches in order from newest to oldest
179 for( auto rit = branchNamesMap.rbegin(); rit != branchNamesMap.rend(); ++rit )
180 branchNames.push_back( rit->second );
181
182 return branchNames;
183}
184
185
186std::vector<wxString> KIGIT_COMMON::GetProjectDirs()
187{
188 wxCHECK( m_repo, {} );
189 std::vector<wxString> projDirs;
190
191 git_oid oid;
192 git_commit* commit;
193 git_tree *tree;
194
195 if( git_reference_name_to_id( &oid, m_repo, "HEAD" ) != GIT_OK )
196 {
197 wxLogTrace( traceGit, "An error occurred: %s", KIGIT_COMMON::GetLastGitError() );
198 return projDirs;
199 }
200
201 if( git_commit_lookup( &commit, m_repo, &oid ) != GIT_OK )
202 {
203 wxLogTrace( traceGit, "An error occurred: %s", KIGIT_COMMON::GetLastGitError() );
204 return projDirs;
205 }
206
207 KIGIT::GitCommitPtr commitPtr( commit );
208
209 if( git_commit_tree( &tree, commit ) != GIT_OK )
210 {
211 wxLogTrace( traceGit, "An error occurred: %s", KIGIT_COMMON::GetLastGitError() );
212 return projDirs;
213 }
214
215 KIGIT::GitTreePtr treePtr( tree );
216
217 // Define callback
218 git_tree_walk(
219 tree, GIT_TREEWALK_PRE,
220 []( const char* root, const git_tree_entry* entry, void* payload )
221 {
222 std::vector<wxString>* prjs = static_cast<std::vector<wxString>*>( payload );
223 wxFileName root_fn( git_tree_entry_name( entry ) );
224
225 root_fn.SetPath( root );
226
227 if( git_tree_entry_type( entry ) == GIT_OBJECT_BLOB
228 && ( ( root_fn.GetExt() == "kicad_pro" ) || ( root_fn.GetExt() == "pro" ) ) )
229 {
230 prjs->push_back( root_fn.GetFullPath() );
231 }
232
233 return 0; // continue walking
234 },
235 &projDirs );
236
237 std::sort( projDirs.begin(), projDirs.end(),
238 []( const wxString& a, const wxString& b )
239 {
240 int a_freq = a.Freq( wxFileName::GetPathSeparator() );
241 int b_freq = b.Freq( wxFileName::GetPathSeparator() );
242
243 if( a_freq == b_freq )
244 return a < b;
245 else
246 return a_freq < b_freq;
247
248 } );
249
250 return projDirs;
251}
252
253
254std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles() const
255{
256 auto get_modified_files = [&]( const git_oid* from_oid, const git_oid* to_oid ) -> std::set<wxString>
257 {
258 std::set<wxString> modified_set;
259 git_revwalk* walker = nullptr;
260
261 if( !m_repo || !from_oid )
262 return modified_set;
263
264 if( git_revwalk_new( &walker, m_repo ) != GIT_OK )
265 {
266 wxLogTrace( traceGit, "Failed to create revwalker: %s", KIGIT_COMMON::GetLastGitError() );
267 return modified_set;
268 }
269
270 KIGIT::GitRevWalkPtr walkerPtr( walker );
271
272 if( git_revwalk_push( walker, from_oid ) != GIT_OK )
273 {
274 wxLogTrace( traceGit, "Failed to set from commit: %s", KIGIT_COMMON::GetLastGitError() );
275 return modified_set;
276 }
277
278 if( to_oid && git_revwalk_hide( walker, to_oid ) != GIT_OK )
279 {
280 wxLogTrace( traceGit, "Failed to set end commit (maybe new repo): %s", KIGIT_COMMON::GetLastGitError() );
281 }
282
283 git_oid oid;
284 git_commit* commit;
285
286 // iterate over all local commits not in remote
287 while( git_revwalk_next( &oid, walker ) == GIT_OK )
288 {
289 if( git_commit_lookup( &commit, m_repo, &oid ) != GIT_OK )
290 {
291 wxLogTrace( traceGit, "Failed to lookup commit: %s", KIGIT_COMMON::GetLastGitError() );
292 continue;
293 }
294
295 KIGIT::GitCommitPtr commitPtr( commit );
296 git_tree *tree, *parent_tree = nullptr;
297
298 if( git_commit_tree( &tree, commit ) != GIT_OK )
299 {
300 wxLogTrace( traceGit, "Failed to get commit tree: %s", KIGIT_COMMON::GetLastGitError() );
301 continue;
302 }
303
304 KIGIT::GitTreePtr treePtr( tree );
305
306 // get parent commit tree to diff against
307 if( !git_commit_parentcount( commit ) )
308 {
309 git_tree_walk(
310 tree, GIT_TREEWALK_PRE,
311 []( const char* root, const git_tree_entry* entry, void* payload )
312 {
313 std::set<wxString>* modified_set_internal = static_cast<std::set<wxString>*>( payload );
314 wxString filePath = wxString::Format( "%s%s", root, git_tree_entry_name( entry ) );
315 modified_set_internal->insert( std::move( filePath ) );
316 return 0; // continue walking
317 },
318 &modified_set );
319 continue;
320 }
321
322 git_commit* parent;
323
324 if( git_commit_parent( &parent, commit, 0 ) != GIT_OK )
325 {
326 wxLogTrace( traceGit, "Failed to get parent commit: %s", KIGIT_COMMON::GetLastGitError() );
327 continue;
328 }
329
330 KIGIT::GitCommitPtr parentPtr( parent );
331
332
333 if( git_commit_tree( &parent_tree, parent ) != GIT_OK )
334 {
335 wxLogTrace( traceGit, "Failed to get parent commit tree: %s", KIGIT_COMMON::GetLastGitError() );
336 continue;
337 }
338
339 KIGIT::GitTreePtr parentTreePtr( parent_tree );
340
341
342 git_diff* diff;
343 git_diff_options diff_opts;
344 git_diff_init_options( &diff_opts, GIT_DIFF_OPTIONS_VERSION );
345
346 if( !m_repo || !parent_tree || !tree )
347 continue;
348
349 if( git_diff_tree_to_tree( &diff, m_repo, parent_tree, tree, &diff_opts ) == GIT_OK )
350 {
351 size_t num_deltas = git_diff_num_deltas( diff );
352
353 for( size_t i = 0; i < num_deltas; ++i )
354 {
355 const git_diff_delta* delta = git_diff_get_delta( diff, i );
356 modified_set.insert( delta->new_file.path );
357 }
358
359 git_diff_free( diff );
360 }
361 else
362 {
363 wxLogTrace( traceGit, "Failed to diff trees: %s", KIGIT_COMMON::GetLastGitError() );
364 }
365 }
366
367 wxLogTrace( traceGit, "Finished walking commits with end: %s", KIGIT_COMMON::GetLastGitError() );
368
369 return modified_set;
370 };
371
372 std::pair<std::set<wxString>,std::set<wxString>> modified_files;
373
374 if( !m_repo )
375 return modified_files;
376
377 git_reference* head = nullptr;
378 git_reference* remote_head = nullptr;
379
380 if( git_repository_head( &head, m_repo ) != GIT_OK )
381 {
382 wxLogTrace( traceGit, "Failed to get modified HEAD" );
383 return modified_files;
384 }
385
386 KIGIT::GitReferencePtr headPtr( head );
387
388 if( git_branch_upstream( &remote_head, head ) != GIT_OK )
389 {
390 wxLogTrace( traceGit, "Failed to get modified remote HEAD" );
391 }
392
393 KIGIT::GitReferencePtr remoteHeadPtr( remote_head );
394
395 if( remote_head != nullptr && head != nullptr )
396 {
397 const git_oid* head_oid = git_reference_target( head );
398 const git_oid* remote_oid = git_reference_target( remote_head );
399
400 modified_files.first = get_modified_files( head_oid, remote_oid );
401 modified_files.second = get_modified_files( remote_oid, head_oid );
402 }
403
404 return modified_files;
405}
406
407
409{
410 if( !m_repo )
411 return false;
412
413 git_reference* head = nullptr;
414 git_reference* remote_head = nullptr;
415
416 if( git_repository_head( &head, m_repo ) != GIT_OK )
417 {
418 wxLogTrace( traceGit, "Failed to get HEAD: %s", KIGIT_COMMON::GetLastGitError() );
419 return false;
420 }
421
422 KIGIT::GitReferencePtr headPtr( head );
423
424 if( git_branch_upstream( &remote_head, head ) != GIT_OK )
425 {
426 // No remote branch, so we have local commits (new repo?)
427 wxLogTrace( traceGit, "Failed to get remote HEAD: %s", KIGIT_COMMON::GetLastGitError() );
428 return true;
429 }
430
431 KIGIT::GitReferencePtr remoteHeadPtr( remote_head );
432 const git_oid* head_oid = git_reference_target( head );
433 const git_oid* remote_oid = git_reference_target( remote_head );
434 git_revwalk* walker = nullptr;
435
436 if( git_revwalk_new( &walker, m_repo ) != GIT_OK )
437 {
438 wxLogTrace( traceGit, "Failed to create revwalker: %s", KIGIT_COMMON::GetLastGitError() );
439 return false;
440 }
441
442 KIGIT::GitRevWalkPtr walkerPtr( walker );
443
444 if( !head_oid || git_revwalk_push( walker, head_oid ) != GIT_OK )
445 {
446 wxLogTrace( traceGit, "Failed to push commits: %s", KIGIT_COMMON::GetLastGitError() );
447 return false;
448 }
449
450 if( remote_oid && git_revwalk_hide( walker, remote_oid ) != GIT_OK )
451 {
452 wxLogTrace( traceGit, "Failed to push/hide commits: %s", KIGIT_COMMON::GetLastGitError() );
453 return false;
454 }
455
456 git_oid oid;
457
458 // If we can't walk to the next commit, then we are at or behind the remote
459 if( git_revwalk_next( &oid, walker ) != GIT_OK )
460 {
461 wxLogTrace( traceGit, "Failed to walk to next commit: %s", KIGIT_COMMON::GetLastGitError() );
462 return false;
463 }
464
465 return true;
466}
467
468
470{
471 wxCHECK( m_repo, false );
472 git_remote* remote = nullptr;
473
474 if( git_remote_lookup( &remote, m_repo, "origin" ) != GIT_OK )
475 {
476 wxLogTrace( traceGit, "Failed to get remote for haspushpull" );
477 return false;
478 }
479
480 KIGIT::GitRemotePtr remotePtr( remote );
481
482 // Get the URLs associated with the remote
483 const char* fetch_url = git_remote_url( remote );
484 const char* push_url = git_remote_pushurl( remote );
485
486 // If no push URL is set, libgit2 defaults to using the fetch URL for pushing
487 if( !push_url )
488 {
489 wxLogTrace( traceGit, "No push URL set, using fetch URL" );
490 push_url = fetch_url;
491 }
492
493 return fetch_url && push_url;
494}
495
496
498{
499 wxCHECK( m_repo, wxEmptyString );
500
501 wxString retval;
502 git_reference* head = nullptr;
503 git_reference* upstream = nullptr;
504
505 if( git_repository_head( &head, m_repo ) != GIT_OK )
506 {
507 wxLogTrace( traceGit, "Failed to get remote name: %s", KIGIT_COMMON::GetLastGitError() );
508 return retval;
509 }
510
511 KIGIT::GitReferencePtr headPtr( head );
512
513 if( git_branch_upstream( &upstream, head ) != GIT_OK )
514 {
515 wxLogTrace( traceGit, "Failed to get upstream branch: %s", KIGIT_COMMON::GetLastGitError() );
516 git_strarray remotes = { nullptr, 0 };
517
518 if( git_remote_list( &remotes, m_repo ) == GIT_OK )
519 {
520 if( remotes.count == 1 )
521 retval = remotes.strings[0];
522
523 git_strarray_dispose( &remotes );
524 }
525 else
526 {
527 wxLogTrace( traceGit, "Failed to list remotes: %s", KIGIT_COMMON::GetLastGitError() );
528
529 // If we can't get the remote name from the upstream branch or the list of remotes,
530 // just return the default remote name
531
532 git_remote* remote = nullptr;
533
534 if( git_remote_lookup( &remote, m_repo, "origin" ) == GIT_OK )
535 {
536 retval = git_remote_name( remote );
537 git_remote_free( remote );
538 }
539 else
540 {
541 wxLogTrace( traceGit, "Failed to get remote name from default remote: %s",
543 }
544 }
545
546 return retval;
547 }
548
549 KIGIT::GitReferencePtr upstreamPtr( upstream );
550 git_buf remote_name = GIT_BUF_INIT_CONST( nullptr, 0 );
551
552 if( git_branch_remote_name( &remote_name, m_repo, git_reference_name( upstream ) ) == GIT_OK )
553 {
554 retval = remote_name.ptr;
555 git_buf_dispose( &remote_name );
556 }
557 else
558 {
559 wxLogTrace( traceGit,
560 "Failed to get remote name from upstream branch: %s",
562 }
563
564 return retval;
565}
566
567void KIGIT_COMMON::SetSSHKey( const wxString& aKey )
568{
569 auto it = std::find( m_publicKeys.begin(), m_publicKeys.end(), aKey );
570
571 if( it != m_publicKeys.end() )
572 m_publicKeys.erase( it );
573
574 m_publicKeys.insert( m_publicKeys.begin(), aKey );
575}
576
577
579{
580 if( !m_repo )
581 return wxEmptyString;
582
583 const char *path = git_repository_path( m_repo );
584 wxString retval = path;
585 return retval;
586}
587
588
590{
591 m_publicKeys.clear();
592
593 wxFileName keyFile( wxGetHomeDir(), wxEmptyString );
594 keyFile.AppendDir( ".ssh" );
595 keyFile.SetFullName( "id_rsa" );
596
597 if( keyFile.FileExists() )
598 m_publicKeys.push_back( keyFile.GetFullPath() );
599
600 keyFile.SetFullName( "id_dsa" );
601
602 if( keyFile.FileExists() )
603 m_publicKeys.push_back( keyFile.GetFullPath() );
604
605 keyFile.SetFullName( "id_ecdsa" );
606
607 if( keyFile.FileExists() )
608 m_publicKeys.push_back( keyFile.GetFullPath() );
609
610 keyFile.SetFullName( "id_ed25519" );
611
612 if( keyFile.FileExists() )
613 m_publicKeys.push_back( keyFile.GetFullPath() );
614
615 // Parse SSH config file for hostname information
616 wxFileName sshConfig( wxGetHomeDir(), wxEmptyString );
617 sshConfig.AppendDir( ".ssh" );
618 sshConfig.SetFullName( "config" );
619
620 if( sshConfig.FileExists() )
621 {
622 wxTextFile configFile( sshConfig.GetFullPath() );
623 configFile.Open();
624
625 bool match = false;
626
627 for( wxString line = configFile.GetFirstLine(); !configFile.Eof(); line = configFile.GetNextLine() )
628 {
629 line.Trim( false ).Trim( true );
630
631 if( line.StartsWith( "Host " ) )
632 match = false;
633
634 // The difference here is that we are matching either "Hostname" or "Host" to get the
635 // match. This is because in the absence of a "Hostname" line, the "Host" line is used
636 if( line.StartsWith( "Host" ) && line.Contains( m_hostname ) )
637 match = true;
638
639 if( match && line.StartsWith( "IdentityFile" ) )
640 {
641 wxString keyPath = line.AfterFirst( ' ' ).Trim( false ).Trim( true );
642
643 // Expand ~ to home directory if present
644 if( keyPath.StartsWith( "~" ) )
645 keyPath.Replace( "~", wxGetHomeDir(), false );
646
647 // Add the public key to the beginning of the list
648 if( wxFileName::FileExists( keyPath ) )
649 SetSSHKey( keyPath );
650 }
651 }
652
653 configFile.Close();
654 }
655}
656
657
659{
660 wxCHECK( m_repo, /* void */ );
661
662 // We want to get the current branch's upstream url as well as the stored password
663 // if one exists given the url and username.
664
665 wxString remote_name = GetRemotename();
666 git_remote* remote = nullptr;
667
668 m_remote.clear();
669 m_password.clear();
670 m_secretFetched = false;
671
672 if( git_remote_lookup( &remote, m_repo, remote_name.ToStdString().c_str() ) == GIT_OK )
673 {
674 const char* url = git_remote_url( remote );
675
676 if( url )
677 m_remote = url;
678
679 git_remote_free( remote );
680 }
681
684}
685
687{
688 wxString remote = m_remote;
689
690 if( remote.IsEmpty() )
691 remote = GetRemotename();
692
693 if( remote.StartsWith( "https://" ) || remote.StartsWith( "http://" ) )
694 {
696 }
697 else if( remote.StartsWith( "ssh://" ) || remote.StartsWith( "git@" ) || remote.StartsWith( "git+ssh://" )
698 || remote.EndsWith( ".git" ) )
699 {
701 }
702
704}
705
707{
708 if( m_remote.StartsWith( "https://" ) || m_remote.StartsWith( "http://" ) )
710 else if( m_remote.StartsWith( "ssh://" ) || m_remote.StartsWith( "git@" ) || m_remote.StartsWith( "git+ssh://" ) )
712 else
714
716 {
717 wxString uri = m_remote;
718 size_t atPos = uri.find( '@' );
719
720 if( atPos != wxString::npos )
721 {
722 size_t protoEnd = uri.find( "//" );
723
724 if( protoEnd != wxString::npos )
725 {
726 wxString credentials = uri.Mid( protoEnd + 2, atPos - protoEnd - 2 );
727 size_t colonPos = credentials.find( ':' );
728
729 if( colonPos != wxString::npos )
730 {
731 m_username = credentials.Left( colonPos );
732 m_password = credentials.Mid( colonPos + 1, credentials.Length() - colonPos - 1 );
733 }
734 else
735 {
736 m_username = credentials;
737 }
738 }
739 else
740 {
741 m_username = uri.Left( atPos );
742 }
743 }
744
745 if( m_remote.StartsWith( "git@" ) )
746 {
747 // SSH format: git@hostname:path
748 size_t colonPos = m_remote.find( ':' );
749 if( colonPos != wxString::npos )
750 m_hostname = m_remote.Mid( 4, colonPos - 4 );
751 }
752 else
753 {
754 // other URL format: proto://[user@]hostname/path
755 size_t hostStart = m_remote.find( "://" ) + 2;
756 size_t hostEnd = m_remote.find( '/', hostStart );
757 wxString host;
758
759 if( hostEnd != wxString::npos )
760 host = m_remote.Mid( hostStart, hostEnd - hostStart );
761 else
762 host = m_remote.Mid( hostStart );
763
764 atPos = host.find( '@' );
765
766 if( atPos != wxString::npos )
767 m_hostname = host.Mid( atPos + 1 );
768 else
769 m_hostname = host;
770 }
771 }
772
773 m_secretFetched = !m_password.IsEmpty();
774}
775
776
777int KIGIT_COMMON::HandleSSHKeyAuthentication( git_cred** aOut, const wxString& aUsername )
778{
780 return HandleSSHAgentAuthentication( aOut, aUsername );
781
782 // SSH key authentication with password
783 wxString sshKey = GetNextPublicKey();
784
785 if( sshKey.IsEmpty() )
786 {
787 wxLogTrace( traceGit, "Finished testing all possible ssh keys" );
788 m_testedTypes |= GIT_CREDENTIAL_SSH_KEY;
789 return GIT_PASSTHROUGH;
790 }
791
792 wxString sshPubKey = sshKey + ".pub";
793 wxString password = GetPassword();
794
795 wxLogTrace( traceGit, "Testing %s\n", sshKey );
796
797 if( git_credential_ssh_key_new( aOut, aUsername.mbc_str(), sshPubKey.mbc_str(), sshKey.mbc_str(),
798 password.mbc_str() ) != GIT_OK )
799 {
800 wxLogTrace( traceGit, "Failed to create SSH key credential for %s: %s",
801 aUsername, KIGIT_COMMON::GetLastGitError() );
802 return GIT_PASSTHROUGH;
803 }
804
805 return GIT_OK;
806}
807
808
809int KIGIT_COMMON::HandlePlaintextAuthentication( git_cred** aOut, const wxString& aUsername )
810{
811 wxString password = GetPassword();
812
813 git_credential_userpass_plaintext_new( aOut, aUsername.mbc_str(), password.mbc_str() );
814 m_testedTypes |= GIT_CREDENTIAL_USERPASS_PLAINTEXT;
815
816 return GIT_OK;
817}
818
819int KIGIT_COMMON::HandleSSHAgentAuthentication( git_cred** aOut, const wxString& aUsername )
820{
821 if( git_credential_ssh_key_from_agent( aOut, aUsername.mbc_str() ) != GIT_OK )
822 {
823 wxLogTrace( traceGit, "Failed to create SSH agent credential for %s: %s",
824 aUsername, KIGIT_COMMON::GetLastGitError() );
825 return GIT_PASSTHROUGH;
826 }
827
829 return GIT_OK;
830}
831
832
833extern "C" int fetchhead_foreach_cb( const char*, const char*,
834 const git_oid* aOID, unsigned int aIsMerge, void* aPayload )
835{
836 if( aIsMerge )
837 git_oid_cpy( (git_oid*) aPayload, aOID );
838
839 return 0;
840}
841
842
843extern "C" void clone_progress_cb( const char* aStr, size_t aLen, size_t aTotal, void* aPayload )
844{
845 KIGIT_REPO_MIXIN* parent = reinterpret_cast<KIGIT_REPO_MIXIN*>( aPayload );
846
847 wxString progressMessage( aStr );
848 parent->UpdateProgress( aLen, aTotal, progressMessage );
849}
850
851
852extern "C" int progress_cb( const char* str, int len, void* aPayload )
853{
854 KIGIT_REPO_MIXIN* parent = reinterpret_cast<KIGIT_REPO_MIXIN*>( aPayload );
855
856 if( parent->GetCommon()->IsCancelled() )
857 {
858 wxLogTrace( traceGit, "Progress CB cancelled" );
859 return GIT_EUSER;
860 }
861
862 wxString progressMessage( str, len );
863 parent->UpdateProgress( 0, 0, progressMessage );
864
865 return 0;
866}
867
868
869extern "C" int transfer_progress_cb( const git_transfer_progress* aStats, void* aPayload )
870{
871 KIGIT_REPO_MIXIN* parent = reinterpret_cast<KIGIT_REPO_MIXIN*>( aPayload );
872
873 wxString progressMessage = wxString::Format( _( "Received %u of %u objects" ),
874 aStats->received_objects,
875 aStats->total_objects );
876 if( parent->GetCommon()->IsCancelled() )
877 {
878 wxLogTrace( traceGit, "Transfer progress cancelled" );
879 return GIT_EUSER;
880 }
881
882 parent->UpdateProgress( aStats->received_objects, aStats->total_objects, progressMessage );
883
884 return 0;
885}
886
887
888extern "C" int update_cb( const char* aRefname, const git_oid* aFirst, const git_oid* aSecond,
889 void* aPayload )
890{
891 constexpr int cstring_len = 8;
892 char a_str[cstring_len + 1];
893 char b_str[cstring_len + 1];
894
895 KIGIT_REPO_MIXIN* parent = reinterpret_cast<KIGIT_REPO_MIXIN*>( aPayload );
896 wxString status;
897
898 git_oid_tostr( b_str, cstring_len, aSecond );
899
900#if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 )
901 if( !git_oid_is_zero( aFirst ) )
902#else
903 if( !git_oid_iszero( aFirst ) )
904#endif
905 {
906 git_oid_tostr( a_str, cstring_len, aFirst );
907 status = wxString::Format( _( "* [updated] %s..%s %s" ), a_str, b_str, aRefname );
908 }
909 else
910 {
911 status = wxString::Format( _( "* [new] %s %s" ), b_str, aRefname );
912 }
913
914 parent->UpdateProgress( 0, 0, status );
915
916 return 0;
917}
918
919
920extern "C" int push_transfer_progress_cb( unsigned int aCurrent, unsigned int aTotal, size_t aBytes,
921 void* aPayload )
922{
923 long long progress = 100;
924 KIGIT_REPO_MIXIN* parent = reinterpret_cast<KIGIT_REPO_MIXIN*>( aPayload );
925
926 if( aTotal != 0 )
927 {
928 progress = ( aCurrent * 100ll ) / aTotal;
929 }
930
931 wxString progressMessage = wxString::Format( _( "Writing objects: %lld%% (%u/%u), %zu bytes" ),
932 progress, aCurrent, aTotal, aBytes );
933 parent->UpdateProgress( aCurrent, aTotal, progressMessage );
934
935 return 0;
936}
937
938
939extern "C" int push_update_reference_cb( const char* aRefname, const char* aStatus, void* aPayload )
940{
941 KIGIT_REPO_MIXIN* parent = reinterpret_cast<KIGIT_REPO_MIXIN*>( aPayload );
942 wxString status( aStatus );
943
944 if( !status.IsEmpty() )
945 {
946 wxString statusMessage = wxString::Format( _( "* [rejected] %s (%s)" ), aRefname, aStatus );
947 parent->UpdateProgress( 0, 0, statusMessage );
948 }
949 else
950 {
951 wxString statusMessage = wxString::Format( _( "[updated] %s" ), aRefname );
952 parent->UpdateProgress( 0, 0, statusMessage );
953 }
954
955 return 0;
956}
957
958
959extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aUsername,
960 unsigned int aAllowedTypes, void* aPayload )
961{
962 KIGIT_REPO_MIXIN* parent = reinterpret_cast<KIGIT_REPO_MIXIN*>( aPayload );
963 KIGIT_COMMON* common = parent->GetCommon();
964
965 wxLogTrace( traceGit, "Credentials callback for %s, testing %d", aUrl, aAllowedTypes );
966
968 {
969 wxLogTrace( traceGit, "Local repository, no credentials needed" );
970 return GIT_PASSTHROUGH;
971 }
972
973 if( aAllowedTypes & GIT_CREDENTIAL_USERNAME
974 && !( parent->TestedTypes() & GIT_CREDENTIAL_USERNAME ) )
975 {
976 wxString username = parent->GetUsername().Trim().Trim( false );
977 wxLogTrace( traceGit, "Username credential for %s at %s with allowed type %d",
978 username, aUrl, aAllowedTypes );
979 if( git_credential_username_new( aOut, username.ToStdString().c_str() ) != GIT_OK )
980 {
981 wxLogTrace( traceGit, "Failed to create username credential for %s: %s",
982 username, KIGIT_COMMON::GetLastGitError() );
983 }
984 else
985 {
986 wxLogTrace( traceGit, "Created username credential for %s", username );
987 }
988
989 parent->TestedTypes() |= GIT_CREDENTIAL_USERNAME;
990 }
992 && ( aAllowedTypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT )
993 && !( parent->TestedTypes() & GIT_CREDENTIAL_USERPASS_PLAINTEXT ) )
994 {
995 // Plaintext authentication
996 wxLogTrace( traceGit, "Plaintext authentication for %s at %s with allowed type %d",
997 parent->GetUsername(), aUrl, aAllowedTypes );
998 return common->HandlePlaintextAuthentication( aOut, parent->GetUsername() );
999 }
1001 && ( aAllowedTypes & GIT_CREDENTIAL_SSH_KEY )
1002 && !( parent->TestedTypes() & GIT_CREDENTIAL_SSH_KEY ) )
1003 {
1004 // SSH key authentication
1005 return common->HandleSSHKeyAuthentication( aOut, parent->GetUsername() );
1006 }
1007 else
1008 {
1009 // If we didn't find anything to try, then we don't have a callback set that the
1010 // server likes
1011 if( !parent->TestedTypes() )
1012 return GIT_PASSTHROUGH;
1013
1014 git_error_clear();
1015 git_error_set_str( GIT_ERROR_NET, _( "Unable to authenticate" ).mbc_str() );
1016 // Otherwise, we did try something but we failed, so return an authentication error
1017 return GIT_EAUTH;
1018 }
1019
1020 return GIT_OK;
1021};
virtual void UpdateProgress(int aCurrent, int aTotal, const wxString &aMessage)
std::mutex m_gitActionMutex
git_repository * m_repo
std::vector< wxString > GetBranchNames() const
GIT_CONN_TYPE GetConnType() const
unsigned m_testedTypes
static wxString GetLastGitError()
wxString GetCurrentBranchName() const
wxString GetGitRootDirectory() const
void SetSSHKey(const wxString &aSSHKey)
bool IsCancelled() const
static const unsigned KIGIT_CREDENTIAL_SSH_AGENT
int HandlePlaintextAuthentication(git_cred **aOut, const wxString &aUsername)
std::vector< wxString > GetProjectDirs()
Return a vector of project files in the repository.
git_repository * GetRepo() const
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
GIT_CONN_TYPE m_connType
void UpdateCurrentBranchInfo()
bool HasLocalCommits() const
wxString GetPassword()
int HandleSSHAgentAuthentication(git_cred **aOut, const wxString &aUsername)
wxString GetRemotename() const
int HandleSSHKeyAuthentication(git_cred **aOut, const wxString &aUsername)
wxString GetUsername() const
Get the username.
unsigned & TestedTypes()
Return the connection types that have been tested for authentication.
KIGIT_COMMON * GetCommon() const
Get the common object.
KIGIT_COMMON::GIT_CONN_TYPE GetConnType() const
Get the connection type.
#define _(s)
const wxChar *const traceGit
Flag to enable Git debugging output.
int fetchhead_foreach_cb(const char *, const char *, const git_oid *aOID, unsigned int aIsMerge, void *aPayload)
int progress_cb(const char *str, int len, 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)
void clone_progress_cb(const char *aStr, size_t aLen, size_t aTotal, void *aPayload)
int push_transfer_progress_cb(unsigned int aCurrent, unsigned int aTotal, size_t aBytes, void *aPayload)
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::unique_ptr< git_revwalk, decltype([](git_revwalk *aWalker) { git_revwalk_free(aWalker); })> GitRevWalkPtr
A unique pointer for git_revwalk objects with automatic cleanup.
std::unique_ptr< git_commit, decltype([](git_commit *aCommit) { git_commit_free(aCommit); })> GitCommitPtr
A unique pointer for git_commit objects with automatic cleanup.
std::unique_ptr< git_reference, decltype([](git_reference *aRef) { git_reference_free(aRef); })> GitReferencePtr
A unique pointer for git_reference objects with automatic cleanup.
std::unique_ptr< git_branch_iterator, decltype([](git_branch_iterator *aIter) { git_branch_iterator_free(aIter); })> GitBranchIteratorPtr
A unique pointer for git_branch_iterator objects with automatic cleanup.
std::unique_ptr< git_remote, decltype([](git_remote *aRemote) { git_remote_free(aRemote); })> GitRemotePtr
A unique pointer for git_remote objects with automatic cleanup.
bool GetSecret(const wxString &aService, const wxString &aKey, wxString &aSecret)
int delta
wxLogTrace helper definitions.