From c2404728b623588aa89920ae435b48af0fef011e Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 22 Jun 2023 15:49:05 +0300 Subject: Add branch sub-option for git project vcs in bdep-new Also fix tests which failed if for the initial branch git-init defaults to the name other than 'master'. --- bdep/git.cxx | 23 ++++++++++++++++++----- bdep/git.hxx | 40 +++++++++++++++++++++++++++++++++++++++- bdep/git.txx | 6 ++++-- bdep/new.cli | 8 +++++++- bdep/new.cxx | 46 ++++++++++++++++++++++++++++++++++++++++++++-- tests/ci.testscript | 6 +++--- tests/init.testscript | 1 + tests/project.testscript | 5 ++++- tests/publish.testscript | 4 ++-- tests/release.testscript | 7 ++++--- 10 files changed, 126 insertions(+), 20 deletions(-) diff --git a/bdep/git.cxx b/bdep/git.cxx index cc697b3..f9bdc04 100644 --- a/bdep/git.cxx +++ b/bdep/git.cxx @@ -24,10 +24,8 @@ namespace bdep static optional bun_git_ver; #endif - // Check that git is at least of the specified minimum supported version. - // - void - git_check_version (const semantic_version& min_ver, bool system) + bool + git_try_check_version (const semantic_version& min_ver, bool system) { // Query and cache git version on the first call. // @@ -50,9 +48,24 @@ namespace bdep // Note that we don't expect the min_ver to contain the build component, // that doesn't matter functionality-wise for git. // - if (*gv < min_ver) + return *gv >= min_ver; + } + + // As above but issue diagnostics and fail if git is older than the + // specified minimum supported version. + // + void + git_check_version (const semantic_version& min_ver, bool system) + { + if (!git_try_check_version (min_ver, system)) + { + optional& gv (system ? sys_git_ver : bun_git_ver); + + assert (gv); // Must have been cached by git_try_check_version(). + fail << "unsupported git version " << *gv << info << "minimum supported version is " << min_ver << endf; + } } // Return git process path and the --exec-path option, if it is required for diff --git a/bdep/git.hxx b/bdep/git.hxx index b7703ea..30baf69 100644 --- a/bdep/git.hxx +++ b/bdep/git.hxx @@ -67,18 +67,50 @@ namespace bdep // Run git process. // + // Pass NULL as the repository argument if the git command is not + // repository-specific (e.g., init). + // template void run_git (const semantic_version&, bool system, bool progress, - const dir_path& repo, + const dir_path* repo, A&&... args); template inline void run_git (const semantic_version& min_ver, bool system, + const dir_path* repo, + A&&... args) + { + run_git (min_ver, + system, + true /* progress */, + repo, + forward (args)...); + } + + template + inline void + run_git (const semantic_version& min_ver, + bool system, + bool progress, + const dir_path& repo, + A&&... args) + { + run_git (min_ver, + system, + progress, + &repo, + forward (args)...); + } + + template + inline void + run_git (const semantic_version& min_ver, + bool system, const dir_path& repo, A&&... args) { @@ -173,6 +205,12 @@ namespace bdep template void git_push (const common_options&, const dir_path& repo, A&&... args); + + // Return true if git is at least of the specified minimum supported + // version. + // + bool + git_try_check_version (const semantic_version&, bool system); } #include diff --git a/bdep/git.txx b/bdep/git.txx index ef81bd8..2af8e1f 100644 --- a/bdep/git.txx +++ b/bdep/git.txx @@ -8,7 +8,7 @@ namespace bdep run_git (const semantic_version& min_ver, bool system, bool progress, - const dir_path& repo, + const dir_path* repo, A&&... args) { // Unfortunately git doesn't have any kind of a no-progress option but @@ -30,10 +30,12 @@ namespace bdep // process pr (start_git (min_ver, system, - repo, 0 /* stdin */, err /* stdout */, err /* stderr */, + (repo != nullptr + ? cstrings ({"-C", repo->string ().c_str ()}) + : cstrings ()), forward (args)...)); bool io (false); diff --git a/bdep/new.cli b/bdep/new.cli index 8f3b094..c6dc8ee 100644 --- a/bdep/new.cli +++ b/bdep/new.cli @@ -501,7 +501,12 @@ namespace bdep \li|\cb{git} Initialize a \cb{git(1)} repository inside the project and generate - \cb{.gitignore} files.|| + \cb{.gitignore} files. Recognized version control system sub-options:| + + \li|\n\ \ \ \c{\b{branch=}\i{name}} + + Use the specified name for the initial branch in the newly created + repository.|| \dl| @@ -632,6 +637,7 @@ namespace bdep // class cmd_new_git_options { + string branch; }; class cmd_new_none_options diff --git a/bdep/new.cxx b/bdep/new.cxx index 2c954fc..a2d2ad4 100644 --- a/bdep/new.cxx +++ b/bdep/new.cxx @@ -24,6 +24,13 @@ using namespace butl; namespace bdep { + // While we don't have any specific requirements for git version here, let's + // use the lowest common denominator for other bdep commands. Note that + // *_git() functions require the minimum supported git version as an + // argument. + // + static const semantic_version git_ver {2, 1, 0}; + // License id to full name map. // // Used for the license full name search for the auto-detected and @@ -1235,8 +1242,43 @@ cmd_new (cmd_new_options&& o, cli::group_scanner& args) { switch (vc) { - case vcs::git: run ("git", "init", "-q", out); break; - case vcs::none: break; + case vcs::git: + { + const cmd_new_git_options& opt (vc.git_opt); + + // If the branch name is specified, then pass it to git-init using the + // --initial-branch option if the git version is at least 2.28.0 (the + // one which has introduced this option). Otherwise, run git-init + // normally and then change HEAD to refer to the requested name. Note + // that the branch will only be created on the first commit. + // + if (opt.branch_specified ()) + { + if (git_try_check_version (semantic_version {2, 28, 0}, + true /* system */)) + { + run_git (git_ver, true /* system */, nullptr /* repo */, + "init", "-q", "--initial-branch", opt.branch (), out); + } + else + { + run_git (git_ver, true /* system */, nullptr /* repo */, + "init", "-q", out); + + run_git (git_ver, true /* system */, out, + "symbolic-ref", + "-q", + "HEAD", + "refs/heads/" + opt.branch ()); + } + } + else + run_git (git_ver, true /* system */, nullptr /* repo */, + "init", "-q", out); + + break; + } + case vcs::none: break; } } diff --git a/tests/ci.testscript b/tests/ci.testscript index cff198b..d604aa7 100644 --- a/tests/ci.testscript +++ b/tests/ci.testscript @@ -20,7 +20,7 @@ end # Create the remote repository. # +mkdir --no-cleanup prj.git -+git -C prj.git init --bare --quiet &prj.git/*** ++git -C prj.git -c init.defaultBranch=master init --bare --quiet &prj.git/*** # Adjust the local repository and push it to the remote one. # @@ -53,7 +53,7 @@ test.arguments += --yes --repository "$repository" --server "$server" \ config_cxx = [cmdline] cc config.cxx=$quote($recall($cxx.path) $cxx.config.mode, true) -new += 2>! +new += --vcs git,branch=master 2>! init += $config_cxx -d prj 2>! &prj/**/bootstrap/*** windows = ($cxx.target.class == 'windows') @@ -663,7 +663,7 @@ windows = ($cxx.target.class == 'windows') # Create the remote repository. # +mkdir --no-cleanup prj.git - +git -C prj.git init --bare --quiet &prj.git/*** + +git -C prj.git -c init.defaultBranch=master init --bare --quiet &prj.git/*** # Create the local repository and push it to the remote one. # diff --git a/tests/init.testscript b/tests/init.testscript index 2a4f686..2e66721 100644 --- a/tests/init.testscript +++ b/tests/init.testscript @@ -8,6 +8,7 @@ config_cxx = [cmdline] cc config.cxx=$quote($recall($cxx.path) $cxx.config.mode, true) +new += --vcs git,branch=master status += -d prj : create-cfg diff --git a/tests/project.testscript b/tests/project.testscript index b40f7da..a675081 100644 --- a/tests/project.testscript +++ b/tests/project.testscript @@ -9,7 +9,10 @@ # project can not be shared between multiple bdep processes. Also we need to # make sure that projects are not cloned while being used by bdep. # -+$new prj 2>- &prj/*** # By default: -t exe -l c++. +# Make sure that the initial branch is named as 'master', since tests may rely +# on that. +# ++$new --vcs git,branch=master prj 2>! &prj/*** # By default: -t exe -l c++. # The most commonly used project cloning command that copies it from the # parent scope working directory. diff --git a/tests/publish.testscript b/tests/publish.testscript index 1fd8d38..41b5eed 100644 --- a/tests/publish.testscript +++ b/tests/publish.testscript @@ -22,7 +22,7 @@ test.arguments += --repository "$repository" --yes \ config_cxx = [cmdline] cc config.cxx=$quote($recall($cxx.path) $cxx.config.mode, true) -new += 2>! +new += --vcs git,branch=master 2>! init += $config_cxx -d prj 2>! &prj/**/bootstrap/*** windows = ($cxx.target.class == 'windows') @@ -438,7 +438,7 @@ g = [cmdline] git -C prj >! 2>! # Create the remote repository. # +mkdir --no-cleanup prj.git - +git -C prj.git init --bare --quiet &prj.git/*** + +git -C prj.git -c init.defaultBranch=master init --bare --quiet &prj.git/*** +$clone_prj diff --git a/tests/release.testscript b/tests/release.testscript index 1140161..2b86964 100644 --- a/tests/release.testscript +++ b/tests/release.testscript @@ -16,7 +16,7 @@ g = [cmdline] git 2>! >&2 # Create the remote repository. # +mkdir --no-cleanup prj.git -+$g -C prj.git init --bare &prj.git/*** ++$g -C prj.git -c init.defaultBranch=master init --bare &prj.git/*** clone_rep = [cmdline] cp --no-cleanup -pr ../prj.git ./ &prj.git/*** clone_root_rep = [cmdline] cp --no-cleanup -pr $~/prj.git ./ &prj.git/*** @@ -54,6 +54,8 @@ pull2 = [cmdline] $gp2 pull --ff-only fetch2 = [cmdline] $gp2 fetch log2 = [cmdline] $gp2 log '--pretty=format:"%d %s"' +new += --vcs git,branch=master 2>- + : single-pkg : { @@ -1103,11 +1105,10 @@ log2 = [cmdline] $gp2 log '--pretty=format:"%d %s"' # Create the remote repository. # +mkdir --no-cleanup prj.git - +git -C prj.git init --bare --quiet &prj.git/*** + +git -C prj.git -c init.defaultBranch=master init --bare --quiet &prj.git/*** # Create the local repository. # - new += 2>- +$new -t empty prj &prj/*** +$new -t exe --package prj -d prj +$new -t lib --package libprj -d prj -- cgit v1.1