diff options
-rw-r--r-- | bpkg/fetch-git.cxx | 67 | ||||
-rw-r--r-- | tests/remote-git.testscript | 10 | ||||
-rw-r--r-- | tests/rep-fetch-git-commit.testscript | 3 | ||||
-rw-r--r-- | tests/rep-fetch-git-refname.testscript | 1 |
4 files changed, 64 insertions, 17 deletions
diff --git a/bpkg/fetch-git.cxx b/bpkg/fetch-git.cxx index 20bad96..2cf526d 100644 --- a/bpkg/fetch-git.cxx +++ b/bpkg/fetch-git.cxx @@ -74,14 +74,11 @@ namespace bpkg static string git_line (const common_options&, const char* what, A&&... args); - // Start git process. On the first call check that git version is 2.14.0 or - // above, and fail if that's not the case. - // - // Note that prior to 2.14.0 the git-fetch command doesn't accept commit id - // as a refspec: - // - // $ git fetch --no-recurse-submodules --depth 1 origin 5e8245ee3526530a3467f59b0601bbffb614f45b - // error: Server does not allow request for unadvertised object 5e8245ee3526530a3467f59b0601bbffb614f45b + // Start git process. On the first call check that git version is 2.11.0 or + // above, and fail if that's not the case. Note that the full functionality + // (such as being able to fetch unadvertised commits) requires 2.14.0. And + // supporting versions prior to 2.11.0 doesn't seem worth it (plus other + // parts of the toolchain also requires 2.11.0). // // Also note that git is executed in the "sanitized" environment, having the // environment variables that are local to the repository being unset (all @@ -89,6 +86,7 @@ namespace bpkg // does for commands executed for submodules. Though we do it for all // commands (including the ones related to the top repository). // + static semantic_version git_ver; static optional<strings> unset_vars; template <typename O, typename E, typename... A> @@ -124,9 +122,11 @@ namespace bpkg info << "produced by '" << co.git () << "'; " << "use --git to override" << endg; - if (*v < semantic_version {2, 14, 0}) + if (*v < semantic_version {2, 11, 0}) fail << "unsupported git version " << *v << - info << "minimum supported version is 2.14.0" << endf; + info << "minimum supported version is 2.11.0" << endf; + + git_ver = move (*v); // Sanitize the environment. // @@ -910,8 +910,9 @@ namespace bpkg // Collect the list of commits together with the refspecs that should be // used to fetch them. If refspecs are absent then the commit is already - // fetched (and must not be re-fetched). Otherwise, it it is empty, then - // the whole repository history must be fetched. + // fetched (and must not be re-fetched). Otherwise, if it is empty, then + // the whole repository history must be fetched. And otherwise, it is a + // list of commit ids. // // Note that the <refname>@<commit> filter may result in multiple refspecs // for a single commit. @@ -1174,6 +1175,40 @@ namespace bpkg // assert (!refspecs.empty () || !shallow); + // Prior to 2.14.0 the git-fetch command didn't accept commit id as a + // refspec: + // + // $ git fetch --no-recurse-submodules --depth 1 origin 5e8245ee3526530a3467f59b0601bbffb614f45b + // error: Server does not allow request for unadvertised object 5e8245ee3526530a3467f59b0601bbffb614f45b + // + // We will try to remap commits back to git refs (tags, branches, etc) + // based on git-ls-remote output and fail if unable to do so (which + // should only happen for unadvertised commits). + // + // Note that in this case we will fail only for servers supporting + // unadvertised refs fetch. For other protocols we have already fallen + // back to fetching some history, passing to fetch() either advertised + // commit ids (of branches, tags, etc) or an empty refspecs list (the + // whole repository history). So we could just reduce the server + // capabilities from 'unadv' to 'smart' for such old clients. + // + optional<strings> remapped_refspecs; + if (!refspecs.empty () && git_ver < semantic_version {2, 14, 0}) + { + remapped_refspecs = strings (); + + for (const string& c: refspecs) + { + const ref* r (load_refs (co, url ()).find_commit (c)); + + if (r == nullptr) + fail << "git version is too old for specified location" << + info << "consider upgrading git to 2.14.0 or above"; + + remapped_refspecs->push_back (r->name); + } + } + // Note that we suppress the (too detailed) fetch command output if the // verbosity level is 1. However, we still want to see the progress in // this case, unless stderr is not directed to a terminal. @@ -1192,7 +1227,7 @@ namespace bpkg verb == 1 && fdterm (2) ? opt ("--progress") : nullopt, verb < 2 ? opt ("-q") : verb > 3 ? opt ("-v") : nullopt, "origin", - refspecs)) + remapped_refspecs ? *remapped_refspecs : refspecs)) fail << "unable to fetch " << dir << endg; }; @@ -1318,7 +1353,11 @@ namespace bpkg co.git_option (), "-C", dir, - !prefix.empty () + // Note that older git versions don't recognize the --super-prefix + // option but seem to behave correctly without any additional + // efforts when it is omitted. + // + !prefix.empty () && git_ver >= semantic_version {2, 14, 0} ? strings ({"--super-prefix", prefix.posix_representation ()}) : strings (), diff --git a/tests/remote-git.testscript b/tests/remote-git.testscript index 2ce0fa0..595220d 100644 --- a/tests/remote-git.testscript +++ b/tests/remote-git.testscript @@ -26,11 +26,15 @@ end +echo "$git_version" | sed -e 's/(\d+).*/\1/' | set git_version_major +echo "$git_version" | sed -e 's/\d+\.(\d+).*/\1/' | set git_version_minor -# This flag must be used by testscripts to decide if they should skip git -# repository-related tests. +# These flags must be used by testscripts to decide if they should skip git +# repository-related tests. See bpkg/fetch-git.cxx for the functionality +# reduction details for partially supported git versions. # git_supported = ($git_version_major > 2 || \ - $git_version_major == 2 && $git_version_minor >= 14) + $git_version_major == 2 && $git_version_minor >= 11) + +git_fully_supported = ($git_version_major > 2 || \ + $git_version_major == 2 && $git_version_minor >= 14) # Output directory path that testscripts must use to prepare repositories # required by tests they contain. diff --git a/tests/rep-fetch-git-commit.testscript b/tests/rep-fetch-git-commit.testscript index ff6c7a3..f5aa685 100644 --- a/tests/rep-fetch-git-commit.testscript +++ b/tests/rep-fetch-git-commit.testscript @@ -6,6 +6,7 @@ : unadvertised : +if ($git_fully_supported || $git_protocol != 'https-smart-unadv') { +git -C ../style-basic log '--pretty=format:%H' --all --grep='README' | \ set commit @@ -72,6 +73,7 @@ %fetching git:.+style-basic#@$commit% %querying .+style-basic\.git%? %fetching from .+style-basic.\git% + %querying .+style-basic\.git%? $warn 1 package\(s\) in 1 repository\(s\) EOE @@ -86,6 +88,7 @@ %fetching git:.+style-basic#stable@$commit% %querying .+style-basic\.git%? %fetching from .+style-basic\.git% + %querying .+style-basic\.git%? $warn 1 package\(s\) in 1 repository\(s\) EOE diff --git a/tests/rep-fetch-git-refname.testscript b/tests/rep-fetch-git-refname.testscript index fe5b330..88f1e2a 100644 --- a/tests/rep-fetch-git-refname.testscript +++ b/tests/rep-fetch-git-refname.testscript @@ -39,6 +39,7 @@ : changed : + if ($git_fully_supported || $git_protocol != 'https-smart-unadv') { g = git -C u = "$rep_git/state1" |