aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2023-01-11 21:39:15 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2023-01-12 13:07:30 +0300
commitdb4a9915b25ab682762eb73d65aab44e6bddcc1f (patch)
treea4be1c133feb07948dff504abb5e4f9cf9defe84
parent01c179eed3fcfccc7cdd262742935177dfcf5106 (diff)
Add --git-capabilities common option
-rw-r--r--bpkg/common.cli18
-rw-r--r--bpkg/fetch-git.cxx27
-rw-r--r--bpkg/options-types.hxx13
-rw-r--r--bpkg/repository-types.cli4
-rw-r--r--bpkg/types-parsers.cxx79
-rw-r--r--bpkg/types-parsers.hxx23
6 files changed, 156 insertions, 8 deletions
diff --git a/bpkg/common.cli b/bpkg/common.cli
index d870a8d..0d97631 100644
--- a/bpkg/common.cli
+++ b/bpkg/common.cli
@@ -381,6 +381,24 @@ namespace bpkg
"Assume the answer to all authentication prompts is \cb{no}."
}
+ git_capabilities_map --git-capabilities
+ {
+ "<up>=<pc>",
+ "Protocol capabilities (<pc>) for a \cb{git} repository URL prefix
+ (<up>). Valid values for the capabilities are \cb{dumb} (no shallow
+ clone support), \cb{smart} (support for shallow clone, but not for
+ fetching unadvertised commits), \cb{unadv} (support for shallow clone
+ and for fetching unadvertised commits). For example:
+
+ \
+ bpkg build https://example.org/foo.git#master \
+ --git-capabilities https://example.org=smart
+ \
+
+ See \l{bpkg-repository-types(1)} for details on the \cb{git} protocol
+ capabilities."
+ }
+
string --pager // String to allow empty value.
{
"<path>",
diff --git a/bpkg/fetch-git.cxx b/bpkg/fetch-git.cxx
index dc1e16a..4b1a242 100644
--- a/bpkg/fetch-git.cxx
+++ b/bpkg/fetch-git.cxx
@@ -556,12 +556,7 @@ namespace bpkg
// URLs, if possible. That's why the function requires the git version
// parameter.
//
- enum class capabilities
- {
- dumb, // No shallow clone support.
- smart, // Support for shallow clone, but not for unadvertised refs fetch.
- unadv // Support for shallow clone and for unadvertised refs fetch.
- };
+ using capabilities = git_protocol_capabilities;
static capabilities
sense_capabilities (const common_options& co,
@@ -1132,7 +1127,25 @@ namespace bpkg
// the first call, and so git version get assigned (and checked).
//
if (!cap)
- cap = sense_capabilities (co, url (), git_ver);
+ {
+ const repository_url& u (url ());
+
+ // Check if the protocol capabilities are overridden for this
+ // repository.
+ //
+ const git_capabilities_map& gcs (co.git_capabilities ());
+
+ if (!gcs.empty () && u.scheme != repository_protocol::file)
+ {
+ auto i (gcs.find_sup (u.string ()));
+
+ if (i != gcs.end ())
+ cap = i->second;
+ }
+
+ if (!cap)
+ cap = sense_capabilities (co, u, git_ver);
+ }
return *cap;
};
diff --git a/bpkg/options-types.hxx b/bpkg/options-types.hxx
index 6576060..30d52a0 100644
--- a/bpkg/options-types.hxx
+++ b/bpkg/options-types.hxx
@@ -8,6 +8,8 @@
#include <cassert>
#include <utility> // move()
+#include <libbutl/prefix-map.hxx>
+
#include <bpkg/types.hxx>
namespace bpkg
@@ -27,6 +29,17 @@ namespace bpkg
json
};
+ enum class git_protocol_capabilities
+ {
+ dumb, // No shallow clone support.
+ smart, // Support for shallow clone, but not for unadvertised refs fetch.
+ unadv // Support for shallow clone and for unadvertised refs fetch.
+ };
+
+ using git_capabilities_map = butl::prefix_map<string,
+ git_protocol_capabilities,
+ '/'>;
+
// Qualified options.
//
// An option that uses this type can have its values qualified using the
diff --git a/bpkg/repository-types.cli b/bpkg/repository-types.cli
index 21ddaf9..1692a13 100644
--- a/bpkg/repository-types.cli
+++ b/bpkg/repository-types.cli
@@ -189,7 +189,9 @@ that they support fetching minimal history for tags and branches and may or
may not support this for commit ids depending on the server configuration.
Note, however, that unlike for \cb{http(s)://}, for these protocols \cb{bpkg}
does not try to sense if fetching unadvertised commits is allowed and always
-assumes that it is not.
+assumes that it is not. Also note that the sensed or assumed protocol
+capabilities can be overridden for a \cb{git} repository URL prefix using the
+\cb{--git-capabilities} option (\l{bpkg-common-options(1)}).
Based on this information, to achieve optimal results the recommended protocol
for remote repositories is smart \cb{https://}. Additionally, if you are
diff --git a/bpkg/types-parsers.cxx b/bpkg/types-parsers.cxx
index e27f050..f23751d 100644
--- a/bpkg/types-parsers.cxx
+++ b/bpkg/types-parsers.cxx
@@ -3,6 +3,8 @@
#include <bpkg/types-parsers.hxx>
+#include <libbpkg/manifest.hxx>
+
namespace bpkg
{
namespace cli
@@ -141,6 +143,83 @@ namespace bpkg
throw invalid_value (o, v);
}
+ void parser<git_protocol_capabilities>::
+ parse (git_protocol_capabilities& x, bool& xs, scanner& s)
+ {
+ xs = true;
+ const char* o (s.next ());
+
+ if (!s.more ())
+ throw missing_value (o);
+
+ const string v (s.next ());
+ if (v == "dumb")
+ x = git_protocol_capabilities::dumb;
+ else if (v == "smart")
+ x = git_protocol_capabilities::smart;
+ else if (v == "unadv")
+ x = git_protocol_capabilities::unadv;
+ else
+ throw invalid_value (o, v);
+ }
+
+ void parser<git_capabilities_map>::
+ parse (git_capabilities_map& x, bool& xs, scanner& s)
+ {
+ xs = true;
+ const char* o (s.next ());
+
+ if (!s.more ())
+ throw missing_value (o);
+
+ string v (s.next ());
+ size_t p (v.rfind ('='));
+
+ if (p == string::npos)
+ throw invalid_value (o, v);
+
+ string k (v, 0, p);
+
+ // Verify that the key is a valid remote git repository URL prefix.
+ //
+ try
+ {
+ repository_url u (k);
+
+ if (u.scheme == repository_protocol::file)
+ throw invalid_value (o, k, "local repository location");
+ }
+ catch (const invalid_argument& e)
+ {
+ throw invalid_value (o, k, e.what ());
+ }
+
+ // Parse the protocol capabilities value.
+ //
+ int ac (2);
+ char* av[] = {const_cast<char*> (o),
+ const_cast<char*> (v.c_str () + p + 1)};
+
+ argv_scanner vs (0, ac, av);
+
+ bool dummy;
+ parser<git_protocol_capabilities>::parse (x[k], dummy, vs);
+ }
+
+ void parser<git_capabilities_map>::
+ merge (git_capabilities_map& b, const git_capabilities_map& a)
+ {
+ for (const auto& o: a)
+ {
+ auto i (b.find (o.first));
+
+ if (i != b.end ())
+ i->second = o.second;
+ else
+ b.emplace (o.first, o.second);
+ }
+ }
+
void parser<stdout_format>::
parse (stdout_format& x, bool& xs, scanner& s)
{
diff --git a/bpkg/types-parsers.hxx b/bpkg/types-parsers.hxx
index dba459a..7bbb414 100644
--- a/bpkg/types-parsers.hxx
+++ b/bpkg/types-parsers.hxx
@@ -84,6 +84,29 @@ namespace bpkg
};
template <>
+ struct parser<git_protocol_capabilities>
+ {
+ static void
+ parse (git_protocol_capabilities&, bool&, scanner&);
+
+ static void
+ merge (git_protocol_capabilities& b, const git_protocol_capabilities& a)
+ {
+ b = a;
+ }
+ };
+
+ template <>
+ struct parser<git_capabilities_map>
+ {
+ static void
+ parse (git_capabilities_map&, bool&, scanner&);
+
+ static void
+ merge (git_capabilities_map&, const git_capabilities_map&);
+ };
+
+ template <>
struct parser<stdout_format>
{
static void