Allow switching between overrides (#1588)
Previously, if one used e.g. command ``` emsdk install --override-repository emscripten-main-64bit@https://github.com/juj/emscripten/tree/my_own_emscripten_branch emscripten-main-64bit ``` then the overridden installation would create a git clone from `juj/emscripten`, and that clone would be located under the default remote name `origin`. This would prevent being able to switch between overrides on subsequent `emsdk install` commands. I.e. it would then not be possible to later do a default ``` emsdk install emscripten-main-64bit ``` command without an override, since the remote name `origin` would point to `juj/emscripten` instead of `emscripten-core/emscripten`. This PR changes the naming scheme of `git clone`s when `--override-repository` is used, to name the clones with remote names from the override, so in the above case, the clone would appear under remote name `juj` and not `origin`. This enables flipping between multiple `emsdk install` commands in the same checkout, without needing to nuke the installed directory in between. This enables me to run experiments like github.com/emscripten-core/emscripten/pull/25025 locally on my CI, without needing to merge them upstream to become available.
This commit is contained in:
83
emsdk.py
83
emsdk.py
@@ -200,10 +200,13 @@ else:
|
||||
EMSDK_SET_ENV = os.path.join(EMSDK_PATH, 'emsdk_set_env.bat')
|
||||
|
||||
|
||||
# Parses https://github.com/emscripten-core/emscripten/tree/d6aced8 to a pair (https://github.com/emscripten-core/emscripten, d6aced8)
|
||||
# Parses https://github.com/emscripten-core/emscripten/tree/d6aced8 to a triplet
|
||||
# (https://github.com/emscripten-core/emscripten, d6aced8, emscripten-core)
|
||||
# or https://github.com/emscripten-core/emscripten/commit/00b76f81f6474113fcf540db69297cfeb180347e
|
||||
# to (https://github.com/emscripten-core/emscripten, 00b76f81f6474113fcf540db69297cfeb180347e, emscripten-core)
|
||||
def parse_github_url_and_refspec(url):
|
||||
if not url:
|
||||
return ('', '')
|
||||
return ('', '', None)
|
||||
|
||||
if url.endswith(('/tree/', '/tree', '/commit/', '/commit')):
|
||||
raise Exception('Malformed git URL and refspec ' + url + '!')
|
||||
@@ -211,13 +214,17 @@ def parse_github_url_and_refspec(url):
|
||||
if '/tree/' in url:
|
||||
if url.endswith('/'):
|
||||
raise Exception('Malformed git URL and refspec ' + url + '!')
|
||||
return url.split('/tree/')
|
||||
url, refspec = url.split('/tree/')
|
||||
remote_name = url.split('/')[-2]
|
||||
return (url, refspec, remote_name)
|
||||
elif '/commit/' in url:
|
||||
if url.endswith('/'):
|
||||
raise Exception('Malformed git URL and refspec ' + url + '!')
|
||||
return url.split('/commit/')
|
||||
url, refspec = url.split('/commit/')
|
||||
remote_name = url.split('/')[-2]
|
||||
return (url, refspec, remote_name)
|
||||
else:
|
||||
return (url, 'main') # Assume the default branch is main in the absence of a refspec
|
||||
return (url, 'main', None) # Assume the default branch is main in the absence of a refspec
|
||||
|
||||
|
||||
ARCHIVE_SUFFIXES = ('zip', '.tar', '.gz', '.xz', '.tbz2', '.bz2')
|
||||
@@ -812,40 +819,58 @@ def git_recent_commits(repo_path, n=20):
|
||||
return []
|
||||
|
||||
|
||||
def git_clone(url, dstpath, branch):
|
||||
def get_git_remotes(repo_path):
|
||||
remotes = []
|
||||
output = subprocess.check_output([GIT(), 'remote', '-v'], stderr=subprocess.STDOUT, text=True, cwd=repo_path)
|
||||
for line in output.splitlines():
|
||||
remotes += [line.split()[0]]
|
||||
return remotes
|
||||
|
||||
|
||||
def git_clone(url, dstpath, branch, remote_name='origin'):
|
||||
debug_print('git_clone(url=' + url + ', dstpath=' + dstpath + ')')
|
||||
if os.path.isdir(os.path.join(dstpath, '.git')):
|
||||
debug_print("Repository '" + url + "' already cloned to directory '" + dstpath + "', skipping.")
|
||||
return True
|
||||
remotes = get_git_remotes(dstpath)
|
||||
if remote_name in remotes:
|
||||
debug_print('Repository ' + url + ' with remote "' + remote_name + '" already cloned to directory ' + dstpath + ', skipping.')
|
||||
return True
|
||||
else:
|
||||
debug_print('Repository ' + url + ' with remote "' + remote_name + '" already cloned to directory ' + dstpath + ', but remote has not yet been added. Creating.')
|
||||
return run([GIT(), 'remote', 'add', remote_name, url], cwd=dstpath) == 0
|
||||
|
||||
mkdir_p(dstpath)
|
||||
git_clone_args = ['--recurse-submodules', '--branch', branch] # Do not check out a branch (installer will issue a checkout command right after)
|
||||
if GIT_CLONE_SHALLOW:
|
||||
git_clone_args += ['--depth', '1']
|
||||
print('Cloning from ' + url + '...')
|
||||
return run([GIT(), 'clone'] + git_clone_args + [url, dstpath]) == 0
|
||||
return run([GIT(), 'clone', '-o', remote_name] + git_clone_args + [url, dstpath]) == 0
|
||||
|
||||
|
||||
def git_pull(repo_path, branch_or_tag):
|
||||
debug_print('git_pull(repo_path=' + repo_path + ', branch/tag=' + branch_or_tag + ')')
|
||||
ret = run([GIT(), 'fetch', '--quiet', 'origin'], repo_path)
|
||||
def git_pull(repo_path, branch_or_tag, remote_name='origin'):
|
||||
debug_print('git_pull(repo_path=' + repo_path + ', branch/tag=' + branch_or_tag + ', remote_name=' + remote_name + ')')
|
||||
ret = run([GIT(), 'fetch', '--quiet', remote_name], repo_path)
|
||||
if ret != 0:
|
||||
return False
|
||||
try:
|
||||
print("Fetching latest changes to the branch/tag '" + branch_or_tag + "' for '" + repo_path + "'...")
|
||||
ret = run([GIT(), 'fetch', '--quiet', 'origin'], repo_path)
|
||||
if ret != 0:
|
||||
return False
|
||||
# this line assumes that the user has not gone and manually messed with the
|
||||
# repo and added new remotes to ambiguate the checkout.
|
||||
ret = run([GIT(), 'checkout', '--recurse-submodules', '--quiet', branch_or_tag], repo_path)
|
||||
ret = run([GIT(), 'fetch', '--quiet', remote_name], repo_path)
|
||||
if ret != 0:
|
||||
return False
|
||||
# Test if branch_or_tag is a branch, or if it is a tag that needs to be updated
|
||||
target_is_tag = run([GIT(), 'symbolic-ref', '-q', 'HEAD'], repo_path, quiet=True)
|
||||
|
||||
if target_is_tag:
|
||||
ret = run([GIT(), 'checkout', '--recurse-submodules', '--quiet', branch_or_tag], repo_path)
|
||||
else:
|
||||
local_branch_prefix = (remote_name + '_') if remote_name != 'origin' else ''
|
||||
ret = run([GIT(), 'checkout', '--recurse-submodules', '--quiet', '-B', local_branch_prefix + branch_or_tag,
|
||||
'--track', remote_name + '/' + branch_or_tag], repo_path)
|
||||
if ret != 0:
|
||||
return False
|
||||
if not target_is_tag:
|
||||
# update branch to latest (not needed for tags)
|
||||
# this line assumes that the user has not gone and made local changes to the repo
|
||||
ret = run([GIT(), 'merge', '--ff-only', 'origin/' + branch_or_tag], repo_path)
|
||||
ret = run([GIT(), 'merge', '--ff-only', remote_name + '/' + branch_or_tag], repo_path)
|
||||
if ret != 0:
|
||||
return False
|
||||
run([GIT(), 'submodule', 'update', '--init'], repo_path, quiet=True)
|
||||
@@ -857,14 +882,16 @@ def git_pull(repo_path, branch_or_tag):
|
||||
return True
|
||||
|
||||
|
||||
def git_clone_checkout_and_pull(url, dstpath, branch):
|
||||
debug_print('git_clone_checkout_and_pull(url=' + url + ', dstpath=' + dstpath + ', branch=' + branch + ')')
|
||||
def git_clone_checkout_and_pull(url, dstpath, branch, override_remote_name='origin'):
|
||||
debug_print('git_clone_checkout_and_pull(url=' + url + ', dstpath=' + dstpath + ', branch=' + branch + ', override_remote_name=' + override_remote_name + ')')
|
||||
|
||||
# If the repository has already been cloned before, issue a pull operation. Otherwise do a new clone.
|
||||
if os.path.isdir(os.path.join(dstpath, '.git')):
|
||||
return git_pull(dstpath, branch)
|
||||
else:
|
||||
return git_clone(url, dstpath, branch)
|
||||
# Make sure the repository is cloned first
|
||||
success = git_clone(url, dstpath, branch, override_remote_name)
|
||||
if not success:
|
||||
return False
|
||||
|
||||
# And/or issue a pull/checkout to get to latest code.
|
||||
return git_pull(dstpath, branch, override_remote_name)
|
||||
|
||||
|
||||
# Each tool can have its own build type, or it can be overridden on the command
|
||||
@@ -1856,7 +1883,7 @@ class Tool(object):
|
||||
elif hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_ccache':
|
||||
success = build_ccache(self)
|
||||
elif hasattr(self, 'git_branch'):
|
||||
success = git_clone_checkout_and_pull(url, self.installation_path(), self.git_branch)
|
||||
success = git_clone_checkout_and_pull(url, self.installation_path(), self.git_branch, getattr(self, 'remote_name', 'origin'))
|
||||
elif url.endswith(ARCHIVE_SUFFIXES):
|
||||
success = download_and_extract(url, self.installation_path(),
|
||||
filename_prefix=getattr(self, 'download_prefix', ''))
|
||||
@@ -2826,7 +2853,7 @@ def main(args):
|
||||
errlog('Failed to find tool ' + tool_name + '!')
|
||||
return False
|
||||
else:
|
||||
t.url, t.git_branch = parse_github_url_and_refspec(url_and_refspec)
|
||||
t.url, t.git_branch, t.remote_name = parse_github_url_and_refspec(url_and_refspec)
|
||||
debug_print('Reading git repository URL "' + t.url + '" and git branch "' + t.git_branch + '" for Tool "' + tool_name + '".')
|
||||
|
||||
forked_url = extract_string_arg('--override-repository')
|
||||
|
||||
Reference in New Issue
Block a user