diff --git a/test/test_alt.py b/test/test_alt.py index ed66bf4..79bb382 100644 --- a/test/test_alt.py +++ b/test/test_alt.py @@ -54,6 +54,50 @@ def test_alt_source(runner, paths, tracked, encrypt, exclude, yadm_alt): assert str(source_file) not in linked +@pytest.mark.usefixtures("ds1_copy") +def test_alt_submodule(runner, paths, yadm_cmd): + """Test alt handling in submodule""" + + yadm_dir, yadm_data = setup_standard_yadm_dir(paths) + + # Create alt files in separate alt dir + alt_dir = yadm_dir.join("alt") + utils.create_alt_files(paths, "##default", tracked=False, yadm_alt=True, yadm_dir=yadm_dir) + + # Make alt dir be a separate repo + runner(["git", "init", alt_dir], report=False) + runner(["git", "-C", alt_dir, "add", "."], report=False) + run = runner(["git", "-C", alt_dir, "commit", "-m", "Add alt files"]) + assert run.success + + # And add it as a submodule + run = runner( + yadm_cmd("-Y", yadm_dir, "--yadm-data", yadm_data, "submodule", "add", "https://foobar/repo", alt_dir.basename), + cwd=alt_dir.dirname, + ) + assert run.success + + # Now when processing alt files + run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"]) + assert run.success + assert run.err == "" + linked = utils.parse_alt_output(run.out) + + # Then files in submodule are also handled + for link_path in TEST_PATHS: + source_file_content = link_path + "##default" + source_file = alt_dir.join(source_file_content) + link_file = paths.work.join(link_path) + if link_path == utils.ALT_DIR: + source_file = source_file.join(utils.CONTAINED) + link_file = link_file.join(utils.CONTAINED) + assert link_file.islink() + target = py.path.local(os.path.realpath(link_file)) + assert target.isfile() + assert link_file.read() == source_file_content + assert str(source_file) in linked + + @pytest.mark.usefixtures("ds1_copy") @pytest.mark.parametrize("yadm_alt", [True, False], ids=["alt", "worktree"]) def test_relative_link(runner, paths, yadm_alt): diff --git a/test/test_unit_exclude_encrypted.py b/test/test_unit_exclude_encrypted.py index 1daa891..3bedd05 100644 --- a/test/test_unit_exclude_encrypted.py +++ b/test/test_unit_exclude_encrypted.py @@ -34,6 +34,7 @@ def test_exclude_encrypted(runner, tmpdir, yadm, encrypt_exists, auto_exclude, e script = f""" YADM_TEST=1 source {yadm} {config_function} + GIT_PROGRAM=true DEBUG=1 YADM_ENCRYPT="{encrypt_file}" YADM_REPO="{repo_dir}" diff --git a/yadm b/yadm index 9757345..f37b16b 100755 --- a/yadm +++ b/yadm @@ -569,7 +569,7 @@ function alt() { # determine all tracked files local tracked_files=() local IFS=$'\n' - for tracked_file in $("$GIT_PROGRAM" ls-files -- '*##*'); do + for tracked_file in $("$GIT_PROGRAM" ls-files --recurse-submodules -- '*##*'); do tracked_files+=("$tracked_file") done @@ -1490,64 +1490,87 @@ function update_exclude() { auto_exclude=$(config --bool yadm.auto-exclude) [ "$auto_exclude" == "false" ] && return 0 - local exclude_path="${YADM_REPO}/info/exclude" local newline=$'\n' - local part_path="$exclude_path.yadm-$1" - local part_str - part_str=$(join_string "$newline" "${@:2}") + while IFS='' read -r submodule; do + local exclude_path="${YADM_REPO}${submodule:+/modules/}${submodule}/info/exclude" - if [ -e "$part_path" ]; then - if [ "$part_str" = "$(<"$part_path")" ]; then - return - fi - - rm -f "$part_path" - elif [ -z "$part_str" ]; then - return - fi - - if [ -n "$part_str" ]; then - assert_parent "$part_path" - cat >"$part_path" <<<"$part_str" - fi - - local exclude_flag="# yadm-auto-excludes" - - local exclude_header="${exclude_flag}${newline}" - exclude_header="${exclude_header}# This section is managed by yadm." - exclude_header="${exclude_header}${newline}" - exclude_header="${exclude_header}# Any edits below will be lost." - exclude_header="${exclude_header}${newline}" - - # read info/exclude - local unmanaged="" - local managed="" - if [ -e "$exclude_path" ]; then - local -i flag_seen=0 - local line - while IFS='' read -r line || [ -n "$line" ]; do - [ "$line" = "$exclude_flag" ] && flag_seen=1 - if ((flag_seen)); then - managed="${managed}${line}${newline}" - else - unmanaged="${unmanaged}${line}${newline}" + local part_path="$exclude_path.yadm-$1" + local part_str="" + if [ -z "$submodule" ]; then + part_str=$(join_string "$newline" "${@:2}") + else + for part in "${@:2}"; do + if [ "${part#/"${submodule}"/}" != "$part" ]; then + part_str="${part_str}${part#/"${submodule}"}${newline}" + fi + done + if [ -n "$part_str" ]; then + part_str="${part_str:0:-1}" fi - done <"$exclude_path" - fi - - local exclude_str="" - for suffix in alt encrypt; do - if [ -e "${exclude_path}.yadm-$suffix" ]; then - local header="# yadm $suffix$newline" - exclude_str="$exclude_str$header$(<"$exclude_path".yadm-"$suffix")" fi - done - if [ "${exclude_header}${exclude_str}${newline}" != "$managed" ]; then - debug "Updating ${exclude_path}" - cat >"$exclude_path" <<<"${unmanaged}${exclude_header}${exclude_str}" - fi + if [ -e "$part_path" ]; then + if [ "$part_str" = "$(<"$part_path")" ]; then + continue + fi + + rm -f "$part_path" + elif [ -z "$part_str" ]; then + continue + fi + + if [ -n "$part_str" ]; then + if [ -n "$submodule" ] && [ ! -d "${YADM_REPO}/modules/$submodule" ]; then + echo "Warning: not updating exclude for submodule '$submodule';" \ + "consider running 'yadm submodule absorbgitdirs'" + continue + fi + assert_parent "$part_path" + cat >"$part_path" <<<"$part_str" + fi + + local exclude_flag="# yadm-auto-excludes" + + local exclude_header="${exclude_flag}${newline}" + exclude_header="${exclude_header}# This section is managed by yadm." + exclude_header="${exclude_header}${newline}" + exclude_header="${exclude_header}# Any edits below will be lost." + exclude_header="${exclude_header}${newline}" + + # read info/exclude + local unmanaged="" + local managed="" + if [ -e "$exclude_path" ]; then + local -i flag_seen=0 + local line + while IFS='' read -r line || [ -n "$line" ]; do + [ "$line" = "$exclude_flag" ] && flag_seen=1 + if ((flag_seen)); then + managed="${managed}${line}${newline}" + else + unmanaged="${unmanaged}${line}${newline}" + fi + done <"$exclude_path" + fi + + local exclude_str="" + for suffix in alt encrypt; do + if [ -e "${exclude_path}.yadm-$suffix" ]; then + local header="# yadm $suffix$newline" + exclude_str="$exclude_str$header$(<"$exclude_path".yadm-"$suffix")" + fi + done + + if [ "${exclude_header}${exclude_str}${newline}" != "$managed" ]; then + debug "Updating ${exclude_path}" + cat >"$exclude_path" <<<"${unmanaged}${exclude_header}${exclude_str}" + fi + done <<<"$( + echo "" + "$GIT_PROGRAM" -C "$YADM_WORK" submodule --quiet \ + foreach --recursive "echo \$displaypath" + )" return 0 }