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