33import  java .io .File ;
44import  java .io .IOException ;
55import  java .io .InputStream ;
6+ import  java .net .URISyntaxException ;
67import  java .nio .file .Files ;
78import  java .nio .file .Path ;
89import  java .util .Optional ;
10+ import  java .util .Set ;
911
1012import  org .jabref .logic .JabRefException ;
1113import  org .jabref .logic .git .prefs .GitPreferences ;
1214
15+ import  org .eclipse .jgit .api .FetchCommand ;
1316import  org .eclipse .jgit .api .Git ;
17+ import  org .eclipse .jgit .api .PullCommand ;
18+ import  org .eclipse .jgit .api .PushCommand ;
1419import  org .eclipse .jgit .api .RmCommand ;
1520import  org .eclipse .jgit .api .Status ;
1621import  org .eclipse .jgit .api .errors .GitAPIException ;
1722import  org .eclipse .jgit .lib .Ref ;
1823import  org .eclipse .jgit .lib .Repository ;
1924import  org .eclipse .jgit .lib .RepositoryState ;
25+ import  org .eclipse .jgit .lib .StoredConfig ;
2026import  org .eclipse .jgit .merge .MergeStrategy ;
21- import  org .eclipse .jgit .revwalk .RevCommit ;
2227import  org .eclipse .jgit .transport .CredentialsProvider ;
2328import  org .eclipse .jgit .transport .RefSpec ;
29+ import  org .eclipse .jgit .transport .URIish ;
2430import  org .eclipse .jgit .transport .UsernamePasswordCredentialsProvider ;
2531import  org .slf4j .Logger ;
2632import  org .slf4j .LoggerFactory ;
@@ -73,7 +79,7 @@ public void initIfNeeded() {
7379        }
7480    }
7581
76-     private  Optional <CredentialsProvider > resolveCredentialsOrLoad ()  throws   JabRefException  {
82+     private  Optional <CredentialsProvider > resolveCredentials ()  {
7783        if  (credentialsProvider  != null ) {
7884            return  Optional .of (credentialsProvider );
7985        }
@@ -107,6 +113,46 @@ public void setCredentials(String username, String pat) {
107113        this .credentialsProvider  = new  UsernamePasswordCredentialsProvider (username , pat );
108114    }
109115
116+     private  static  Optional <String > currentRemoteUrl (Repository  repo ) {
117+         try  {
118+             StoredConfig  config  = repo .getConfig ();
119+             String  branch  = repo .getBranch ();
120+ 
121+             String  remote  = config .getString ("branch" , branch , "remote" );
122+             if  (remote  == null ) {
123+                 Set <String > remotes  = config .getSubsections ("remote" );
124+                 if  (remotes .contains ("origin" )) {
125+                     remote  = "origin" ;
126+                 } else  if  (!remotes .isEmpty ()) {
127+                     remote  = remotes .iterator ().next ();
128+                 }
129+             }
130+             if  (remote  == null ) {
131+                 return  Optional .empty ();
132+             }
133+             String  url  = config .getString ("remote" , remote , "url" );
134+             if  (url  == null  || url .isBlank ()) {
135+                 return  Optional .empty ();
136+             }
137+             return  Optional .of (url );
138+         } catch  (IOException  e ) {
139+             return  Optional .empty ();
140+         }
141+     }
142+ 
143+     private  static  boolean  requiresCredentialsForUrl (String  url ) {
144+         try  {
145+             URIish  uri  = new  URIish (url );
146+             String  scheme  = uri .getScheme ();
147+             if  (scheme  == null ) {
148+                 return  false ;
149+             }
150+             return  "http" .equalsIgnoreCase (scheme ) || "https" .equalsIgnoreCase (scheme );
151+         } catch  (URISyntaxException  e ) {
152+             return  false ;
153+         }
154+     }
155+ 
110156    void  setupGitIgnore () {
111157        Path  gitignore  = Path .of (repositoryPath .toString (), ".gitignore" );
112158        if  (!Files .exists (gitignore )) {
@@ -231,37 +277,65 @@ public void mergeBranches(String targetBranch, String sourceBranch, MergeStrateg
231277     * If pushing to remote fails, it fails silently. 
232278     */ 
233279    public  void  pushCommitsToRemoteRepository () throws  IOException , GitAPIException , JabRefException  {
234-         CredentialsProvider  provider  = resolveCredentialsOrLoad ().orElseThrow (() -> new  IOException ("Missing Git credentials (username and Personal Access Token)." ));
235- 
236280        try  (Git  git  = Git .open (this .repositoryPathAsFile )) {
237-             git .push ()
238-                .setCredentialsProvider (provider )
239-                .call ();
281+             Optional <String > urlOpt  = currentRemoteUrl (git .getRepository ());
282+             Optional <CredentialsProvider > credsOpt  = resolveCredentials ();
283+ 
284+             boolean  needCreds  = urlOpt .map (GitHandler ::requiresCredentialsForUrl ).orElse (false );
285+             if  (needCreds  && credsOpt .isEmpty ()) {
286+                 throw  new  IOException ("Missing Git credentials (username and Personal Access Token)." );
287+             }
288+ 
289+             PushCommand  pushCommand  = git .push ();
290+             if  (credsOpt .isPresent ()) {
291+                 pushCommand .setCredentialsProvider (credsOpt .get ());
292+             }
293+             pushCommand .call ();
240294        }
241295    }
242296
243297    public  void  pushCurrentBranchCreatingUpstream () throws  IOException , GitAPIException , JabRefException  {
244298        try  (Git  git  = open ()) {
245-             CredentialsProvider  provider  = resolveCredentialsOrLoad ().orElseThrow (() -> new  IOException ("Missing Git credentials (username and Personal Access Token)." ));
246-             String  branch  = git .getRepository ().getBranch ();
247-             git .push ()
248-                .setRemote ("origin" )
249-                .setCredentialsProvider (provider )
250-                .setRefSpecs (new  RefSpec ("refs/heads/"  + branch  + ":refs/heads/"  + branch ))
251-                .call ();
299+             Repository  repo  = git .getRepository ();
300+             StoredConfig  config  = repo .getConfig ();
301+             String  remoteUrl  = config .getString ("remote" , "origin" , "url" );
302+ 
303+             Optional <CredentialsProvider > credsOpt  = resolveCredentials ();
304+             boolean  needCreds  = (remoteUrl  != null ) && requiresCredentialsForUrl (remoteUrl );
305+             if  (needCreds  && credsOpt .isEmpty ()) {
306+                 throw  new  IOException ("Missing Git credentials (username and Personal Access Token)." );
307+             }
308+ 
309+             String  branch  = repo .getBranch ();
310+ 
311+             PushCommand  pushCommand  = git .push ()
312+                                  .setRemote ("origin" )
313+                                  .setRefSpecs (new  RefSpec ("refs/heads/"  + branch  + ":refs/heads/"  + branch ));
314+ 
315+             if  (credsOpt .isPresent ()) {
316+                 pushCommand .setCredentialsProvider (credsOpt .get ());
317+             }
318+             pushCommand .call ();
252319        }
253320    }
254321
255-     public  void  pullOnCurrentBranch () throws  IOException , JabRefException  {
256-         CredentialsProvider  provider  = resolveCredentialsOrLoad ().orElseThrow (() -> new  IOException ("Missing Git credentials (username and Personal Access Token)." ));
322+     public  void  pullOnCurrentBranch () throws  IOException  {
257323        try  (Git  git  = Git .open (this .repositoryPathAsFile )) {
258-             try  {
259-                 git .pull ()
260-                    .setCredentialsProvider (provider )
261-                    .call ();
262-             } catch  (GitAPIException  e ) {
263-                 LOGGER .info ("Failed to push" );
324+             Optional <String > urlOpt  = currentRemoteUrl (git .getRepository ());
325+             Optional <CredentialsProvider > credsOpt  = resolveCredentials ();
326+ 
327+             boolean  needCreds  = urlOpt .map (GitHandler ::requiresCredentialsForUrl ).orElse (false );
328+             if  (needCreds  && credsOpt .isEmpty ()) {
329+                 throw  new  IOException ("Missing Git credentials (username and Personal Access Token)." );
330+             }
331+ 
332+             PullCommand  pullCommand  = git .pull ();
333+             if  (credsOpt .isPresent ()) {
334+                 pullCommand .setCredentialsProvider (credsOpt .get ());
264335            }
336+             pullCommand .call ();
337+         } catch  (GitAPIException  e ) {
338+             throw  new  IOException ("Failed to pull from remote: "  + e .getMessage (), e );
265339        }
266340    }
267341
@@ -271,14 +345,22 @@ public String getCurrentlyCheckedOutBranch() throws IOException {
271345        }
272346    }
273347
274-     public  void  fetchOnCurrentBranch () throws  IOException , JabRefException  {
275-         CredentialsProvider  provider  = resolveCredentialsOrLoad ().orElseThrow (() -> new  IOException ("Missing Git credentials (username and Personal Access Token)." ));
348+     public  void  fetchOnCurrentBranch () throws  IOException  {
276349        try  (Git  git  = Git .open (this .repositoryPathAsFile )) {
277-             git .fetch ()
278-                .setCredentialsProvider (provider )
279-                .call ();
350+             Optional <String > urlOpt  = currentRemoteUrl (git .getRepository ());
351+             Optional <CredentialsProvider > credsOpt  = resolveCredentials ();
352+ 
353+             boolean  needCreds  = urlOpt .map (GitHandler ::requiresCredentialsForUrl ).orElse (false );
354+             if  (needCreds  && credsOpt .isEmpty ()) {
355+                 throw  new  IOException ("Missing Git credentials (username and Personal Access Token)." );
356+             }
357+             FetchCommand  fetchCommand  = git .fetch ();
358+             if  (credsOpt .isPresent ()) {
359+                 fetchCommand .setCredentialsProvider (credsOpt .get ());
360+             }
361+             fetchCommand .call ();
280362        } catch  (GitAPIException  e ) {
281-             LOGGER . error ("Failed to fetch from remote"  , e );
363+             throw   new   IOException ("Failed to fetch from remote: "   +  e . getMessage () , e );
282364        }
283365    }
284366
@@ -312,4 +394,15 @@ public File getRepositoryPathAsFile() {
312394    public  Git  open () throws  IOException  {
313395        return  Git .open (this .repositoryPathAsFile );
314396    }
397+ 
398+     public  boolean  hasRemote (String  remoteName ) {
399+         try  (Git  git  = Git .open (this .repositoryPathAsFile )) {
400+             return  git .getRepository ().getConfig ()
401+                       .getSubsections ("remote" )
402+                       .contains (remoteName );
403+         } catch  (IOException  e ) {
404+             LOGGER .error ("Failed to check remote configuration" , e );
405+             return  false ;
406+         }
407+     }
315408}
0 commit comments