Skip to content

Bring back support for Git :ref and :depth used together #13713

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

Merged
merged 1 commit into from
Jul 10, 2024

Conversation

rhcarvalho
Copy link
Contributor

@rhcarvalho rhcarvalho commented Jul 9, 2024

An optimization to fetch only the about-to-be-used refs introduced when the :depth option was added broke support for specifying a SHA1 prefix as :ref. Such possibility was neither documented nor tested, but certainly used in the wild. This use case is now explicitly supported with a new test case.

We maintain this fetch optimization whenever :branch, :tag or :depth is specified. We fetch all refs when :ref is used without :depth to be able to resolve short SHA1 refs, and require full commit hashes to use :ref and :depth together.

When using :ref with :depth, a full SHA1 is required by Git and surfaced in our docs. We enforce it when validating the options, leaving room for longer hashes.

This commit brings back two tests to cover the :ref and :depth functionality, and adds a new one to cover the validation mentioned above.


Related forum thread: https://elixirforum.com/t/elixir-v1-17-2-released/64794

@rhcarvalho
Copy link
Contributor Author

An optimization to fetch only the about-to-be-used refs introduced when the :depth option was added

This was discussed in https://github.com/elixir-lang/elixir/pull/13128/files#r1399042619.

An optimization to fetch only the about-to-be-used refs introduced when
the :depth option was added broke support for specifying a SHA1 prefix
as :ref. Such possibility was neither documented nor tested, but
certainly used in the wild. This use case is now explicitly supported
with a new test case.

We maintain this fetch optimization whenever :branch, :tag or :depth is
specified. We fetch all refs when :ref is used without :depth to be able
to resolve short SHA1 refs, and require full commit hashes to use :ref
and :depth together.

When using :ref with :depth, a full SHA1 is required by Git and surfaced
in our docs. We enforce it when validating the options, leaving room for
longer hashes.

This commit brings back two tests to cover the :ref and :depth
functionality, and adds a new one to cover the validation mentioned
above.
@rhcarvalho rhcarvalho force-pushed the git-fetch-short-sha1 branch from 4f51f5a to 7ba18df Compare July 9, 2024 12:17
@rhcarvalho rhcarvalho changed the title Fetch all refs unless using Git :depth option Bring back support for Git :ref and :depth used together Jul 9, 2024
@rhcarvalho
Copy link
Contributor Author

Ready for a second pass.


["--git-dir=.git", "fetch", "--force", "--quiet"]
|> Kernel.++(progress_switch(git_version()))
|> Kernel.++(tags_switch(opts[:tag]))
|> Kernel.++(depth_switch(opts[:depth]))
|> Kernel.++(if branch_or_tag, do: ["origin", branch_or_tag], else: [])
|> Kernel.++(refspec_switch(opts, rev))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would merge refspec_switch and depth_switch logic into a single function. There is too much dependency between these two and unifying it in a single place would probably be cleaner.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than moving the call to depth_switch into refspec_switch, I'm not seeing a clean path forward here:

  defp refspec_switch(_opts, nil), do: []

  defp refspec_switch(opts, rev) do
    depth_switch(opts[:depth]) ++
      case Keyword.take(opts, [:depth, :branch, :tag]) do
        [_ | _] -> ["origin", rev]
        _ -> []
      end
  end

  defp depth_switch(nil), do: []

  defp depth_switch(n) when is_integer(n) and n > 0 do
    check_depth_support(git_version())
    ["--depth=#{n}"]
  end

We only want to call check_depth_support with a valid :depth, and the other logic is about when to include the repository + refspec (e.g. origin main). Do you see a better way to organize this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking this:

  defp fetch_args(opts, nil), do: []
  
  defp fetch_args(opts, rev) do
    cond do
      depth = opts[:depth] ->
        check_depth_support(git_version())
        ["--depth=#{n}", "origin", rev]

      # We can only fetch full refs, which :ref may not be one of
      opts[:ref] == rev ->
        []

      true ->
        ["origin", rev]
    end
  end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this misses the case of depth with no rev (tip of default branch, when neither of :ref, :branch, or :tag are specified)?

(I think a test case would go red)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn’t that rev equals to nil, handled in the first clause?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, my code above also falls into the same mistake. The current commit handles that case with the two independent functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So which arguments would reproduce the failure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran the tests locally for code in #13713 (comment) and #13713 (comment) (with a small fix, s/{n}/{depth}/).

Both fail 5 tests (5 out of 10 in the describe "Git depth option"): all the tests where the mix spec doesn't include one of :ref, :branch, :tag (in the test file they are the first and the last four tests in the describe block).

The failure is the same, the produced repos are not shallow:

 Expected truthy, got false
 code: assert File.exists?(repo_path <> "/.git/shallow")

That confirms what I said in #13713 (comment) last night (I was on mobile and "ran the tests" on my head).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I got it. We need to run depth even if there is no rev. :)

@josevalim josevalim merged commit 58e76cc into elixir-lang:main Jul 10, 2024
@josevalim
Copy link
Member

💚 💙 💜 💛 ❤️

@rhcarvalho rhcarvalho deleted the git-fetch-short-sha1 branch July 10, 2024 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants