Unless a branch is specified, the default remote HEAD is used during clone. Also a local master branch is not created if it is not the remote HEAD.
This commit is contained in:
parent
4cb13d5d08
commit
4b5f16d73a
4 changed files with 90 additions and 21 deletions
|
@ -70,6 +70,9 @@ def test_clone(
|
||||||
# clone should succeed, and repo should be configured properly
|
# clone should succeed, and repo should be configured properly
|
||||||
assert successful_clone(run, paths, repo_config)
|
assert successful_clone(run, paths, repo_config)
|
||||||
|
|
||||||
|
# these clones should have master as HEAD
|
||||||
|
verify_head(paths, 'master')
|
||||||
|
|
||||||
# ensure conflicts are handled properly
|
# ensure conflicts are handled properly
|
||||||
if conflicts:
|
if conflicts:
|
||||||
assert 'NOTE' in run.out
|
assert 'NOTE' in run.out
|
||||||
|
@ -162,6 +165,7 @@ def test_clone_bootstrap(
|
||||||
assert BOOTSTRAP_MSG not in run.out
|
assert BOOTSTRAP_MSG not in run.out
|
||||||
|
|
||||||
assert successful_clone(run, paths, repo_config, expected_code)
|
assert successful_clone(run, paths, repo_config, expected_code)
|
||||||
|
verify_head(paths, 'master')
|
||||||
|
|
||||||
if not bs_exists:
|
if not bs_exists:
|
||||||
assert BOOTSTRAP_MSG not in run.out
|
assert BOOTSTRAP_MSG not in run.out
|
||||||
|
@ -230,6 +234,7 @@ def test_clone_perms(
|
||||||
)
|
)
|
||||||
|
|
||||||
assert successful_clone(run, paths, repo_config)
|
assert successful_clone(run, paths, repo_config)
|
||||||
|
verify_head(paths, 'master')
|
||||||
if in_work:
|
if in_work:
|
||||||
# private directories which already exist, should be left as they are,
|
# private directories which already exist, should be left as they are,
|
||||||
# which in this test is "insecure".
|
# which in this test is "insecure".
|
||||||
|
@ -260,7 +265,8 @@ def test_clone_perms(
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('remote')
|
@pytest.mark.usefixtures('remote')
|
||||||
@pytest.mark.parametrize('branch', ['master', 'valid', 'invalid'])
|
@pytest.mark.parametrize(
|
||||||
|
'branch', ['master', 'default', 'valid', 'invalid'])
|
||||||
def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
||||||
"""Test cloning a branch other than master"""
|
"""Test cloning a branch other than master"""
|
||||||
|
|
||||||
|
@ -269,6 +275,12 @@ def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
||||||
os.system(
|
os.system(
|
||||||
f'GIT_DIR="{paths.remote}" git commit '
|
f'GIT_DIR="{paths.remote}" git commit '
|
||||||
f'--allow-empty -m "This branch is valid"')
|
f'--allow-empty -m "This branch is valid"')
|
||||||
|
if branch != 'default':
|
||||||
|
# When branch == 'default', the "default" branch of the remote repo
|
||||||
|
# will remain "valid" to validate identification the correct default
|
||||||
|
# branch by inspecting the repo. Otherwise it will be set back to
|
||||||
|
# "master"
|
||||||
|
os.system(f'GIT_DIR="{paths.remote}" git checkout master')
|
||||||
|
|
||||||
# clear out the work path
|
# clear out the work path
|
||||||
paths.work.remove()
|
paths.work.remove()
|
||||||
|
@ -278,7 +290,7 @@ def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
||||||
|
|
||||||
# run the clone command
|
# run the clone command
|
||||||
args = ['clone', '-w', paths.work]
|
args = ['clone', '-w', paths.work]
|
||||||
if branch != 'master':
|
if branch not in ['master', 'default']:
|
||||||
args += ['-b', branch]
|
args += ['-b', branch]
|
||||||
args += [remote_url]
|
args += [remote_url]
|
||||||
run = runner(command=yadm_cmd(*args))
|
run = runner(command=yadm_cmd(*args))
|
||||||
|
@ -298,10 +310,12 @@ def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
||||||
assert run.err == ''
|
assert run.err == ''
|
||||||
assert f'origin\t{remote_url}' in run.out
|
assert f'origin\t{remote_url}' in run.out
|
||||||
run = runner(command=yadm_cmd('show'))
|
run = runner(command=yadm_cmd('show'))
|
||||||
if branch == 'valid':
|
if branch == 'master':
|
||||||
assert 'This branch is valid' in run.out
|
|
||||||
else:
|
|
||||||
assert 'Initial commit' in run.out
|
assert 'Initial commit' in run.out
|
||||||
|
verify_head(paths, 'master')
|
||||||
|
else:
|
||||||
|
assert 'This branch is valid' in run.out
|
||||||
|
verify_head(paths, 'valid')
|
||||||
|
|
||||||
|
|
||||||
def successful_clone(run, paths, repo_config, expected_code=0):
|
def successful_clone(run, paths, repo_config, expected_code=0):
|
||||||
|
@ -324,3 +338,16 @@ def remote(paths, ds1_repo_copy):
|
||||||
# cannot be applied to another fixture.
|
# cannot be applied to another fixture.
|
||||||
paths.remote.remove()
|
paths.remote.remove()
|
||||||
paths.repo.move(paths.remote)
|
paths.repo.move(paths.remote)
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_repo(runner, yadm_cmd, ):
|
||||||
|
"""Test cloning without specifying a repo"""
|
||||||
|
run = runner(command=yadm_cmd('clone'))
|
||||||
|
assert run.failure
|
||||||
|
assert run.err == ''
|
||||||
|
assert 'ERROR: No repository provided' in run.out
|
||||||
|
|
||||||
|
|
||||||
|
def verify_head(paths, branch):
|
||||||
|
"""Assert the local repo has the correct head branch"""
|
||||||
|
assert paths.repo.join('HEAD').read() == f'ref: refs/heads/{branch}\n'
|
||||||
|
|
27
test/test_default_remote_branch.py
Normal file
27
test/test_default_remote_branch.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
"""Unit tests: _default_remote_branch()"""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('condition', ['found', 'missing'])
|
||||||
|
def test(runner, paths, condition):
|
||||||
|
"""Test _default_remote_branch()"""
|
||||||
|
test_branch = 'test/branch'
|
||||||
|
output = f'ref: refs/heads/{test_branch}\\tHEAD\\n'
|
||||||
|
if condition == 'missing':
|
||||||
|
output = 'output that is missing ref'
|
||||||
|
script = f"""
|
||||||
|
YADM_TEST=1 source {paths.pgm}
|
||||||
|
function git() {{
|
||||||
|
printf '{output}';
|
||||||
|
printf 'mock stderr\\n' 1>&2
|
||||||
|
}}
|
||||||
|
_default_remote_branch URL
|
||||||
|
"""
|
||||||
|
print(condition)
|
||||||
|
run = runner(command=['bash'], inp=script)
|
||||||
|
assert run.success
|
||||||
|
assert run.err == ''
|
||||||
|
if condition == 'found':
|
||||||
|
assert run.out.strip() == test_branch
|
||||||
|
else:
|
||||||
|
assert run.out.strip() == 'master'
|
38
yadm
38
yadm
|
@ -743,13 +743,23 @@ function clean() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _default_remote_branch() {
|
||||||
|
local ls_remote
|
||||||
|
ls_remote=$("$GIT_PROGRAM" ls-remote -q --symref "$1" 2>/dev/null)
|
||||||
|
match="^ref:[[:blank:]]+refs/heads/([^[:blank:]]+)"
|
||||||
|
if [[ "$ls_remote" =~ $match ]] ; then
|
||||||
|
echo "${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
echo master
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
function clone() {
|
function clone() {
|
||||||
|
|
||||||
DO_BOOTSTRAP=1
|
DO_BOOTSTRAP=1
|
||||||
local branch
|
local branch=
|
||||||
branch="master"
|
|
||||||
|
|
||||||
clone_args=()
|
local repo_url=
|
||||||
while [[ $# -gt 0 ]] ; do
|
while [[ $# -gt 0 ]] ; do
|
||||||
key="$1"
|
key="$1"
|
||||||
case $key in
|
case $key in
|
||||||
|
@ -766,22 +776,29 @@ function clone() {
|
||||||
--no-bootstrap) # prevent bootstrap, without prompt
|
--no-bootstrap) # prevent bootstrap, without prompt
|
||||||
DO_BOOTSTRAP=3
|
DO_BOOTSTRAP=3
|
||||||
;;
|
;;
|
||||||
*) # main arguments are kept intact
|
*) # use first found argument as the URL
|
||||||
clone_args+=("$1")
|
[ -z "$repo_url" ] && repo_url="$1"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
|
[ -z "$repo_url" ] && error_out "No repository provided"
|
||||||
|
|
||||||
|
[ -z "$branch" ] && branch=$(_default_remote_branch "$repo_url")
|
||||||
|
|
||||||
[ -n "$DEBUG" ] && display_private_perms "initial"
|
[ -n "$DEBUG" ] && display_private_perms "initial"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2119
|
||||||
# clone will begin with a bare repo
|
# clone will begin with a bare repo
|
||||||
local empty=
|
init
|
||||||
init $empty
|
|
||||||
|
# configure local HEAD with the correct branch
|
||||||
|
printf 'ref: refs/heads/%s\n' "$branch" > "${YADM_REPO}/HEAD"
|
||||||
|
|
||||||
# add the specified remote, and configure the repo to track origin/$branch
|
# add the specified remote, and configure the repo to track origin/$branch
|
||||||
debug "Adding remote to new repo"
|
debug "Adding remote to new repo"
|
||||||
"$GIT_PROGRAM" remote add origin "${clone_args[@]}"
|
"$GIT_PROGRAM" remote add origin "$repo_url"
|
||||||
debug "Configuring new repo to track origin/${branch}"
|
debug "Configuring new repo to track origin/${branch}"
|
||||||
"$GIT_PROGRAM" config "branch.${branch}.remote" origin
|
"$GIT_PROGRAM" config "branch.${branch}.remote" origin
|
||||||
"$GIT_PROGRAM" config "branch.${branch}.merge" "refs/heads/${branch}"
|
"$GIT_PROGRAM" config "branch.${branch}.merge" "refs/heads/${branch}"
|
||||||
|
@ -791,13 +808,13 @@ function clone() {
|
||||||
"$GIT_PROGRAM" fetch origin || {
|
"$GIT_PROGRAM" fetch origin || {
|
||||||
debug "Removing repo after failed clone"
|
debug "Removing repo after failed clone"
|
||||||
rm -rf "$YADM_REPO"
|
rm -rf "$YADM_REPO"
|
||||||
error_out "Unable to fetch origin ${clone_args[0]}"
|
error_out "Unable to fetch origin $repo_url"
|
||||||
}
|
}
|
||||||
debug "Verifying '${branch}' is a valid branch to merge"
|
debug "Verifying '${branch}' is a valid branch to merge"
|
||||||
[ -f "${YADM_REPO}/refs/remotes/origin/${branch}" ] || {
|
[ -f "${YADM_REPO}/refs/remotes/origin/${branch}" ] || {
|
||||||
debug "Removing repo after failed clone"
|
debug "Removing repo after failed clone"
|
||||||
rm -rf "$YADM_REPO"
|
rm -rf "$YADM_REPO"
|
||||||
error_out "Clone failed, 'origin/${branch}' does not exist in ${clone_args[0]}"
|
error_out "Clone failed, 'origin/${branch}' does not exist in $repo_url"
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ "$YADM_WORK" = "$HOME" ]; then
|
if [ "$YADM_WORK" = "$HOME" ]; then
|
||||||
|
@ -1164,6 +1181,7 @@ EOF
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC2120
|
||||||
function init() {
|
function init() {
|
||||||
|
|
||||||
# safety check, don't attempt to init when the repo is already present
|
# safety check, don't attempt to init when the repo is already present
|
||||||
|
|
9
yadm.1
9
yadm.1
|
@ -116,12 +116,10 @@ if it exists.
|
||||||
.BI clone " url
|
.BI clone " url
|
||||||
Clone a remote repository for tracking dotfiles.
|
Clone a remote repository for tracking dotfiles.
|
||||||
After the contents of the remote repository have been fetched, a "merge" of
|
After the contents of the remote repository have been fetched, a "merge" of
|
||||||
.I origin/master
|
the remote HEAD branch is attempted.
|
||||||
is attempted.
|
|
||||||
If there are conflicting files already present in the
|
If there are conflicting files already present in the
|
||||||
.IR work-tree ,
|
.IR work-tree ,
|
||||||
this merge will fail and instead a "reset" of
|
this merge will fail and instead a "reset" of the remote HEAD branch
|
||||||
.I origin/master
|
|
||||||
will be done, followed by a "stash". This "stash" operation will preserve the
|
will be done, followed by a "stash". This "stash" operation will preserve the
|
||||||
original data.
|
original data.
|
||||||
|
|
||||||
|
@ -154,8 +152,7 @@ but this can be overridden with the
|
||||||
.BR -w " option.
|
.BR -w " option.
|
||||||
yadm can be forced to overwrite an existing repository by providing the
|
yadm can be forced to overwrite an existing repository by providing the
|
||||||
.BR -f " option.
|
.BR -f " option.
|
||||||
If you want to use a branch other than
|
If you want to use a branch other than the remote HEAD branch
|
||||||
.IR origin/master ,
|
|
||||||
you can specify it using the
|
you can specify it using the
|
||||||
.BR -b " option.
|
.BR -b " option.
|
||||||
By default yadm will ask the user if the bootstrap program should be run (if it
|
By default yadm will ask the user if the bootstrap program should be run (if it
|
||||||
|
|
Loading…
Reference in a new issue