Git是一个强大的工具。而且,在我看来,到2020年,唯一的版本控制系统软件开发人员应该使用该软件。也许有一天,该镇将有一位新国王。但是在2020年,Git是唯一值得您投入时间的解决方案。为什么?因为每个人都使用它!从字面上看,每个人。
但是,并不是每个人都是git的专家!我相信这是三个git命令,除非您真正了解自己在做什么,否则切勿使用!
简而言之,git s ubmodule命令允许您将git仓库添加到git仓库中!但是,管理使用多个子模块的项目可能很麻烦。
上面的命令将指定为目录的存储库添加到当前git存储库中。这个目录本身就是一个完整的git仓库。如果您将目录更改为新的子模块,则会注意到所有git命令的行为都如同您刚刚移至全新的存储库一样。因为你有!
在此,我们将顶级存储库称为“父”存储库,将子模块称为“子级”。在父存储库中,作为子子模块的目录或多或少准确地对待了git中的任何其他文件。更改子存储库后,您将在父存储库的git状态下看到该子模块具有新的提交或已修改的内容。从父级的角度来看,可以将git子模块视为一个单独的文件,该文件包含远程位置和提交ID!更新此文件以及父级对子模块的引用的唯一方法是跟踪不同的提交ID。仅当子存储库的最新提交(即HEAD)更改时,父存储库git add / git才能提交更新的子模块。
直观地表示,如果您正在子级子模块中积极工作,则工作流还必须包括管理父级对子级的引用。例如,当您在子项中进行新提交时,还需要提交父项对子项的引用,以使整个项目保持最新状态。这显然会很痛苦。特别是当您有许多子模块或多层嵌套子模块时。同样,任何子存储库中的每次提交都需要更新对该存储库的所有引用。我认为这就是为什么仅在父存储库的开发团队与子存储库的开发团队不同的情况下才使用子模块的原因。子模块可以很好地跟踪对外部源代码依赖项的引用。但是,我认为子模块不能很好地将单个开发团队的项目划分为多个子组件。结果造成的复杂性增加不值得维持父母/子女关系所需的额外工作。
另一个有趣的注意事项:使用git子模块时,您必须认识到父存储库将不存储该子模块内容的备份。仅供参考。如果您不拥有子模块引用的位置,则无法保证您引用的内容将永远存在于该位置。如果您不拥有该远程位置,我强烈建议您创建自己的远程位置,该位置是原始位置的副本,而不是依赖第三方将其存储库永久保持在该确切位置。
git filter-branch是一个非常有趣的命令,它具有非常有用的利基用例。
上面的命令将从HEAD开始并从HEAD开始对树中的每个提交执行。该命令将使用每个提交的新内容创建一个新提交。例如,假设您不小心将私人用户凭据作为git存储库中的文件提交。您突然注意到这种情况在以后多次提交。您可以运行以下命令,通过从每个提交中删除该文件来重写所有先前的提交。
您为什么要这样做,而不仅仅是创建一个删除该文件的新提交?因为如果您也没有从所有过去的提交中删除文件,那么恶意软件总会返回到先前的提交并查看您的私人凭据。
大!我们实现了我们想要做的。这个命令似乎很棒!有什么问题?!
问题是您现在已经创建了一个具有全新历史记录的全新存储库。如果您在一个使用共享远程存储库的团队中工作,那么您只是破坏了向/从远程存储库推/拉的能力。为了缓解这种情况,您可以强行推送到远程存储库,但是有可能永久性地破坏有价值的更改。
由于不再有共同的历史记录,因此需要将本地存储库与远程存储库进行比较,或者需要在git filter-branch之前进行比较。此外,在强行推入之后,您将破坏团队推入/拉出远程存储库的能力。这导致您的团队需要删除其本地分支或存储库才能克隆/提取新历史记录。
而且,将不会存储git filter-branch命令本身创建的更改的历史记录。最好绝对确保git filter-branch引入的更改是您想要的更改,因为没有回头路了!
git rebase也许是现存最有争议的git命令。有许多开发团队要求git在git merge上重新设置基础。我个人是git merge方法的粉丝。原因很简单。 git rebase重写历史记录,而重写历史记录本质上是危险的。
让我先解释一下git merge和git rebase之间的区别,然后再进一步解释为什么我更喜欢git merge。
git merge和git rebase解决了相同的问题。他们解决了将具有并行更改的两个分支合并为具有一个历史记录的单个分支的问题。让我们检查一下我们拥有一个主分支和一个功能分支的情况,如下图所示。
功能分支是在“ m2”提交时从主分支创建的。然后,功能分支在提交“ f1”和“ f2”中对存储库进行更改。在开发“ f1”和“ f2”更改的同时,master分支已更新为“ m3”提交。如何使用功能分支中创建的更改来更新master分支?
git merge通过保留feature分支的所有提交来做到这一点。在master分支上,将创建一个新提交,该提交与master分支的HEAD和feature分支的HEAD都具有历史关系。存在此合并提交以解决两个分支的最终状态。如果不能自动解决两个分支之间引入的更改,则会发生合并冲突,并且用户必须通过确定在创建合并提交之前应保留哪些更改来解决冲突。将主分支合并到功能分支或将功能分支合并到主分支的方向通常不会影响最终的分辨率。所有更改都将在最初创建合并提交的哪个分支上进行。
但是,git rebase通过在历史记录内移动提交来将两个分支解析为一个历史记录。例如,不是将“ f1”提交的更改应用于“ m2”。 git rebase将存储在“ f1”和“ f2”中所做的更改,将功能分支移至基于“ m3”的位置,然后在“ m3”上应用“ f1”和“ f2”。如果存在冲突,则必须针对将应用于新的基本提交的每个提交一一解决该冲突(在我们的示例中,在“ m3”上同时应用“ f1”和“ f2”时可能会发生合并冲突) 。
git rebase很棒,因为生成的git历史是一条直线。没有一个提交具有多个父级。但是,在重新设置基准之后,提交“ f1”和“ f2”不再是原始“ f1”和“ f2”提交的真正保留。他们的提交ID将不同。它们的内容可能有所不同。结果,您陷入了git filter-branch创建的同一问题。如果您正在使用共享的远程存储库并在本地进行了变基。您需要强制将经过重新调整的更改推送到远程存储库。强制推送可能会永久删除远程存储库中可能有价值的更改。此外,强行推销会破坏您的协作者向远程存储库中的该分支推入/拉出的能力。
通常,团队将git rebate的使用授权仅发生在对master分支的拉动请求期间。因此,避免了潜在的协作问题。但是,我仍然希望永远不要重写历史记录。
与开发人员团队合作时,重要的是git不要妨碍无缝协作。一般而言,应避免在git中重写历史记录或更改大多数人与git交互方式的约定的行为。 Git子模块通过使更改在多个级别上进行跟踪而改变了整个git约定,而不是整个项目一次。 git filter-branch和git rebase都重新编写了git历史记录。我们讨论了git历史记录更改时会出现一些复杂情况。在我看来,git rebase与git merge相比没有什么优势,应该几乎避免使用。但是,git filter-branch在可怕的情况下有其用途。它比“ Oh $#!&”按钮更实用。很高兴知道它的存在,但是希望您永远不需要使用它。