From b57845c0cc7d4d9428f26701b1c21e03982639c8 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 17 Jan 2012 09:35:29 -0800 Subject: Support relative submodule URLs on init/add/sync Interpret submodule URLs that start with './' or '../' as relative to either the configured remote for the HEAD branch, or 'origin', or the parent repository working directory if no remote URL is configured Bug: 368536 Change-Id: Id4985824023b75cd45cd64a4dd9d421166391e10 --- .../resources/org/eclipse/jgit/JGitText.properties | 1 + .../src/org/eclipse/jgit/JGitText.java | 1 + .../org/eclipse/jgit/api/SubmoduleAddCommand.java | 10 ++- .../org/eclipse/jgit/api/SubmoduleInitCommand.java | 2 +- .../org/eclipse/jgit/api/SubmoduleSyncCommand.java | 2 +- .../org/eclipse/jgit/submodule/SubmoduleWalk.java | 94 ++++++++++++++++++++++ 6 files changed, 106 insertions(+), 4 deletions(-) (limited to 'org.eclipse.jgit') diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties index a80e6bcae5..5b22801342 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties @@ -423,6 +423,7 @@ staleRevFlagsOn=Stale RevFlags on {0} startingReadStageWithoutWrittenRequestDataPendingIsNotSupported=Starting read stage without written request data pending is not supported statelessRPCRequiresOptionToBeEnabled=stateless RPC requires {0} to be enabled submoduleExists=Submodule ''{0}'' already exists in the index +submoduleParentRemoteUrlInvalid=Cannot remove segment from remote url ''{0}'' submodulesNotSupported=Submodules are not supported symlinkCannotBeWrittenAsTheLinkTarget=Symlink "{0}" cannot be written as the link target cannot be read from within Java. systemConfigFileInvalid=Systen wide config file {0} is invalid {1} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java index f4de487558..dbc3bcae62 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java @@ -484,6 +484,7 @@ public class JGitText extends TranslationBundle { /***/ public String statelessRPCRequiresOptionToBeEnabled; /***/ public String submoduleExists; /***/ public String submodulesNotSupported; + /***/ public String submoduleParentRemoteUrlInvalid; /***/ public String symlinkCannotBeWrittenAsTheLinkTarget; /***/ public String systemConfigFileInvalid; /***/ public String tagNameInvalid; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java index ac81ccb457..e1b293c43d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java @@ -148,12 +148,18 @@ public class SubmoduleAddCommand extends throw new JGitInternalException(e.getMessage(), e); } + final String resolvedUri; + try { + resolvedUri = SubmoduleWalk.getSubmoduleRemoteUrl(repo, uri); + } catch (IOException e) { + throw new JGitInternalException(e.getMessage(), e); + } // Clone submodule repository File moduleDirectory = SubmoduleWalk.getSubmoduleDirectory(repo, path); CloneCommand clone = Git.cloneRepository(); configure(clone); clone.setDirectory(moduleDirectory); - clone.setURI(uri); + clone.setURI(resolvedUri); if (monitor != null) clone.setProgressMonitor(monitor); Repository subRepo = clone.call().getRepository(); @@ -161,7 +167,7 @@ public class SubmoduleAddCommand extends // Save submodule URL to parent repository's config StoredConfig config = repo.getConfig(); config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path, - ConfigConstants.CONFIG_KEY_URL, uri); + ConfigConstants.CONFIG_KEY_URL, resolvedUri); try { config.save(); } catch (IOException e) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java index ad8f02e47f..fef13704ef 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java @@ -106,7 +106,7 @@ public class SubmoduleInitCommand extends GitCommand> { String path = generator.getPath(); // Copy 'url' and 'update' fields from .gitmodules to config // file - String url = generator.getModulesUrl(); + String url = generator.getRemoteUrl(); String update = generator.getModulesUpdate(); if (url != null) config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java index 43647a0c69..fd8ddc941d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java @@ -116,7 +116,7 @@ public class SubmoduleSyncCommand extends GitCommand> { Map synced = new HashMap(); StoredConfig config = repo.getConfig(); while (generator.next()) { - String remoteUrl = generator.getModulesUrl(); + String remoteUrl = generator.getRemoteUrl(); if (remoteUrl == null) continue; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java index ebe994826e..78896f8a54 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java @@ -44,7 +44,9 @@ package org.eclipse.jgit.submodule; import java.io.File; import java.io.IOException; +import java.text.MessageFormat; +import org.eclipse.jgit.JGitText; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.CorruptObjectException; @@ -176,6 +178,83 @@ public class SubmoduleWalk { return new File(getSubmoduleDirectory(parent, path), Constants.DOT_GIT); } + /** + * Resolve submodule repository URL. + *

+ * This handles relative URLs that are typically specified in the + * '.gitmodules' file by resolving them against the remote URL of the parent + * repository. + *

+ * Relative URLs will be resolved against the parent repository's working + * directory if the parent repository has no configured remote URL. + * + * @param parent + * parent repository + * @param url + * absolute or relative URL of the submodule repository + * @return resolved URL + * @throws IOException + */ + public static String getSubmoduleRemoteUrl(final Repository parent, + final String url) throws IOException { + if (!url.startsWith("./") && !url.startsWith("../")) + return url; + + String remoteName = null; + // Look up remote URL associated wit HEAD ref + Ref ref = parent.getRef(Constants.HEAD); + if (ref != null) { + if (ref.isSymbolic()) + ref = ref.getLeaf(); + remoteName = parent.getConfig().getString( + ConfigConstants.CONFIG_BRANCH_SECTION, + Repository.shortenRefName(ref.getName()), + ConfigConstants.CONFIG_KEY_REMOTE); + } + + // Fall back to 'origin' if current HEAD ref has no remote URL + if (remoteName == null) + remoteName = Constants.DEFAULT_REMOTE_NAME; + + String remoteUrl = parent.getConfig().getString( + ConfigConstants.CONFIG_REMOTE_SECTION, remoteName, + ConfigConstants.CONFIG_KEY_URL); + + // Fall back to parent repository's working directory if no remote URL + if (remoteUrl == null) { + remoteUrl = parent.getWorkTree().getAbsolutePath(); + // Normalize slashes to '/' + if ('\\' == File.separatorChar) + remoteUrl = remoteUrl.replace('\\', '/'); + } + + // Remove trailing '/' + if (remoteUrl.charAt(remoteUrl.length() - 1) == '/') + remoteUrl = remoteUrl.substring(0, remoteUrl.length() - 1); + + char separator = '/'; + String submoduleUrl = url; + while (submoduleUrl.length() > 0) { + if (submoduleUrl.startsWith("./")) + submoduleUrl = submoduleUrl.substring(2); + else if (submoduleUrl.startsWith("../")) { + int lastSeparator = remoteUrl.lastIndexOf('/'); + if (lastSeparator < 1) { + lastSeparator = remoteUrl.lastIndexOf(':'); + separator = ':'; + } + if (lastSeparator < 1) + throw new IOException(MessageFormat.format( + JGitText.get().submoduleParentRemoteUrlInvalid, + remoteUrl)); + remoteUrl = remoteUrl.substring(0, lastSeparator); + submoduleUrl = submoduleUrl.substring(3); + } else + break; + } + return remoteUrl + separator + submoduleUrl; + } + private final Repository repository; private final TreeWalk walk; @@ -432,4 +511,19 @@ public class SubmoduleWalk { Ref head = subRepo.getRef(Constants.HEAD); return head != null ? head.getLeaf().getName() : null; } + + /** + * Get the resolved remote URL for the current submodule. + *

+ * This method resolves the value of {@link #getModulesUrl()} to an absolute + * URL + * + * @return resolved remote URL + * @throws IOException + * @throws ConfigInvalidException + */ + public String getRemoteUrl() throws IOException, ConfigInvalidException { + String url = getModulesUrl(); + return url != null ? getSubmoduleRemoteUrl(repository, url) : null; + } } -- cgit v1.2.3