aboutsummaryrefslogtreecommitdiff
path: root/bpkg/fetch-git.cxx
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-04-19 22:42:10 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-04-20 15:56:25 +0300
commit1f490ec2c9d1e5c80697b370d82f0bb3530aa450 (patch)
treed0a883b0d2490b1a1276c02bd1fc4b393eb86c33 /bpkg/fetch-git.cxx
parentf2f08e0758243a820fe47128ffabaa474c0e86e7 (diff)
Add support for comma-separated list of git reference filter
Diffstat (limited to 'bpkg/fetch-git.cxx')
-rw-r--r--bpkg/fetch-git.cxx160
1 files changed, 99 insertions, 61 deletions
diff --git a/bpkg/fetch-git.cxx b/bpkg/fetch-git.cxx
index fba1c8e..e6836a6 100644
--- a/bpkg/fetch-git.cxx
+++ b/bpkg/fetch-git.cxx
@@ -554,9 +554,9 @@ namespace bpkg
//
struct ref
{
- string name; // Note: without the peel marker ('^{}').
+ string name; // Note: without the peel operation ('^{...}').
string commit;
- bool peeled; // True for '...^{}' references.
+ bool peeled; // True for '...^{...}' references.
};
// List of all refs and their commit ids advertized by a repository (i.e.,
@@ -823,33 +823,32 @@ namespace bpkg
return false;
}
- // @@ Move to libbpkg when adding support for multiple fragments.
+ // Fetch and return repository fragments obtained with the repository
+ // reference filters specified. If there are no filters specified, then
+ // fetch all the tags and branches.
//
- using git_ref_filters = vector<git_ref_filter>;
-
- // Fetch the references and return their commit ids. If there are no
- // reference filters specified, then fetch all the tags and branches.
- //
- static strings
+ static vector<git_fragment>
fetch (const common_options& co,
const dir_path& dir,
const dir_path& submodule, // Used only for diagnostics.
const git_ref_filters& rfs)
{
- strings r;
+ using git_fragments = vector<git_fragment>;
+
+ git_fragments r;
capabilities cap;
repository_url url;
- strings scs; // Shallow fetch commits.
- strings dcs; // Deep fetch commits.
+ strings scs; // Shallow fetch commits.
+ strings dcs; // Deep fetch commits.
bool fetch_repo (false);
- // Translate a name to the corresponding commit. Fail if the name is not
+ // Translate a name to the corresponding reference. Fail if the name is not
// known by the remote repository.
//
- auto commit = [&co, &dir, &url] (const string& nm, bool abbr_commit)
- -> const string&
+ auto reference = [&co, &dir, &url] (const string& nm, bool abbr_commit)
+ -> const ref&
{
if (url.empty ())
url = origin_url (co, dir);
@@ -860,17 +859,44 @@ namespace bpkg
fail << "unable to fetch " << nm << " from " << url <<
info << "name is not recognized" << endg;
- return r->commit;
+ return *r;
};
- // Add a commit to the list, suppressing duplicates.
+ // Add a fragment to the resulting list, suppressing duplicates.
//
- auto add = [] (const string& c, strings& cs)
+ auto add_frag = [&r] (const git_fragment& f)
+ {
+ auto i (find_if (r.begin (), r.end (),
+ [&f] (const git_fragment& i)
+ {
+ return i.commit == f.commit;
+ }));
+
+ if (i == r.end ())
+ r.push_back (f);
+ else if (i->name.empty ())
+ i->name = f.name;
+ };
+
+ // Add a commit to the list for subsequent fetching.
+ //
+ auto add_commit = [] (const string& c, strings& cs)
{
if (find (cs.begin (), cs.end (), c) == cs.end ())
cs.push_back (c);
};
+ // Return a user-friendly git reference name.
+ //
+ auto friendly_name = [] (const string& n) -> string
+ {
+ return n.compare (0, 11, "refs/heads/") == 0
+ ? "branch " + string (n, 11)
+ : n.compare (0, 10, "refs/tags/") == 0
+ ? "tag " + string (n, 10)
+ : "reference " + n;
+ };
+
if (rfs.empty ())
{
url = origin_url (co, dir);
@@ -884,13 +910,14 @@ namespace bpkg
rf.name.compare (0, 10, "refs/tags/") != 0))
continue;
- // Add an already fetched commit to the resulting list.
+ // Add the commit to the resulting list.
+ //
+ add_frag (git_fragment {rf.commit, friendly_name (rf.name)});
+
+ // Skip the commit if it is already fetched.
//
if (commit_fetched (co, dir, rf.commit))
- {
- add (rf.commit, r);
continue;
- }
// Evaluate if the commit can be obtained with the shallow fetch and
// add it to the proper list.
@@ -901,7 +928,7 @@ namespace bpkg
// The commit is advertised, so we can fetch shallow, unless the
// protocol is dumb.
//
- add (rf.commit, cap != capabilities::dumb ? scs : dcs);
+ add_commit (rf.commit, cap != capabilities::dumb ? scs : dcs);
}
}
else
@@ -912,7 +939,7 @@ namespace bpkg
//
if (rf.commit && commit_fetched (co, dir, *rf.commit))
{
- add (*rf.commit, r);
+ add_frag (git_fragment {*rf.commit, string ()});
continue;
}
@@ -920,11 +947,11 @@ namespace bpkg
{
assert (rf.name);
- const string& c (commit (*rf.name, true /* abbr_commit */));
+ const ref& rfc (reference (*rf.name, true /* abbr_commit */));
- if (commit_fetched (co, dir, c))
+ if (commit_fetched (co, dir, rfc.commit))
{
- add (c, r);
+ add_frag (git_fragment {rfc.commit, friendly_name (rfc.name)});
continue;
}
}
@@ -932,6 +959,9 @@ namespace bpkg
// Evaluate if the commit can be obtained with the shallow fetch and
// add it to the proper list.
//
+ // Get the remote origin URL and sense the git protocol capabilities
+ // for it, if not done yet.
+ //
if (scs.empty () && dcs.empty ())
{
if (url.empty ()) // Can already be assigned by commit() lambda.
@@ -941,7 +971,7 @@ namespace bpkg
}
bool shallow (shallow_fetch (co, url, cap, rf));
- strings& commits (shallow ? scs : dcs);
+ strings& fetch_list (shallow ? scs : dcs);
// If commit is not specified, then we fetch the commit the refname
// translates to.
@@ -950,29 +980,42 @@ namespace bpkg
{
assert (rf.name);
- add (commit (*rf.name, true /* abbr_commit */), commits);
- }
+ const ref& rfc (reference (*rf.name, true /* abbr_commit */));
+ add_frag (git_fragment {rfc.commit, friendly_name (rfc.name)});
+ add_commit (rfc.commit, fetch_list);
+ }
// If commit is specified and the shallow fetch is possible, then we
// fetch the commit.
//
else if (shallow)
- add (*rf.commit, commits);
-
+ {
+ add_frag (git_fragment {*rf.commit, string ()});
+ add_commit (*rf.commit, fetch_list);
+ }
// If commit is specified and the shallow fetch is not possible, but
// the refname containing the commit is specified, then we fetch the
// whole refname history.
//
else if (rf.name)
- add (commit (*rf.name, false /* abbr_commit */), commits);
+ {
+ // Note that commits we return and fetch are likely to differ.
+ //
+ add_frag (git_fragment {*rf.commit, string ()});
+ add_commit (reference (*rf.name, false /* abbr_commit */).commit,
+ fetch_list);
+ }
// Otherwise, if the refname is not specified and the commit is not
// advertised, we have to fetch the whole repository history.
//
else
{
- add (*rf.commit, commits);
- fetch_repo = !commit_advertized (co, url, *rf.commit);
+ add_frag (git_fragment {*rf.commit, string ()});
+ add_commit (*rf.commit, fetch_list);
+
+ if (!commit_advertized (co, url, *rf.commit))
+ fetch_repo = true;
}
}
}
@@ -1057,38 +1100,33 @@ namespace bpkg
dr << " in '" << dir.posix_string () << "'"; // Is used by tests.
}
- // Note that the shallow, deep and the resulting lists don't overlap.
- // Thus, we will be moving commits between the lists not caring about
- // suppressing duplicates.
- //
-
- // First, we fetch deep commits.
+ // First, we perform the deep fetching.
//
if (!dcs.empty ())
- {
fetch (!fetch_repo ? dcs : strings (), false);
- r.insert (r.end (),
- make_move_iterator (dcs.begin ()),
- make_move_iterator (dcs.end ()));
+ // 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 move them to the resulting list.
+ // Finally, we perform the shallow fetching.
//
- strings cs;
- for (auto& c: scs)
- (commit_fetched (co, dir, c) ? r : cs).push_back (move (c));
+ if (!scs.empty ())
+ fetch (scs, true);
- // Finally, fetch shallow commits.
+ // Name the unnamed commits with a 12-character length abbreviation.
//
- if (!cs.empty ())
+ for (auto& fr: r)
{
- fetch (cs, true);
-
- r.insert (r.end (),
- make_move_iterator (cs.begin ()),
- make_move_iterator (cs.end ()));
+ if (fr.name.empty ())
+ fr.name = "commit " + fr.commit.substr (0, 12);
}
return r;
@@ -1339,18 +1377,18 @@ namespace bpkg
init (co, dir, url);
}
- strings
+ vector<git_fragment>
git_fetch (const common_options& co,
const repository_location& rl,
const dir_path& dir)
{
- git_ref_filters refs;
+ git_ref_filters rfs;
repository_url url (rl.url ());
if (url.fragment)
try
{
- refs.emplace_back (*url.fragment);
+ rfs = parse_git_ref_filters (*url.fragment);
url.fragment = nullopt;
}
catch (const invalid_argument& e)
@@ -1383,7 +1421,7 @@ namespace bpkg
}
}
- return fetch (co, dir, dir_path () /* submodule */, refs);
+ return fetch (co, dir, dir_path () /* submodule */, rfs);
}
void