aboutsummaryrefslogtreecommitdiff
path: root/bpkg/fetch-git.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bpkg/fetch-git.cxx')
-rw-r--r--bpkg/fetch-git.cxx226
1 files changed, 152 insertions, 74 deletions
diff --git a/bpkg/fetch-git.cxx b/bpkg/fetch-git.cxx
index 1695bcf..ab3255c 100644
--- a/bpkg/fetch-git.cxx
+++ b/bpkg/fetch-git.cxx
@@ -35,8 +35,6 @@ namespace bpkg
static const diag_noreturn_end<fail_git> endg;
- using opt = optional<const char*>; // Program option.
-
static strings
timeout_opts (const common_options& co, repository_protocol proto)
{
@@ -209,19 +207,77 @@ namespace bpkg
}
}
- // Run git process.
+ // Run git process, optionally suppressing progress.
//
template <typename... A>
static process_exit
- run_git (const common_options& co, A&&... args)
+ run_git (const common_options& co, bool progress, A&&... args)
{
+ // Unfortunately git doesn't have any kind of a no-progress option. The
+ // only way to suppress progress is to run quiet (-q) which also
+ // suppresses some potentially useful information. However, git suppresses
+ // progress automatically if its stderr is not a terminal. So we use this
+ // feature for the progress suppression by redirecting git's stderr to our
+ // own diagnostics stream via a proxy pipe.
+ //
+ fdpipe pipe;
+
+ if (!progress)
+ pipe = open_pipe ();
+
process pr (start_git (co,
- 1 /* stdout */, 2 /* stderr */,
+ 1 /* stdout */,
+ !progress ? pipe.out.get () : 2 /* stderr */,
forward<A> (args)...));
+
+ if (!progress)
+ {
+ // Shouldn't throw, unless something is severely damaged.
+ //
+ pipe.out.close ();
+
+ try
+ {
+ ifdstream is (move (pipe.in), fdstream_mode::skip, ifdstream::badbit);
+
+ // We could probably write something like this, instead:
+ //
+ // *diag_stream << is.rdbuf () << flush;
+ //
+ // However, it would never throw and we could potentially miss the
+ // reading failure, unless we decide to additionally mess with the
+ // diagnostics stream exception mask.
+ //
+ for (string l; !eof (getline (is, l)); )
+ *diag_stream << l << endl;
+
+ is.close ();
+
+ // Fall through.
+ }
+ catch (const io_error& e)
+ {
+ // Fail if git exited normally with zero code, so the issue won't go
+ // unnoticed. Otherwise, let the caller handle git's failure.
+ //
+ if (pr.wait ())
+ fail << "unable to read git diagnostics: " << e;
+
+ // Fall through.
+ }
+ }
+
pr.wait ();
return *pr.exit;
}
+ template <typename... A>
+ static process_exit
+ run_git (const common_options& co, A&&... args)
+ {
+ return run_git (co, true /* progress */, forward<A> (args)...);
+ }
+
// Run git process and return it's output as a string. Fail if the output
// doesn't contain a single line.
//
@@ -695,12 +751,15 @@ namespace bpkg
if (i != repository_refs.end ())
return i->second;
- if (verb)
+ if (verb && !co.no_progress ())
text << "querying " << url;
refs rs;
fdpipe pipe (open_pipe ());
+ // Note: ls-remote doesn't print anything to stderr, so no progress
+ // suppression is required.
+ //
process pr (start_git (co,
pipe, 2 /* stderr */,
timeout_opts (co, url.scheme),
@@ -823,7 +882,7 @@ namespace bpkg
? strings ({"--separate-git-dir=" + git_dir.string ()})
: strings (),
- verb < 2 ? opt ("-q") : nullopt,
+ verb < 2 ? "-q" : nullptr,
dir))
fail << "unable to init " << dir << endg;
@@ -1262,27 +1321,44 @@ namespace bpkg
}
};
- // 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.
+ // Map verbosity level. 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 we were asked to suppress it (git also
+ // suppress progress for a non-terminal stderr).
//
+ cstrings vos;
+ bool progress (!co.no_progress ());
+
+ if (verb < 2)
+ {
+ vos.push_back ("-q");
+
+ if (progress)
+ {
+ if (verb == 1 && stderr_term)
+ vos.push_back ("--progress");
+ }
+ else
+ progress = true; // Already suppressed with -q.
+ }
+ else if (verb > 3)
+ vos.push_back ("-v");
+
// Also note that we don't need to specify --refmap option since we can
// rely on the init() function that properly sets the
// remote.origin.fetch configuration option.
//
if (!run_git (co,
+ progress,
timeout_opts (co, url ().scheme),
co.git_option (),
"-C", dir,
"fetch",
"--no-recurse-submodules",
-
- shallow ? cstrings ({"--depth", "1"}) :
- shallow_repo () ? cstrings ({"--unshallow"}) :
- cstrings (),
-
- verb == 1 && fdterm (2) ? opt ("--progress") : nullopt,
- verb < 2 ? opt ("-q") : verb > 3 ? opt ("-v") : nullopt,
+ (shallow ? cstrings ({"--depth", "1"}) :
+ shallow_repo () ? cstrings ({"--unshallow"}) :
+ cstrings ()),
+ vos,
"origin",
remapped_refspecs ? *remapped_refspecs : refspecs))
fail << "unable to fetch " << dir << endg;
@@ -1294,72 +1370,74 @@ namespace bpkg
fail << "unable to test if " << dir << " is shallow" << endg;
};
- // Print the progress indicator.
- //
- // Note that the clone command prints the following line prior to the
- // progress lines:
- //
- // Cloning into '<dir>'...
- //
- // The fetch command doesn't print anything similar, for some reason.
- // This makes it hard to understand which superproject/submodule is
- // currently being fetched. Let's fix that.
+ // Print progress.
//
- // Also note that we have "fixed" that capital letter nonsense and stripped
- // the trailing '...'.
- //
- if (verb)
+ if (verb && !co.no_progress ())
{
- diag_record dr (text);
- dr << "fetching ";
+ // Note that the clone command prints the following line prior to the
+ // progress lines:
+ //
+ // Cloning into '<dir>'...
+ //
+ // The fetch command doesn't print anything similar, for some reason.
+ // This makes it hard to understand which superproject/submodule is
+ // currently being fetched. Let's fix that.
+ //
+ // Also note that we have "fixed" that capital letter nonsense and
+ // stripped the trailing '...'.
+ //
+ {
+ diag_record dr (text);
+ dr << "fetching ";
- if (!submodule.empty ())
- dr << "submodule '" << submodule.posix_string () << "' ";
+ if (!submodule.empty ())
+ dr << "submodule '" << submodule.posix_string () << "' ";
- dr << "from " << url ();
+ dr << "from " << url ();
- if (verb >= 2)
- dr << " in '" << dir.posix_string () << "'"; // Is used by tests.
- }
+ if (verb >= 2)
+ dr << " in '" << dir.posix_string () << "'"; // Is used by tests.
+ }
- // First, we perform the deep fetching.
- //
- if (fetch_repo || !dcs.empty ())
- {
// Print warnings prior to the deep fetching.
//
+ if (fetch_repo || !dcs.empty ())
{
- diag_record dr (warn);
- dr << "fetching whole " << (fetch_repo ? "repository" : "reference")
- << " history";
-
- if (!submodule.empty ())
- dr << " for submodule '" << submodule.posix_string () << "'";
+ {
+ diag_record dr (warn);
+ dr << "fetching whole " << (fetch_repo ? "repository" : "reference")
+ << " history";
+
+ if (!submodule.empty ())
+ dr << " for submodule '" << submodule.posix_string () << "'";
+
+ dr << " ("
+ << (caps () == capabilities::dumb
+ ? "dumb HTTP"
+ : "unadvertised commit") // There are no other reasons so far.
+ << ')';
+ }
- dr << " ("
- << (caps () == capabilities::dumb
- ? "dumb HTTP"
- : "unadvertised commit") // There are no other reasons so far.
- << ')';
+ if (caps () == capabilities::dumb)
+ warn << "no progress will be shown (dumb HTTP)";
}
+ }
- if (caps () == capabilities::dumb)
- warn << "no progress will be shown (dumb HTTP)";
-
- // Fetch.
- //
- fetch (fetch_repo ? strings () : dcs, false);
+ // Fetch.
+ //
+ // First, we perform the deep fetching.
+ //
+ fetch (fetch_repo ? strings () : dcs, false);
- // After the deep fetching some of the shallow commits might also be
- // fetched, so we drop them from the fetch list.
- //
- for (auto i (scs.begin ()); i != scs.end (); )
- {
- if (commit_fetched (co, dir, *i))
- i = scs.erase (i);
- else
- ++i;
- }
+ // After the deep fetching some of the shallow commits might also be
+ // fetched, so we drop them from the fetch list.
+ //
+ for (auto i (scs.begin ()); i != scs.end (); )
+ {
+ if (commit_fetched (co, dir, *i))
+ i = scs.erase (i);
+ else
+ ++i;
}
// Finally, we perform the shallow fetching.
@@ -1425,7 +1503,7 @@ namespace bpkg
: strings (),
"submodule--helper", "init",
- verb < 2 ? opt ("-q") : nullopt))
+ verb < 2 ? "-q" : nullptr))
failure ("unable to initialize submodules");
repository_url orig_url (origin_url (co, dir));
@@ -1589,7 +1667,7 @@ namespace bpkg
// Let's make the message match the git-submodule script output
// (again, except for capitalization).
//
- if (verb)
+ if (verb && !co.no_progress ())
text << "submodule path '" << psd << "': checked out '" << commit
<< "'";
@@ -1716,7 +1794,7 @@ namespace bpkg
"-C", dir,
"reset",
"--hard",
- verb < 2 ? opt ("-q") : nullopt,
+ verb < 2 ? "-q" : nullptr,
commit));
if (!pr.wait ())
fail << "unable to reset to " << commit << endg;
@@ -1729,7 +1807,7 @@ namespace bpkg
"-d",
"-x",
"-ff",
- verb < 2 ? opt ("-q") : nullopt))
+ verb < 2 ? "-q" : nullptr))
fail << "unable to clean " << dir << endg;
}