Skip to content

Commit 0c45297

Browse files
authored
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.
1 parent 8ad0ba0 commit 0c45297

File tree

1 file changed

+55
-28
lines changed

1 file changed

+55
-28
lines changed

emsdk.py

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -200,24 +200,31 @@ def to_unix_path(p):
200200
EMSDK_SET_ENV = os.path.join(EMSDK_PATH, 'emsdk_set_env.bat')
201201

202202

203-
# Parses https://github.com/emscripten-core/emscripten/tree/d6aced8 to a pair (https://github.com/emscripten-core/emscripten, d6aced8)
203+
# Parses https://github.com/emscripten-core/emscripten/tree/d6aced8 to a triplet
204+
# (https://github.com/emscripten-core/emscripten, d6aced8, emscripten-core)
205+
# or https://github.com/emscripten-core/emscripten/commit/00b76f81f6474113fcf540db69297cfeb180347e
206+
# to (https://github.com/emscripten-core/emscripten, 00b76f81f6474113fcf540db69297cfeb180347e, emscripten-core)
204207
def parse_github_url_and_refspec(url):
205208
if not url:
206-
return ('', '')
209+
return ('', '', None)
207210

208211
if url.endswith(('/tree/', '/tree', '/commit/', '/commit')):
209212
raise Exception('Malformed git URL and refspec ' + url + '!')
210213

211214
if '/tree/' in url:
212215
if url.endswith('/'):
213216
raise Exception('Malformed git URL and refspec ' + url + '!')
214-
return url.split('/tree/')
217+
url, refspec = url.split('/tree/')
218+
remote_name = url.split('/')[-2]
219+
return (url, refspec, remote_name)
215220
elif '/commit/' in url:
216221
if url.endswith('/'):
217222
raise Exception('Malformed git URL and refspec ' + url + '!')
218-
return url.split('/commit/')
223+
url, refspec = url.split('/commit/')
224+
remote_name = url.split('/')[-2]
225+
return (url, refspec, remote_name)
219226
else:
220-
return (url, 'main') # Assume the default branch is main in the absence of a refspec
227+
return (url, 'main', None) # Assume the default branch is main in the absence of a refspec
221228

222229

223230
ARCHIVE_SUFFIXES = ('zip', '.tar', '.gz', '.xz', '.tbz2', '.bz2')
@@ -812,40 +819,58 @@ def git_recent_commits(repo_path, n=20):
812819
return []
813820

814821

815-
def git_clone(url, dstpath, branch):
822+
def get_git_remotes(repo_path):
823+
remotes = []
824+
output = subprocess.check_output([GIT(), 'remote', '-v'], stderr=subprocess.STDOUT, text=True, cwd=repo_path)
825+
for line in output.splitlines():
826+
remotes += [line.split()[0]]
827+
return remotes
828+
829+
830+
def git_clone(url, dstpath, branch, remote_name='origin'):
816831
debug_print('git_clone(url=' + url + ', dstpath=' + dstpath + ')')
817832
if os.path.isdir(os.path.join(dstpath, '.git')):
818-
debug_print("Repository '" + url + "' already cloned to directory '" + dstpath + "', skipping.")
819-
return True
833+
remotes = get_git_remotes(dstpath)
834+
if remote_name in remotes:
835+
debug_print('Repository ' + url + ' with remote "' + remote_name + '" already cloned to directory ' + dstpath + ', skipping.')
836+
return True
837+
else:
838+
debug_print('Repository ' + url + ' with remote "' + remote_name + '" already cloned to directory ' + dstpath + ', but remote has not yet been added. Creating.')
839+
return run([GIT(), 'remote', 'add', remote_name, url], cwd=dstpath) == 0
840+
820841
mkdir_p(dstpath)
821842
git_clone_args = ['--recurse-submodules', '--branch', branch] # Do not check out a branch (installer will issue a checkout command right after)
822843
if GIT_CLONE_SHALLOW:
823844
git_clone_args += ['--depth', '1']
824845
print('Cloning from ' + url + '...')
825-
return run([GIT(), 'clone'] + git_clone_args + [url, dstpath]) == 0
846+
return run([GIT(), 'clone', '-o', remote_name] + git_clone_args + [url, dstpath]) == 0
826847

827848

828-
def git_pull(repo_path, branch_or_tag):
829-
debug_print('git_pull(repo_path=' + repo_path + ', branch/tag=' + branch_or_tag + ')')
830-
ret = run([GIT(), 'fetch', '--quiet', 'origin'], repo_path)
849+
def git_pull(repo_path, branch_or_tag, remote_name='origin'):
850+
debug_print('git_pull(repo_path=' + repo_path + ', branch/tag=' + branch_or_tag + ', remote_name=' + remote_name + ')')
851+
ret = run([GIT(), 'fetch', '--quiet', remote_name], repo_path)
831852
if ret != 0:
832853
return False
833854
try:
834855
print("Fetching latest changes to the branch/tag '" + branch_or_tag + "' for '" + repo_path + "'...")
835-
ret = run([GIT(), 'fetch', '--quiet', 'origin'], repo_path)
836-
if ret != 0:
837-
return False
838-
# this line assumes that the user has not gone and manually messed with the
839-
# repo and added new remotes to ambiguate the checkout.
840-
ret = run([GIT(), 'checkout', '--recurse-submodules', '--quiet', branch_or_tag], repo_path)
856+
ret = run([GIT(), 'fetch', '--quiet', remote_name], repo_path)
841857
if ret != 0:
842858
return False
843859
# Test if branch_or_tag is a branch, or if it is a tag that needs to be updated
844860
target_is_tag = run([GIT(), 'symbolic-ref', '-q', 'HEAD'], repo_path, quiet=True)
861+
862+
if target_is_tag:
863+
ret = run([GIT(), 'checkout', '--recurse-submodules', '--quiet', branch_or_tag], repo_path)
864+
else:
865+
local_branch_prefix = (remote_name + '_') if remote_name != 'origin' else ''
866+
ret = run([GIT(), 'checkout', '--recurse-submodules', '--quiet', '-B', local_branch_prefix + branch_or_tag,
867+
'--track', remote_name + '/' + branch_or_tag], repo_path)
868+
if ret != 0:
869+
return False
845870
if not target_is_tag:
846871
# update branch to latest (not needed for tags)
847872
# this line assumes that the user has not gone and made local changes to the repo
848-
ret = run([GIT(), 'merge', '--ff-only', 'origin/' + branch_or_tag], repo_path)
873+
ret = run([GIT(), 'merge', '--ff-only', remote_name + '/' + branch_or_tag], repo_path)
849874
if ret != 0:
850875
return False
851876
run([GIT(), 'submodule', 'update', '--init'], repo_path, quiet=True)
@@ -857,14 +882,16 @@ def git_pull(repo_path, branch_or_tag):
857882
return True
858883

859884

860-
def git_clone_checkout_and_pull(url, dstpath, branch):
861-
debug_print('git_clone_checkout_and_pull(url=' + url + ', dstpath=' + dstpath + ', branch=' + branch + ')')
885+
def git_clone_checkout_and_pull(url, dstpath, branch, override_remote_name='origin'):
886+
debug_print('git_clone_checkout_and_pull(url=' + url + ', dstpath=' + dstpath + ', branch=' + branch + ', override_remote_name=' + override_remote_name + ')')
862887

863-
# If the repository has already been cloned before, issue a pull operation. Otherwise do a new clone.
864-
if os.path.isdir(os.path.join(dstpath, '.git')):
865-
return git_pull(dstpath, branch)
866-
else:
867-
return git_clone(url, dstpath, branch)
888+
# Make sure the repository is cloned first
889+
success = git_clone(url, dstpath, branch, override_remote_name)
890+
if not success:
891+
return False
892+
893+
# And/or issue a pull/checkout to get to latest code.
894+
return git_pull(dstpath, branch, override_remote_name)
868895

869896

870897
# Each tool can have its own build type, or it can be overridden on the command
@@ -1856,7 +1883,7 @@ def install_tool(self):
18561883
elif hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_ccache':
18571884
success = build_ccache(self)
18581885
elif hasattr(self, 'git_branch'):
1859-
success = git_clone_checkout_and_pull(url, self.installation_path(), self.git_branch)
1886+
success = git_clone_checkout_and_pull(url, self.installation_path(), self.git_branch, getattr(self, 'remote_name', 'origin'))
18601887
elif url.endswith(ARCHIVE_SUFFIXES):
18611888
success = download_and_extract(url, self.installation_path(),
18621889
filename_prefix=getattr(self, 'download_prefix', ''))
@@ -2826,7 +2853,7 @@ def extract_string_arg(name):
28262853
errlog('Failed to find tool ' + tool_name + '!')
28272854
return False
28282855
else:
2829-
t.url, t.git_branch = parse_github_url_and_refspec(url_and_refspec)
2856+
t.url, t.git_branch, t.remote_name = parse_github_url_and_refspec(url_and_refspec)
28302857
debug_print('Reading git repository URL "' + t.url + '" and git branch "' + t.git_branch + '" for Tool "' + tool_name + '".')
28312858

28322859
forked_url = extract_string_arg('--override-repository')

0 commit comments

Comments
 (0)