aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2020-08-07 14:55:08 -0400
committerXavier Claessens <xclaesse@gmail.com>2020-09-10 11:39:30 -0400
commitd3c06bb023c5fe47d29721af732d7c52ff31c792 (patch)
treea512648cdc5e4d1c13e31873eae0e817f83aad0f
parent8cb5e3d7d2a325bfe3d02e87650d47f9e9d06234 (diff)
downloadmeson-d3c06bb023c5fe47d29721af732d7c52ff31c792.zip
meson-d3c06bb023c5fe47d29721af732d7c52ff31c792.tar.gz
meson-d3c06bb023c5fe47d29721af732d7c52ff31c792.tar.bz2
msubprojects: Rework update command
Besides refactoring code into smaller functions: - Makes the --rebase behaviour 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. - 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. Fixes: #7526
-rwxr-xr-xmesonbuild/msubprojects.py148
1 files changed, 96 insertions, 52 deletions
diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py
index 49e759b..50ffb3e 100755
--- a/mesonbuild/msubprojects.py
+++ b/mesonbuild/msubprojects.py
@@ -45,11 +45,74 @@ def git_output(cmd, workingdir):
return git(cmd, workingdir, check=True, universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout
+def git_stash(workingdir):
+ # Don't pipe stdout here because we want the user to see his changes have
+ # been saved.
+ git(['stash'], workingdir, check=True, universal_newlines=True)
+
def git_show(repo_dir):
commit_message = git_output(['show', '--quiet', '--pretty=format:%h%n%d%n%s%n[%an]'], repo_dir)
parts = [s.strip() for s in commit_message.split('\n')]
mlog.log(' ->', mlog.yellow(parts[0]), mlog.red(parts[1]), parts[2], mlog.blue(parts[3]))
+def git_rebase(repo_dir, revision):
+ try:
+ git_output(['-c', 'rebase.autoStash=true', 'rebase', 'FETCH_HEAD'], repo_dir)
+ except subprocess.CalledProcessError as e:
+ out = e.output.strip()
+ mlog.log(' -> Could not rebase', mlog.bold(repo_dir), 'onto', mlog.bold(revision))
+ mlog.log(mlog.red(out))
+ mlog.log(mlog.red(str(e)))
+ return False
+ return True
+
+def git_reset(repo_dir, revision):
+ try:
+ # Stash local changes, commits can always be found back in reflog, to
+ # avoid any data lost by mistake.
+ git_stash(repo_dir)
+ git_output(['reset', '--hard', 'FETCH_HEAD'], repo_dir)
+ except subprocess.CalledProcessError as e:
+ out = e.output.strip()
+ mlog.log(' -> Could not reset', mlog.bold(repo_dir), 'to', mlog.bold(revision))
+ mlog.log(mlog.red(out))
+ mlog.log(mlog.red(str(e)))
+ return False
+ return True
+
+def git_checkout(repo_dir, revision, create=False):
+ cmd = ['checkout', revision, '--']
+ if create:
+ cmd.insert('-b', 1)
+ try:
+ # Stash local changes, commits can always be found back in reflog, to
+ # avoid any data lost by mistake.
+ git_stash(repo_dir)
+ git_output(cmd, repo_dir)
+ except subprocess.CalledProcessError as e:
+ out = e.output.strip()
+ mlog.log(' -> Could not checkout', mlog.bold(revision), 'in', mlog.bold(repo_dir))
+ mlog.log(mlog.red(out))
+ mlog.log(mlog.red(str(e)))
+ return False
+ return True
+
+def git_checkout_and_reset(repo_dir, revision):
+ # revision could be a branch that already exists but is outdated, so we still
+ # have to reset after the checkout.
+ success = git_checkout(repo_dir, revision)
+ if success:
+ success = git_reset(repo_dir, revision)
+ return success
+
+def git_checkout_and_rebase(repo_dir, revision):
+ # revision could be a branch that already exists but is outdated, so we still
+ # have to rebase after the checkout.
+ success = git_checkout(repo_dir, revision)
+ if success:
+ success = git_rebase(repo_dir, revision)
+ return success
+
def update_git(wrap, repo_dir, options):
if not os.path.isdir(repo_dir):
mlog.log(' -> Not used.')
@@ -60,48 +123,36 @@ def update_git(wrap, repo_dir, options):
mlog.log(' -> No revision specified.')
return True
branch = git_output(['branch', '--show-current'], repo_dir).strip()
+ # Fetch only the revision we need, this avoids fetching useless branches and
+ # is needed for http case were new remote branches wouldn't be discovered
+ # otherwise. After this command, FETCH_HEAD is the revision we want.
+ git_output(['fetch', 'origin', revision], repo_dir)
if branch == '':
- try:
- # We are currently in detached mode, just checkout the new revision
- git_output(['fetch'], repo_dir)
- git_output(['checkout', revision], repo_dir)
- except subprocess.CalledProcessError as e:
- out = e.output.strip()
- mlog.log(' -> Could not checkout revision', mlog.cyan(revision))
- mlog.log(mlog.red(out))
- mlog.log(mlog.red(str(e)))
- return False
+ # We are currently in detached mode
+ if options.reset:
+ success = git_checkout_and_reset(repo_dir, revision)
+ else:
+ success = git_checkout_and_rebase(repo_dir, revision)
elif branch == revision:
- try:
- # We are in the same branch, pull latest commits
- git_output(['-c', 'rebase.autoStash=true', 'pull', '--rebase'], repo_dir)
- except subprocess.CalledProcessError as e:
- out = e.output.strip()
- mlog.log(' -> Could not rebase', mlog.bold(repo_dir), 'please fix and try again.')
- mlog.log(mlog.red(out))
- mlog.log(mlog.red(str(e)))
- return False
+ # We are in the same branch. A reset could still be needed in the case
+ # a force push happened on remote repository.
+ if options.reset:
+ success = git_reset(repo_dir, revision)
+ else:
+ success = git_rebase(repo_dir, revision)
else:
- # We are in another branch, probably user created their own branch and
- # we should rebase it on top of wrap's branch.
- if options.rebase:
- try:
- git_output(['fetch'], repo_dir)
- git_output(['-c', 'rebase.autoStash=true', 'rebase', revision], repo_dir)
- except subprocess.CalledProcessError as e:
- out = e.output.strip()
- mlog.log(' -> Could not rebase', mlog.bold(repo_dir), 'please fix and try again.')
- mlog.log(mlog.red(out))
- mlog.log(mlog.red(str(e)))
- return False
+ # We are in another branch, either the user created their own branch and
+ # we should rebase it, or revision changed in the wrap file and we need
+ # to checkout the new branch.
+ if options.reset:
+ success = git_checkout_and_reset(repo_dir, revision)
else:
- mlog.log(' -> Target revision is', mlog.bold(revision), 'but currently in branch is', mlog.bold(ret), '\n' +
- ' To rebase your branch on top of', mlog.bold(revision), 'use', mlog.bold('--rebase'), 'option.')
- return True
+ success = git_rebase(repo_dir, revision)
- git_output(['submodule', 'update', '--checkout', '--recursive'], repo_dir)
- git_show(repo_dir)
- return True
+ if success:
+ git_output(['submodule', 'update', '--checkout', '--recursive'], repo_dir)
+ git_show(repo_dir)
+ return success
def update_hg(wrap, repo_dir, options):
if not os.path.isdir(repo_dir):
@@ -158,21 +209,11 @@ def checkout(wrap, repo_dir, options):
if not branch_name:
# It could be a detached git submodule for example.
return True
- cmd = ['checkout', branch_name, '--']
- if options.b:
- cmd.insert(1, '-b')
mlog.log('Checkout {} in {}...'.format(branch_name, wrap.name))
- try:
- # Stash any pending changes. Don't use git_output() here because we want
- # the user to see his changes have been saved.
- git(['stash'], repo_dir, check=True, universal_newlines=True)
- git_output(cmd, repo_dir)
+ if git_checkout(repo_dir, branch_name, create=options.b):
git_show(repo_dir)
- except subprocess.CalledProcessError as e:
- out = e.output.strip()
- mlog.log(' -> ', mlog.red(out))
- return False
- return True
+ return True
+ return False
def download(wrap, repo_dir, options):
mlog.log('Download {}...'.format(wrap.name))
@@ -222,8 +263,11 @@ def add_arguments(parser):
subparsers.required = True
p = subparsers.add_parser('update', help='Update all subprojects from wrap files')
- p.add_argument('--rebase', default=False, action='store_true',
- help='Rebase your branch on top of wrap\'s revision (git only)')
+ p.add_argument('--rebase', default=True, action='store_true',
+ help='Rebase your branch on top of wrap\'s revision. ' + \
+ 'Deprecated, it is now the default behaviour. (git only)')
+ p.add_argument('--reset', default=False, action='store_true',
+ help='Checkout wrap\'s revision and hard reset to that commit. (git only)')
add_common_arguments(p)
add_subprojects_argument(p)
p.set_defaults(subprojects_func=update)