Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sparse Checkout support for a submodule #503

Open
andyneff opened this issue Apr 18, 2024 · 0 comments
Open

Sparse Checkout support for a submodule #503

andyneff opened this issue Apr 18, 2024 · 0 comments
Labels

Comments

@andyneff
Copy link
Member

Plugin idea for supporting sparse checkout

Here are some Just targets to consider:

In just sync

    if [ "$(git config submodule.external/data.update)" = "none" ]; then
      echo 'Initializing data'
      justify data init
    fi

Init

Note: Docs say sparse-checkout init is deprecated, so probably should be matching that behavior instead.

    data_init) # git sparse-checkout init for external/data
      local git_version=${git_version-$(git_version)}
      local sm_name=external/data
      local sm_path="$(git config -f .gitmodules "submodule.${sm_name}.path")"

      ### 1. First clone --no-checkout ###
      if version_lt "${git_version}" 1.8.4; then
        # Port of git's submodule update's clone.
        local git_dir="$("${GIT}" rev-parse --git-dir)/modules/${sm_path}"

        "${GIT}" submodule init "${sm_path}"
        mkdir -p "$(dirname "${git_dir}")"
        "${GIT}" clone --no-checkout \
          --separate-git-dir "${git_dir}" \
          "$("${GIT}" config "submodule.${sm_name}.url")" \
          "${sm_path}"
      else
        # A close equivalent of clone no checkout
        "${GIT}" -c "submodule.${sm_name}.update="'!true' submodule update --init external/data
      fi

      ### 2. git sparse-checkout init ###
      pushd "${sm_path}" &> /dev/null
        if version_lt "${git_version}" 2.25.0; then
          "${GIT}" config core.sparseCheckout true
        else # 2.25.0
          "${GIT}" sparse-checkout init --cone
        fi
        justify data set prod .gitlab
      popd &> /dev/null

      ### 3. Now, return normal control over to git submodule
      "${GIT}" config "submodule.${sm_name}.update" checkout
      "${GIT}" submodule update "${sm_path}"
      pushd "${sm_path}" &> /dev/null
        # I'm ok with always doing read-tree here, as I am init-ing and this
        # directory should would have not existed before "data init" started

        # This is needed for corner cases even in git 2.25 like when someone deinit's a sparse submodule
        # which results in a no-checkout scenario where every file is stages as deleted, and we need to
        # read-tree, and not prompt the user, so don't call "just data read-tree"
        "${GIT}" read-tree -mu HEAD
      popd &> /dev/null
      ;;

Add

    data_add) # git sparse-checkout add for external/data
      local git_version=${git_version-$(git_version)}
      pushd "${DATA_DIR}" &> /dev/null
        if version_lt "${git_version}" 2.26.0; then
          echo "Approximation of 'git sparse-checkout add', update to git 2.26.0 or newer for more reliable results" >&2
          local add_dir
          local sparse_file="$("${GIT}" rev-parse --git-dir)/info/sparse-checkout"
          for add_dir in ${@+"${@}"}; do
            # The entry should look like /foo/bar/, starting and ending with a /
            if [ "${add_dir:0:1}" != "/" ]; then
              add_dir="/${add_dir}"
            fi
            if [ "${add_dir:${#add_dir}-1:1}" != "/" ]; then
              add_dir="${add_dir}/"
            fi
            # Just blindly add it, no cone check. TODO: Cone?
            echo "${add_dir}" >> "${sparse_file}"
          done
          justify data read-tree
        else
          "${GIT}" sparse-checkout add ${@+"${@}"}
        fi
      popd &> /dev/null
      extra_args=${#}
      ;;

List

    data_list) # git sparse-checkout list for external/data
      local git_version=${git_version-$(git_version)}
      pushd "${DATA_DIR}" &> /dev/null
        if version_lt "${git_version}" 2.25.0; then
          echo "Approximation of 'git sparse-checkout list', update to git 2.25.0 or newer for more reliable results" >&2
          sed -En 's|^/(.*)/|\1|p'  ../../.git/modules/external/data/info/sparse-checkout
        else
          "${GIT}" sparse-checkout list
        fi
      popd &> /dev/null
      ;;

Disable

    data_disable) # git sparse-checkout disable for external/data
      local git_version=${git_version-$(git_version)}
      pushd "$DATA_DIR}" &> /dev/null
        if version_lt "${git_version}" 2.25.0; then
          echo "Approximation of 'git sparse-checkout disable', update to git 2.25.0 or newer for more reliable results" >&2
          "${GIT}" config core.sparseCheckout false
          "${GIT}" read-tree -mu HEAD
        else
          "${GIT}" sparse-checkout disable
        fi
      popd &> /dev/null
      ;;

Set

    data_set) # git sparse-checkout set for external/data
      local git_version=${git_version-$(git_version)}
      pushd "${DATA_DIR}" &> /dev/null
        if version_lt "${git_version}" 2.25.0; then
          echo '/*
!/*/' > "$("${GIT}" rev-parse --git-dir)/info/sparse-checkout"
          justify data add ${@+"${@}"}
        else
          "${GIT}" sparse-checkout set  ${@+"${@}"}
        fi
      popd &> /dev/null
      extra_args=${#}
      ;;

Other

    data_read-tree) # git read-tree -mu HEAD for external/data
      # Several references refer to read-tree being the way to update the workspace
      # https://vmiklos.hu/blog/sparse-checkout-example-in-git-1-7
      # https://web.archive.org/web/20130121010056/https://blog.quilitz.de/2010/03/checkout-sub-directories-in-git-sparse-checkouts/comment-page-1/
      # https://stackoverflow.com/a/2340860/4166604
      # https://stackoverflow.com/a/50818880/4166604

      # This is more destructive of a command then I thought it was.
      # This will remove any STAGED changes, which is not what I think is the point? I just
      # wanted something like "git checkout" that will update the workspace after the sparse-checkout
      # is updated. I originally thought this was less intrusive.

      # This behavior is also what we want on initial clone. A clone --no-checkout looks like all files
      # are staged as deleted. So reverting that is exactly what we need on data init
      pushd "${DATA_DIR}" &> /dev/null
        if [ -z "$(${GIT} status --untracked-files=no --porcelain 2>/dev/null)" ]; then
          "${GIT}" read-tree -mu HEAD
        else
          echo "external/data is in a dirty state. This command will revert the ${RED}staged changes in external/data!${NC}."
          git status --untracked-files=no
          local ans
          ask_question "Are you sure you want to execute 'git read-tree -mu HEAD' on external/data?" ans y
          if [ "${ans}" = "1" ]; then
            "${GIT}" read-tree -mu HEAD
          else
            echo "To complete sparse checkout, run: ${YELLOW}git read-tree -mu HEAD${NC}"
          fi
        fi
      popd &> /dev/null
      ;;

    data_ls-files) # git ls-files for external/data
      pushd "${DATA_DIR}" &> /dev/null
        "${GIT}" ls-files
      popd &> /dev/null
      ;;

    data_ls-dirs) # Print directory tree for external/data
      pushd "${DATA_DIR}" &> /dev/null
        "${GIT}" ls-files -z | xargs -0 dirname | sort -u
      popd &> /dev/null
      ;;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant