msubprojects: Handle wrap-file to wrap-git case

pull/7844/head
Xavier Claessens 4 years ago committed by Nirbheek Chauhan
parent 3ade5bbd92
commit dccff1f2bc
  1. 3
      docs/markdown/Subprojects.md
  2. 7
      docs/markdown/snippets/subprojects_update.md
  3. 52
      mesonbuild/msubprojects.py
  4. 14
      run_unittests.py

@ -315,6 +315,9 @@ To pull latest version of all your subprojects at once, just run the command:
- *Since 0.56.0* if the `url` specified in wrap file is different to the URL set
on `origin` for a git repository it will not be updated, unless `--reset` is
specified in which case the URL of `origin` will be reset first.
- *Since 0.56.0* If the subproject directory is not a git repository but has a
`[wrap-git]` the subproject is ignored, unless `--reset` is specified in which
case the directory is deleted and the new repository is cloned.
### Start a topic branch across all git subprojects

@ -19,13 +19,18 @@ subprojects, but at the end an error code is now returned.
The `update` subcommand has been reworked:
- In the case the URL of `origin` is different as the `url` set in wrap file,
the subproject will not be updated unless `--reset` is specified (see below).
- In the case a subproject directory exists and is not a git repository but has
a `[wrap-git]`, meson used to run git commands that would wrongly apply to the
main project. It now skip the subproject unless `--reset` is specified (see below).
- The `--rebase` behaviour is now the default for consistency: it was
already rebasing when current branch and revision are the same, it is
less confusing to rebase when they are different too.
- Add `--reset` mode that checkout the new branch and hard reset that
branch to remote commit. This new mode guarantees that every
subproject are exactly at the wrap's revision. In addition the URL of `origin`
is updated in case it changed in the wrap file.
is updated in case it changed in the wrap file. If the subproject directory is
not a git repository but has a `[wrap-git]` the directory is deleted and the
new repository is cloned.
- Local changes are always stashed first to avoid any data loss. In the
worst case scenario the user can always check reflog and stash list to
rollback.

@ -3,7 +3,7 @@ import argparse
from ._pathlib import Path
from . import mlog
from .mesonlib import quiet_git, verbose_git, GitException, Popen_safe, MesonException
from .mesonlib import quiet_git, verbose_git, GitException, Popen_safe, MesonException, windows_proof_rmtree
from .wrap.wrap import API_ROOT, Resolver, WrapException, ALL_TYPES
from .wrap import wraptool
@ -26,7 +26,7 @@ def update_wrapdb_file(wrap, repo_dir, options):
mlog.log(*msg)
return True
def update_file(wrap, repo_dir, options):
def update_file(r, wrap, repo_dir, options):
patch_url = wrap.values.get('patch_url', '')
if patch_url.startswith(API_ROOT):
return update_wrapdb_file(wrap, repo_dir, options)
@ -116,10 +116,25 @@ def git_checkout_and_rebase(repo_dir, revision):
success = git_rebase(repo_dir, revision)
return success
def update_git(wrap, repo_dir, options):
def update_git(r, wrap, repo_dir, options):
if not os.path.isdir(repo_dir):
mlog.log(' -> Not used.')
return True
if not os.path.exists(os.path.join(repo_dir, '.git')):
if options.reset:
# Delete existing directory and redownload
windows_proof_rmtree(repo_dir)
try:
r.resolve(wrap.name, 'meson')
update_git_done(repo_dir)
return True
except WrapException as e:
mlog.log(' ->', mlog.red(str(e)))
return False
else:
mlog.log(' -> Not a git repository.')
mlog.log('Pass --reset option to delete directory and redownload.')
return False
revision = wrap.values.get('revision')
url = wrap.values.get('url')
push_url = wrap.values.get('push-url')
@ -193,13 +208,15 @@ def update_git(wrap, repo_dir, options):
success = git_checkout_and_reset(repo_dir, revision)
else:
success = git_rebase(repo_dir, revision)
if success:
git_output(['submodule', 'update', '--checkout', '--recursive'], repo_dir)
git_show(repo_dir)
update_git_done(repo_dir)
return success
def update_hg(wrap, repo_dir, options):
def update_git_done(repo_dir):
git_output(['submodule', 'update', '--checkout', '--recursive'], repo_dir)
git_show(repo_dir)
def update_hg(r, wrap, repo_dir, options):
if not os.path.isdir(repo_dir):
mlog.log(' -> Not used.')
return True
@ -215,7 +232,7 @@ def update_hg(wrap, repo_dir, options):
subprocess.check_call(['hg', 'checkout', revno], cwd=repo_dir)
return True
def update_svn(wrap, repo_dir, options):
def update_svn(r, wrap, repo_dir, options):
if not os.path.isdir(repo_dir):
mlog.log(' -> Not used.')
return True
@ -233,21 +250,21 @@ def update_svn(wrap, repo_dir, options):
subprocess.check_call(['svn', 'update', '-r', revno], cwd=repo_dir)
return True
def update(wrap, repo_dir, options):
def update(r, wrap, repo_dir, options):
mlog.log('Updating {}...'.format(wrap.name))
if wrap.type == 'file':
return update_file(wrap, repo_dir, options)
return update_file(r, wrap, repo_dir, options)
elif wrap.type == 'git':
return update_git(wrap, repo_dir, options)
return update_git(r, wrap, repo_dir, options)
elif wrap.type == 'hg':
return update_hg(wrap, repo_dir, options)
return update_hg(r, wrap, repo_dir, options)
elif wrap.type == 'svn':
return update_svn(wrap, repo_dir, options)
return update_svn(r, wrap, repo_dir, options)
else:
mlog.log(' -> Cannot update', wrap.type, 'subproject')
return True
def checkout(wrap, repo_dir, options):
def checkout(r, wrap, repo_dir, options):
if wrap.type != 'git' or not os.path.isdir(repo_dir):
return True
branch_name = options.branch_name if options.branch_name else wrap.get('revision')
@ -260,13 +277,12 @@ def checkout(wrap, repo_dir, options):
return True
return False
def download(wrap, repo_dir, options):
def download(r, wrap, repo_dir, options):
mlog.log('Download {}...'.format(wrap.name))
if os.path.isdir(repo_dir):
mlog.log(' -> Already downloaded')
return True
try:
r = Resolver(os.path.dirname(repo_dir))
r.resolve(wrap.name, 'meson')
mlog.log(' -> done')
except WrapException as e:
@ -274,7 +290,7 @@ def download(wrap, repo_dir, options):
return False
return True
def foreach(wrap, repo_dir, options):
def foreach(r, wrap, repo_dir, options):
mlog.log('Executing command in {}'.format(repo_dir))
if not os.path.isdir(repo_dir):
mlog.log(' -> Not downloaded yet')
@ -362,7 +378,7 @@ def run(options):
if types and wrap.type not in types:
continue
dirname = Path(subprojects_dir, wrap.directory).as_posix()
if not options.subprojects_func(wrap, dirname, options):
if not options.subprojects_func(r, wrap, dirname, options):
failures.append(wrap.name)
if failures:
m = 'Please check logs above as command failed in some subprojects which could have been left in conflict state: '

@ -9151,6 +9151,20 @@ class SubprojectsCommandTests(BasePlatformTests):
self.assertEqual(self._git_local_branch(subp_name), '')
self.assertEqual(self._git_local_commit(subp_name), new_commit)
# Create a local project not in a git repository, then update it with
# a git wrap. Without --reset it should print error message and return
# failure. With --reset it should delete existing project and clone the
# new project.
subp_name = 'sub2'
self._create_project(self.subprojects_dir / subp_name)
self._git_create_remote_repo(subp_name)
self._wrap_create_git(subp_name)
with self.assertRaises(subprocess.CalledProcessError) as cm:
self._subprojects_cmd(['update'])
self.assertIn('Not a git repository', cm.exception.output)
self._subprojects_cmd(['update', '--reset'])
self.assertEqual(self._git_local_commit(subp_name), self._git_remote_commit(subp_name))
@skipIfNoExecutable('true')
def test_foreach(self):
self._create_project(self.subprojects_dir / 'sub_file')

Loading…
Cancel
Save