git - add single_branch option (#28465)

In some usecases, we want to be able to clone a single branch
of a repository, without using --depth (which implies --single-branch).

* Use branch name when available
  -  update description of parameter
  - consolidate branch or tag checking for easy reuse

* Add changelog
* Use static task imports rather than dynamic includes
* Add integration tests for single_branch
* Account for older versions of git
* Minor tweak to warnings

Co-authored-by: Laurent Coustet <laurent.coustet@clarisys.fr>
Co-authored-by: Sam Doran <sdoran@redhat.com>
This commit is contained in:
Laurent Coustet 2020-06-29 21:40:54 +02:00 committed by GitHub
parent 5a28b2b86c
commit e396715d7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 146 additions and 28 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- git - add ``single_branch`` parameter (https://github.com/ansible/ansible/pull/28465)

View file

@ -31,7 +31,7 @@ options:
required: true
version:
description:
- What version of the repository to check out. This can be
- What version of the repository to check out. This can be
the literal string C(HEAD), a branch name, a tag name.
It can also be a I(SHA-1) hash, in which case C(refspec) needs
to be specified if the given revision is not already available.
@ -69,7 +69,7 @@ options:
If version is set to a I(SHA-1) not reachable from any branch
or tag, this option may be necessary to specify the ref containing
the I(SHA-1).
Uses the same syntax as the 'git fetch' command.
Uses the same syntax as the C(git fetch) command.
An example value could be "refs/meta/config".
version_added: "1.9"
force:
@ -127,6 +127,13 @@ options:
default: 'yes'
version_added: "1.6"
single_branch:
description:
- Clone only the history leading to the tip of the specified C(branch)
type: bool
default: 'no'
version_added: '2.11'
track_submodules:
description:
- if C(yes), submodules will track the latest commit on their
@ -232,6 +239,11 @@ EXAMPLES = '''
repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples
separate_git_dir: /src/ansible-examples.git
# Example clone of a single branch
- git:
single_branch: yes
branch: master
'''
RETURN = '''
@ -254,7 +266,7 @@ warnings:
description: List of warnings if requested features were not available due to a too old git version.
returned: error
type: str
sample: Your git version is too old to fully support the depth argument. Falling back to full checkouts.
sample: git version is too old to fully support the depth argument. Falling back to full checkouts.
git_dir_now:
description: Contains the new path of .git directory if it's changed
returned: success
@ -453,7 +465,7 @@ def get_submodule_versions(git_path, module, dest, version='HEAD'):
def clone(git_path, module, repo, dest, remote, depth, version, bare,
reference, refspec, verify_commit, separate_git_dir, result, gpg_whitelist):
reference, refspec, git_version_used, verify_commit, separate_git_dir, result, gpg_whitelist, single_branch):
''' makes a new git repo if it does not already exist '''
dest_dirname = os.path.dirname(dest)
try:
@ -466,11 +478,12 @@ def clone(git_path, module, repo, dest, remote, depth, version, bare,
cmd.append('--bare')
else:
cmd.extend(['--origin', remote])
is_branch_or_tag = is_remote_branch(git_path, module, dest, repo, version) or is_remote_tag(git_path, module, dest, repo, version)
if depth:
if version == 'HEAD' or refspec:
cmd.extend(['--depth', str(depth)])
elif is_remote_branch(git_path, module, dest, repo, version) \
or is_remote_tag(git_path, module, dest, repo, version):
elif is_branch_or_tag:
cmd.extend(['--depth', str(depth)])
cmd.extend(['--branch', version])
else:
@ -480,12 +493,23 @@ def clone(git_path, module, repo, dest, remote, depth, version, bare,
"HEAD, branches, tags or in combination with refspec.")
if reference:
cmd.extend(['--reference', str(reference)])
needs_separate_git_dir_fallback = False
if separate_git_dir:
git_version_used = git_version(git_path, module)
if single_branch:
if git_version_used is None:
module.fail_json(msg='Can not find git executable at %s' % git_path)
module.fail_json(msg='Cannot find git executable at %s' % git_path)
if git_version_used < LooseVersion('1.7.10'):
module.warn("git version '%s' is too old to use 'single-branch'. Ignoring." % git_version_used)
else:
cmd.append("--single-branch")
if is_branch_or_tag:
cmd.extend(['--branch', version])
needs_separate_git_dir_fallback = False
if separate_git_dir:
if git_version_used is None:
module.fail_json(msg='Cannot find git executable at %s' % git_path)
if git_version_used < LooseVersion('1.7.5'):
# git before 1.7.5 doesn't have separate-git-dir argument, do fallback
needs_separate_git_dir_fallback = True
@ -1061,6 +1085,7 @@ def main():
executable=dict(default=None, type='path'),
bare=dict(default='no', type='bool'),
recursive=dict(default='yes', type='bool'),
single_branch=dict(default=False, type='bool'),
track_submodules=dict(default='no', type='bool'),
umask=dict(default=None, type='raw'),
archive=dict(type='path'),
@ -1085,6 +1110,7 @@ def main():
verify_commit = module.params['verify_commit']
gpg_whitelist = module.params['gpg_whitelist']
reference = module.params['reference']
single_branch = module.params['single_branch']
git_path = module.params['executable'] or module.get_bin_path('git', True)
key_file = module.params['key_file']
ssh_opts = module.params['ssh_opts']
@ -1157,7 +1183,7 @@ def main():
git_version_used = git_version(git_path, module)
if depth is not None and git_version_used < LooseVersion('1.9.1'):
result['warnings'].append("Your git version is too old to fully support the depth argument. Falling back to full checkouts.")
module.warn("git version is too old to fully support the depth argument. Falling back to full checkouts.")
depth = None
recursive = module.params['recursive']
@ -1180,7 +1206,8 @@ def main():
result['diff'] = diff
module.exit_json(**result)
# there's no git config, so clone
clone(git_path, module, repo, dest, remote, depth, version, bare, reference, refspec, verify_commit, separate_git_dir, result, gpg_whitelist)
clone(git_path, module, repo, dest, remote, depth, version, bare, reference,
refspec, git_version_used, verify_commit, separate_git_dir, result, gpg_whitelist, single_branch)
elif not update:
# Just return having found a repo already in the dest path
# this does no checking that the repo is the actual repo

View file

@ -16,25 +16,26 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
- include_tasks: setup.yml
- include_tasks: setup-local-repos.yml
- import_tasks: setup.yml
- import_tasks: setup-local-repos.yml
- include_tasks: formats.yml
- include_tasks: missing_hostkey.yml
- include_tasks: no-destination.yml
- include_tasks: specific-revision.yml
- include_tasks: submodules.yml
- include_tasks: change-repo-url.yml
- include_tasks: depth.yml
- include_tasks: checkout-new-tag.yml
- import_tasks: formats.yml
- import_tasks: missing_hostkey.yml
- import_tasks: no-destination.yml
- import_tasks: specific-revision.yml
- import_tasks: submodules.yml
- import_tasks: change-repo-url.yml
- import_tasks: depth.yml
- import_tasks: single-branch.yml
- import_tasks: checkout-new-tag.yml
- include_tasks: gpg-verification.yml
when:
- not gpg_version.stderr
- gpg_version.stdout
- git_version.stdout is version("2.1.0", '>=')
- include_tasks: localmods.yml
- include_tasks: reset-origin.yml
- include_tasks: ambiguous-ref.yml
- include_tasks: archive.yml
- include_tasks: separate-git-dir.yml
- include_tasks: forcefully-fetch-tag.yml
- import_tasks: localmods.yml
- import_tasks: reset-origin.yml
- import_tasks: ambiguous-ref.yml
- import_tasks: archive.yml
- import_tasks: separate-git-dir.yml
- import_tasks: forcefully-fetch-tag.yml

View file

@ -0,0 +1,87 @@
# Test single_branch parameter
- name: SINGLE_BRANCH | clear checkout_dir
file:
state: absent
path: "{{ checkout_dir }}"
- name: SINGLE_BRANCH | Clone example git repo using single_branch
git:
repo: 'file://{{ repo_dir|expanduser }}/shallow_branches'
dest: '{{ checkout_dir }}'
single_branch: yes
register: single_branch_1
- name: SINGLE_BRANCH | Clone example git repo using single_branch again
git:
repo: 'file://{{ repo_dir|expanduser }}/shallow_branches'
dest: '{{ checkout_dir }}'
single_branch: yes
register: single_branch_2
- name: SINGLE_BRANCH | List revisions
command: git rev-list --all --count
args:
chdir: '{{ checkout_dir }}'
register: rev_list1
when: git_version.stdout is version(git_version_supporting_single_branch, '>=')
- name: SINGLE_BRANCH | Ensure single_branch did the right thing with git >= {{ git_version_supporting_single_branch }}
assert:
that:
- single_branch_1 is changed
- single_branch_2 is not changed
when: git_version.stdout is version(git_version_supporting_single_branch, '>=')
- name: SINGLE_BRANCH | Ensure single_branch did the right thing with git < {{ git_version_supporting_single_branch }}
assert:
that:
- single_branch_1 is changed
- single_branch_1.warnings | length == 1
- single_branch_2 is not changed
when: git_version.stdout is version(git_version_supporting_single_branch, '<')
- name: SINGLE_BRANCH | clear checkout_dir
file:
state: absent
path: "{{ checkout_dir }}"
- name: SINGLE_BRANCH | Clone example git repo using single_branch with version
git:
repo: 'file://{{ repo_dir|expanduser }}/shallow_branches'
dest: '{{ checkout_dir }}'
single_branch: yes
version: master
register: single_branch_3
- name: SINGLE_BRANCH | Clone example git repo using single_branch with version again
git:
repo: 'file://{{ repo_dir|expanduser }}/shallow_branches'
dest: '{{ checkout_dir }}'
single_branch: yes
version: master
register: single_branch_4
- name: SINGLE_BRANCH | List revisions
command: git rev-list --all --count
args:
chdir: '{{ checkout_dir }}'
register: rev_list2
when: git_version.stdout is version(git_version_supporting_single_branch, '>=')
- name: SINGLE_BRANCH | Ensure single_branch did the right thing with git >= {{ git_version_supporting_single_branch }}
assert:
that:
- single_branch_3 is changed
- single_branch_4 is not changed
- rev_list2.stdout == '1'
when: git_version.stdout is version(git_version_supporting_single_branch, '>=')
- name: SINGLE_BRANCH | Ensure single_branch did the right thing with git < {{ git_version_supporting_single_branch }}
assert:
that:
- single_branch_3 is changed
- single_branch_3.warnings | length == 1
- single_branch_4 is not changed
when: git_version.stdout is version(git_version_supporting_single_branch, '<')

View file

@ -43,6 +43,7 @@ known_host_files:
- '/etc/ssh/ssh_known_hosts'
git_version_supporting_depth: 1.9.1
git_version_supporting_ls_remote: 1.7.5
git_version_supporting_single_branch: 1.7.10
# path to a SSH private key for use with github.com (tests skipped if undefined)
# github_ssh_private_key: "{{ lookup('env', 'HOME') }}/.ssh/id_rsa"
git_gpg_testkey: |