aboutsummaryrefslogtreecommitdiff
path: root/bpkg
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-05-23 17:38:30 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-05-24 10:07:58 +0300
commit85511898b89cb31358c18315db456f98de1f34e0 (patch)
treedccb5769b67d05c70cff45b7ca79d85e329af917 /bpkg
parent71375bfe421704d5a4ada22cb978ce4ee8b0d1ff (diff)
Work around git-reset issue when symlink=dir attribute in .gitattributes is ignored (GH issue #346)
Diffstat (limited to 'bpkg')
-rw-r--r--bpkg/fetch-git.cxx157
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.
//