Fix git shallow update (#3912)
* remove unused variables
* fetch branch name instead of HEAD
fix #3782, which was introduced by f1bacc1d3f
* disable git depth option for old git versions
fixes #3782
git support for `--depth` did not fully work in old git versions (before 1.8.2)
fall back to full clones/fetches on those versions
* raise required git version to 1.9.1 for depth option
* use correct depth argument in switch_version
This commit is contained in:
parent
e25cb4df77
commit
5d928ca13c
1 changed files with 37 additions and 16 deletions
|
@ -110,7 +110,7 @@ options:
|
||||||
description:
|
description:
|
||||||
- Create a shallow clone with a history truncated to the specified
|
- Create a shallow clone with a history truncated to the specified
|
||||||
number or revisions. The minimum possible value is C(1), otherwise
|
number or revisions. The minimum possible value is C(1), otherwise
|
||||||
ignored. Needs I(git>=1.8.3) to work correctly.
|
ignored. Needs I(git>=1.9.1) to work correctly.
|
||||||
clone:
|
clone:
|
||||||
required: false
|
required: false
|
||||||
default: "yes"
|
default: "yes"
|
||||||
|
@ -206,6 +206,7 @@ EXAMPLES = '''
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
def get_submodule_update_params(module, git_path, cwd):
|
def get_submodule_update_params(module, git_path, cwd):
|
||||||
|
|
||||||
|
@ -523,12 +524,12 @@ def fetch(git_path, module, repo, dest, version, remote, depth, bare, refspec):
|
||||||
if depth:
|
if depth:
|
||||||
# try to find the minimal set of refs we need to fetch to get a
|
# try to find the minimal set of refs we need to fetch to get a
|
||||||
# successful checkout
|
# successful checkout
|
||||||
|
currenthead = get_head_branch(git_path, module, dest, remote)
|
||||||
if refspec:
|
if refspec:
|
||||||
refspecs.append(refspec)
|
refspecs.append(refspec)
|
||||||
elif version == 'HEAD':
|
elif version == 'HEAD':
|
||||||
refspecs.append('HEAD')
|
refspecs.append(currenthead)
|
||||||
elif is_remote_branch(git_path, module, dest, repo, version):
|
elif is_remote_branch(git_path, module, dest, repo, version):
|
||||||
currenthead = get_head_branch(git_path, module, dest, remote)
|
|
||||||
if currenthead != version:
|
if currenthead != version:
|
||||||
# this workaroung is only needed for older git versions
|
# this workaroung is only needed for older git versions
|
||||||
# 1.8.3 is broken, 1.9.x works
|
# 1.8.3 is broken, 1.9.x works
|
||||||
|
@ -541,6 +542,7 @@ def fetch(git_path, module, repo, dest, version, remote, depth, bare, refspec):
|
||||||
refspecs.append('+refs/tags/'+version+':refs/tags/'+version)
|
refspecs.append('+refs/tags/'+version+':refs/tags/'+version)
|
||||||
if refspecs:
|
if refspecs:
|
||||||
# if refspecs is empty, i.e. version is neither heads nor tags
|
# if refspecs is empty, i.e. version is neither heads nor tags
|
||||||
|
# assume it is a version hash
|
||||||
# fall back to a full clone, otherwise we might not be able to checkout
|
# fall back to a full clone, otherwise we might not be able to checkout
|
||||||
# version
|
# version
|
||||||
fetch_cmd.extend(['--depth', str(depth)])
|
fetch_cmd.extend(['--depth', str(depth)])
|
||||||
|
@ -655,12 +657,11 @@ def set_remote_branch(git_path, module, dest, remote, version, depth):
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
module.fail_json(msg="Failed to fetch branch from remote: %s" % version, stdout=out, stderr=err, rc=rc)
|
module.fail_json(msg="Failed to fetch branch from remote: %s" % version, stdout=out, stderr=err, rc=rc)
|
||||||
|
|
||||||
def switch_version(git_path, module, dest, remote, version, verify_commit):
|
def switch_version(git_path, module, dest, remote, version, verify_commit, depth):
|
||||||
cmd = ''
|
cmd = ''
|
||||||
if version != 'HEAD':
|
if version != 'HEAD':
|
||||||
if is_remote_branch(git_path, module, dest, remote, version):
|
if is_remote_branch(git_path, module, dest, remote, version):
|
||||||
if not is_local_branch(git_path, module, dest, version):
|
if not is_local_branch(git_path, module, dest, version):
|
||||||
depth = module.params['depth']
|
|
||||||
if depth:
|
if depth:
|
||||||
# git clone --depth implies --single-branch, which makes
|
# git clone --depth implies --single-branch, which makes
|
||||||
# the checkout fail if the version changes
|
# the checkout fail if the version changes
|
||||||
|
@ -703,6 +704,20 @@ def verify_commit_sign(git_path, module, dest, version):
|
||||||
module.fail_json(msg='Failed to verify GPG signature of commit/tag "%s"' % version, stdout=out, stderr=err, rc=rc)
|
module.fail_json(msg='Failed to verify GPG signature of commit/tag "%s"' % version, stdout=out, stderr=err, rc=rc)
|
||||||
return (rc, out, err)
|
return (rc, out, err)
|
||||||
|
|
||||||
|
|
||||||
|
def git_version(git_path, module):
|
||||||
|
"""return the installed version of git"""
|
||||||
|
cmd = "%s --version" % git_path
|
||||||
|
(rc, out, err) = module.run_command(cmd)
|
||||||
|
if rc != 0:
|
||||||
|
# one could fail_json here, but the version info is not that important, so let's try to fail only on actual git commands
|
||||||
|
return None
|
||||||
|
rematch = re.search('git version (.*)$', out)
|
||||||
|
if not rematch:
|
||||||
|
return None
|
||||||
|
return LooseVersion(rematch.groups()[0])
|
||||||
|
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -746,6 +761,9 @@ def main():
|
||||||
key_file = module.params['key_file']
|
key_file = module.params['key_file']
|
||||||
ssh_opts = module.params['ssh_opts']
|
ssh_opts = module.params['ssh_opts']
|
||||||
|
|
||||||
|
return_values = {}
|
||||||
|
return_values['warnings'] = []
|
||||||
|
|
||||||
# We screenscrape a huge amount of git commands so use C locale anytime we
|
# We screenscrape a huge amount of git commands so use C locale anytime we
|
||||||
# call run_command()
|
# call run_command()
|
||||||
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C', LC_CTYPE='C')
|
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C', LC_CTYPE='C')
|
||||||
|
@ -775,12 +793,15 @@ def main():
|
||||||
add_git_host_key(module, repo, accept_hostkey=module.params['accept_hostkey'])
|
add_git_host_key(module, repo, accept_hostkey=module.params['accept_hostkey'])
|
||||||
else:
|
else:
|
||||||
add_git_host_key(module, repo, accept_hostkey=module.params['accept_hostkey'])
|
add_git_host_key(module, repo, accept_hostkey=module.params['accept_hostkey'])
|
||||||
|
git_version_used = git_version(git_path, module)
|
||||||
|
|
||||||
|
if depth is not None and git_version_used < LooseVersion('1.9.1'):
|
||||||
|
return_values['warnings'].append("Your git version is too old to fully support the depth argument. Falling back to full checkouts.")
|
||||||
|
depth = None
|
||||||
|
|
||||||
recursive = module.params['recursive']
|
recursive = module.params['recursive']
|
||||||
track_submodules = module.params['track_submodules']
|
track_submodules = module.params['track_submodules']
|
||||||
|
|
||||||
rc, out, err, status = (0, None, None, None)
|
|
||||||
|
|
||||||
before = None
|
before = None
|
||||||
local_mods = False
|
local_mods = False
|
||||||
repo_updated = None
|
repo_updated = None
|
||||||
|
@ -791,7 +812,7 @@ def main():
|
||||||
# In those cases we do an ls-remote
|
# In those cases we do an ls-remote
|
||||||
if module.check_mode or not allow_clone:
|
if module.check_mode or not allow_clone:
|
||||||
remote_head = get_remote_head(git_path, module, dest, version, repo, bare)
|
remote_head = get_remote_head(git_path, module, dest, version, repo, bare)
|
||||||
module.exit_json(changed=True, before=before, after=remote_head)
|
module.exit_json(changed=True, before=before, after=remote_head, **return_values)
|
||||||
# there's no git config, so clone
|
# there's no git config, so clone
|
||||||
clone(git_path, module, repo, dest, remote, depth, version, bare, reference, refspec, verify_commit)
|
clone(git_path, module, repo, dest, remote, depth, version, bare, reference, refspec, verify_commit)
|
||||||
repo_updated = True
|
repo_updated = True
|
||||||
|
@ -800,7 +821,7 @@ def main():
|
||||||
# this does no checking that the repo is the actual repo
|
# this does no checking that the repo is the actual repo
|
||||||
# requested.
|
# requested.
|
||||||
before = get_version(module, git_path, dest)
|
before = get_version(module, git_path, dest)
|
||||||
module.exit_json(changed=False, before=before, after=before)
|
module.exit_json(changed=False, before=before, after=before, **return_values)
|
||||||
else:
|
else:
|
||||||
# else do a pull
|
# else do a pull
|
||||||
local_mods = has_local_mods(module, git_path, dest, bare)
|
local_mods = has_local_mods(module, git_path, dest, bare)
|
||||||
|
@ -808,7 +829,7 @@ def main():
|
||||||
if local_mods:
|
if local_mods:
|
||||||
# failure should happen regardless of check mode
|
# failure should happen regardless of check mode
|
||||||
if not force:
|
if not force:
|
||||||
module.fail_json(msg="Local modifications exist in repository (force=no).")
|
module.fail_json(msg="Local modifications exist in repository (force=no).", **return_values)
|
||||||
# if force and in non-check mode, do a reset
|
# if force and in non-check mode, do a reset
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
reset(git_path, module, dest)
|
reset(git_path, module, dest)
|
||||||
|
@ -819,7 +840,7 @@ def main():
|
||||||
if before == remote_head:
|
if before == remote_head:
|
||||||
if local_mods:
|
if local_mods:
|
||||||
module.exit_json(changed=True, before=before, after=remote_head,
|
module.exit_json(changed=True, before=before, after=remote_head,
|
||||||
msg="Local modifications exist")
|
msg="Local modifications exist", **return_values)
|
||||||
elif version == 'HEAD':
|
elif version == 'HEAD':
|
||||||
# If the remote and local match and we're using the default of
|
# If the remote and local match and we're using the default of
|
||||||
# HEAD (It's not a real tag) then exit early
|
# HEAD (It's not a real tag) then exit early
|
||||||
|
@ -835,14 +856,14 @@ def main():
|
||||||
|
|
||||||
if repo_updated is None:
|
if repo_updated is None:
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, before=before, after=remote_head)
|
module.exit_json(changed=True, before=before, after=remote_head, **return_values)
|
||||||
fetch(git_path, module, repo, dest, version, remote, depth, bare, refspec)
|
fetch(git_path, module, repo, dest, version, remote, depth, bare, refspec)
|
||||||
repo_updated = True
|
repo_updated = True
|
||||||
|
|
||||||
# switch to version specified regardless of whether
|
# switch to version specified regardless of whether
|
||||||
# we got new revisions from the repository
|
# we got new revisions from the repository
|
||||||
if not bare:
|
if not bare:
|
||||||
switch_version(git_path, module, dest, remote, version, verify_commit)
|
switch_version(git_path, module, dest, remote, version, verify_commit, depth)
|
||||||
|
|
||||||
# Deal with submodules
|
# Deal with submodules
|
||||||
submodules_updated = False
|
submodules_updated = False
|
||||||
|
@ -851,9 +872,9 @@ def main():
|
||||||
|
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
if submodules_updated:
|
if submodules_updated:
|
||||||
module.exit_json(changed=True, before=before, after=remote_head, submodules_changed=True)
|
module.exit_json(changed=True, before=before, after=remote_head, submodules_changed=True, **return_values)
|
||||||
else:
|
else:
|
||||||
module.exit_json(changed=False, before=before, after=remote_head)
|
module.exit_json(changed=False, before=before, after=remote_head, **return_values)
|
||||||
|
|
||||||
if submodules_updated:
|
if submodules_updated:
|
||||||
# Switch to version specified
|
# Switch to version specified
|
||||||
|
@ -874,7 +895,7 @@ def main():
|
||||||
# No need to fail if the file already doesn't exist
|
# No need to fail if the file already doesn't exist
|
||||||
pass
|
pass
|
||||||
|
|
||||||
module.exit_json(changed=changed, before=before, after=after)
|
module.exit_json(changed=changed, before=before, after=after, **return_values)
|
||||||
|
|
||||||
# import module snippets
|
# import module snippets
|
||||||
from ansible.module_utils.basic import *
|
from ansible.module_utils.basic import *
|
||||||
|
|
Loading…
Reference in a new issue