diff options
-rw-r--r-- | bpkg/fetch-git.cxx | 157 |
1 files changed, 99 insertions, 58 deletions
diff --git a/bpkg/fetch-git.cxx b/bpkg/fetch-git.cxx index d2c30a1..17fcc1f 100644 --- a/bpkg/fetch-git.cxx +++ b/bpkg/fetch-git.cxx @@ -2412,64 +2412,6 @@ namespace bpkg } void - git_checkout (const common_options& co, - const dir_path& dir, - const string& commit) - { - // For some (probably valid) reason the hard reset command doesn't remove - // a submodule directory that is not plugged into the repository anymore. - // It also prints the non-suppressible warning like this: - // - // warning: unable to rmdir libbar: Directory not empty - // - // That's why we run the clean command afterwards. It may also be helpful - // if we produce any untracked files in the tree between checkouts down - // the road. - // - if (!run_git (co, - co.git_option (), - "-C", dir, - "reset", - "--hard", - verb < 2 ? "-q" : nullptr, - commit)) - fail << "unable to reset to " << commit << endg; - - if (!run_git (co, - co.git_option (), - "-C", dir, - "clean", - "-d", - "-x", - "-ff", - verb < 2 ? "-q" : nullptr)) - fail << "unable to clean " << dir << endg; - - // Iterate over the registered submodules and "deinitialize" those whose - // tip commit has changed. - // - // Note that not doing so will make git treat the repository worktree as - // modified (new commits in submodule). Also the caller may proceed with - // an inconsistent repository, having no indication that they need to - // re-run git_checkout_submodules(). - // - for (const submodule& sm: - find_submodules (co, dir, dir_path () /* prefix */)) - { - dir_path sd (dir / sm.path); // Submodule full directory path. - - optional<string> commit (submodule_commit (co, sd)); - - // Note that we may re-initialize the submodule later due to the empty - // directory (see checkout_submodules() for details). Seems that git - // has no problem with such a re-initialization. - // - if (commit && *commit != sm.commit) - rm_r (sd, false /* dir_itself */); - } - } - - void git_checkout_submodules (const common_options& co, const repository_location& rl, const dir_path& dir) @@ -2593,6 +2535,105 @@ namespace bpkg submodule_failure ("unable to list repository files", prefix); } + static void + git_checkout (const common_options& co, + const dir_path& dir, + const string& commit, + const dir_path& prefix) + { + // Note that on Windows git may incorrectly deduce the type of a symlink + // it needs to create. Thus, it is recommended to specify the link type + // for directory symlinks in the project's .gitattributes file (see Using + // Symlinks in build2 Projects article for details). However, it turns out + // that if such a type has not been specified for some early package + // version and this have been fixed in some later version, then this may + // still cause problems even when this later version is being built. This + // happens because during the git repository fetch, to build the available + // package list bpkg sequentially checks out multiple package + // versions. Git, on the other hand, doesn't re-create an existing symlink + // even though .gitattributes indicates that it should. Thus, let's just + // remove all the symlinks prior to git-reset on Windows. + // +//#ifdef _WIN32 + for (const auto& l: find_symlinks (co, dir, prefix)) + { + // Note that we could potentially replace the filesystem-agnostic + // symlinks with hardlinks and thus we need to check the type of a + // filesystem entry prior to the removal. Also note try_rmsymlink() + // implementation actually doesn't distinguish between the directory and + // file symlinks. + // + path p (dir / l.first); + + const auto& e ( + path_entry (p, false /* follow_symlink */, true /* ignore_error */)); + + if (e.first && e.second.type == entry_type::symlink) + try_rmsymlink (p, false /* dir */, true /* ignore_error */); + } +//#endif + + // For some (probably valid) reason the hard reset command doesn't remove + // a submodule directory that is not plugged into the repository anymore. + // It also prints the non-suppressible warning like this: + // + // warning: unable to rmdir libbar: Directory not empty + // + // That's why we run the clean command afterwards. It may also be helpful + // if we produce any untracked files in the tree between checkouts down + // the road. + // + if (!run_git (co, + co.git_option (), + "-C", dir, + "reset", + "--hard", + verb < 2 ? "-q" : nullptr, + commit)) + fail << "unable to reset to " << commit << endg; + + if (!run_git (co, + co.git_option (), + "-C", dir, + "clean", + "-d", + "-x", + "-ff", + verb < 2 ? "-q" : nullptr)) + fail << "unable to clean " << dir << endg; + + // Iterate over the registered submodules and "deinitialize" those whose + // tip commit has changed. + // + // Note that not doing so will make git treat the repository worktree as + // modified (new commits in submodule). Also the caller may proceed with + // an inconsistent repository, having no indication that they need to + // re-run git_checkout_submodules(). + // + for (const submodule& sm: + find_submodules (co, dir, dir_path () /* prefix */)) + { + dir_path sd (dir / sm.path); // Submodule full directory path. + + optional<string> commit (submodule_commit (co, sd)); + + // Note that we may re-initialize the submodule later due to the empty + // directory (see checkout_submodules() for details). Seems that git + // has no problem with such a re-initialization. + // + if (commit && *commit != sm.commit) + rm_r (sd, false /* dir_itself */); + } + } + + void + git_checkout (const common_options& co, + const dir_path& dir, + const string& commit) + { + git_checkout (co, dir, commit, dir_path () /* prefix */); + } + // Verify symlinks in a working tree of a top repository or submodule, // recursively. // |