【问题标题】:Is there any way to NOT ignore .git in subfolders?有什么方法可以不忽略子文件夹中的 .git 吗?
【发布时间】:2019-07-18 03:55:01
【问题描述】:

我有一个类似这样的项目:

+ MonoRepo
  - .git
  + PublicUtil
    - .git
    - PublicFile.js
  + PrivateStuff
    - internalUseOfPublicUtil.js

通过这种结构,我们可以将更新推送到 PublicUtil,这些更新是受版本控制的并且可供公众查看。然后,同时在开发方面,我们只使用 MonoRepo 进行所有提交,然后有一个脚本在必要时推送到 subgits。

我们需要这个,因为(实际上)项目要大得多。想象一下 30 个 PublicUtil 存储库。我们不能使用子模块,因为这将导致内部开发人员不得不推送到多个存储库进行一次更新(通常,多个存储库需要同时修改),然后更新 MonoRepo。这意味着在连接不同 PR 之间的点时会浪费大量时间,并且当一个子模块在没有另一个子模块的情况下被拉出,或者某些东西还没有被推送时,偶尔会出现损坏,等等。

无论如何,既然我已经解释了我为什么要尝试实现这个奇怪的事情,那么问题/问题是:

据我所知,git 不允许用户从子文件夹中推送 .git。但是,我可能错了。所以,我不妨问一下:

有没有办法忽略子文件夹中的 .git?

我可以很好地设置项目,但是如果我推送 MonoRepo 然后将其拉到另一台计算机上,它不会附带任何子文件夹 git,我将无法执行任何操作花式公开发布私人 git 内容的部分。

附:目前,我对如何解决这个问题的最佳猜测是制作一个用于重命名 git 文件夹的 pre-commit 和 post-commit 挂钩,仅由 MonoRepo 使用。我还需要一个后克隆……它会起作用,但如果可能的话,我宁愿不要那么笨拙。

【问题讨论】:

  • 只需在每个不是顶级.git.git 上运行rm -rf。如果您没有删除 .git 目录但 确实 以某种方式击败了子模块检测器,那么当您现在添加您将拥有的所有工作树文件时,您将获得相同的结果。 (是的,您可以这样做,但这种方式更容易。)
  • 我正在尝试将这些 .git(及其内容)包含在我的父级 .git 中。不要删除它们。我误解了你的建议吗?
  • 你别无选择。 Git 不存储存储库。 Git 只存储 files (作为单个提交中的条目)。有一种特殊类型的文件,称为 gitlink,它将提交的哈希 ID 存储在另一个 Git 存储库中。这仅作为子模块的一部分有用:子模块告诉您的 Git 要克隆的其他 Git 存储库。然后 gitlink 告诉你的 Git 在另一个 Git 存储库中还有什么其他提交到另一个 Git 存储库中的 git checkout
  • 我没有 git 子模块。我已经解决了这些问题。我只是想存储文件。特别是在**/.git 中找到的文件。
  • 嗯,你可以存储它们,但是你必须把它们变成一个 Git 存储库来检查它们——因为存储在 Git 存储库中的提交中的文件不是作为常规文件存储的,而是作为对象的数据库。然后,您必须运行 git checkout 以将 Git 化的文件从数据库中提取为有用的格式。

标签: git monorepo


【解决方案1】:

我已经制定了一个适用于具有 node.js 的项目的解决方案。如果我在 bash 方面做得更好,我会把它做成一个 bash 脚本。如果你可以在 bash 中做到这一点,我会认为这是对这个问题的真正答案。无论如何,这是我的设置脚本:

const fs = require("fs");
const path = require("path");
const { exec } = require('child_process');

function createRenamer(forCommit) {
  const from = forCommit ? ".xgit" : ".git";
  const to = forCommit ? ".git" : ".xgit";
  return [
    `for x in $(find */ -name ${from}); do`,
    `  mv ${forCommit ? "-n" : ""} $x $(echo "$x" | sed 's/${from.replace(".", "\\.")}$/${to}/')`,
    `done`,
    forCommit ? `&& git add */.xgit` : ""
  ].join("\n");
}

const hooksPath = path.join(__dirname, "./.git/hooks");

const preCommitFilename = hooksPath + "/pre-commit";
fs.writeFileSync(preCommitFilename, createRenamer(true));
fs.chmodSync(preCommitFilename, 0o755); 

const postCommitFilename = hooksPath + "/post-commit";
fs.writeFileSync(postCommitFilename, createRenamer(false));
fs.chmodSync(postCommitFilename, 0o755); 

exec(postCommitFilename);

它必须在任何时候运行一次,它会初始化所有的子存储库。它的作用是:

  • 创建一个预克隆 bash 脚本,该脚本
    • 将每个**/.git 更新为**/.xgit
    • 然后 git 添加所有 .xgit 文件。这实际上只需要完成一次,但它使新的子回购更加自动化
  • 然后它会创建一个克隆后 bash 脚本,该脚本将每个 **/.xgit 更新回 **/.git,以便再次使用它
  • 然后运行后克隆,初始化子仓库

CAVEAT:这仍然是一个不完整的解决方案。如果有人提取了 repo,则 .gits 在重命名脚本运行之前不会更新。此外,mv 的工作方式非常愚蠢,似乎没有办法重命名目录而不无意中复制到您想要命名的文件夹中。这让我大吃一惊,我肯定是错的……(How can I rename a directory in bash, without overwriting or moving inside a pre-existing target?)

我会在弄清楚问题后更新此内容。

【讨论】:

    【解决方案2】:

    除了删除 .git 子文件夹之外,为了在真正的 monorepo 中提交,您可以随时在其自己的 Git 存储库中记录一个子文件夹(如 PublicUtil)。

    你需要的只是它的 .git 存储在其他地方,主要的 monorepo 结构之外,然后做一个

     git --git-dir=/path/to/monorepo§publicUtils \
         --work-tree=/path/to/PublicUtils/.git add .
    
     git --git-dir=/path/to/monorepo§publicUtils \
         --work-tree=/path/to/PublicUtils/.git commit -m "record PublicUtils"
    

    这允许您维护两组历史记录:

    • 你的单一仓库中的一个全局
    • 特定于子文件夹项目。

    所有这些都可以通过钩子自动化,这些钩子会检测哪些子文件夹(项目)涉及新的提交,并进行额外的git --git-dir=... --work-tree=... 调用。

    【讨论】:

    • 如果我拉出 MonoRepo,它将不包含子文件夹 .gits。它们仍然不会包含在 monorepo 中。还是我误解了你的意思?
    • @SephReed 是的,这就是想法:将 monorepo 与其多项目 repos 分开(这可能是带有子模块的单独存储库的一部分)。我建议将多个存储库放在一个集中的地方,只有当有人推回主要的 monorepo 时才会自动更新。
    猜你喜欢
    • 2014-05-06
    • 2016-12-08
    • 2020-12-10
    • 2018-06-25
    • 1970-01-01
    • 2016-04-09
    • 2011-10-23
    • 2015-04-20
    相关资源
    最近更新 更多