DVCS 中不同分支模型的优缺点

发布于 2024-08-09 10:54:21 字数 555 浏览 1 评论 0原文

分布式版本控制三巨头(Git、Bazaar 和 Mercurial)各自对待分支的方式相当不同。例如,在 Bazaar 中,分支是单独的存储库(实际上是父存储库的不同副本);在您的文件系统上,不同的分支位于不同的目录中。另一方面,在 Git 中,您可以在同一个存储库中存在多个分支(因此位于文件系统的同一目录中)。 Mercurial 支持这两种行为 ,后者带有命名分支

这些不同的分支模型有哪些优缺点?在我看来,Bazaar 的一个分支、一个存储库的方法使得分支比 Git 的方法更痛苦(例如,要在 Bazaar 中使用分支,我必须首先创建分支,然后 cd 出我当前的工作副本,然后签出新分支,就像我在 SVN 中一样)。

The Big Three of distributed version control (Git, Bazaar, and Mercurial) each treat branching fairly differently. In Bazaar, for example, branches are separate repos (actually, divergent copies of the parent repo); on your file system, different branches live in different directories. In Git, on the other hand, you can have multiple branches existing in the same repo (and therefore in the same directory on your file system). Mercurial supports both behaviors, the latter with named branches.

What are the pros and cons associated with these different branching models? In my mind, Bazaar's approach of one branch, one repo makes branching more of a pain than Git's approach (e.g. to use a branch in Bazaar, I have to first create the branch, then cd out of my current working copy, then check out the new branch, like I would in SVN).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

云朵有点甜 2024-08-16 10:54:21

Bazaar 并不要求您按照您所描述的方式工作。我实际上已经写了关于它的一篇博客文章两天前。您几乎可以仅使用单个工作树进行工作,在各个分支之间切换并创建新分支,而无需离开工作树。有用的命令是:checkout, 切换, branch --switch。查看 Bazaar 的工作流程文档,您将看到您几乎可以按照您想要的任何方式配置它。

Bazaar doesn't require you to work the way you describe. I've actually written a blog post about it two days ago. You can work pretty much with only a single working tree, switching between various branches and creating new branches without leaving the working tree. Useful commands for this are: checkout, switch, branch --switch. Check out the workflow documentation for Bazaar, you will see that you can configure it in almost any way you want.

就此别过 2024-08-16 10:54:21

除了 Git 之外,我对 VCS 中的分支模型了解不多。我想说,在任何 DVCS 中,您都可以通过克隆来实现分支(通过克隆来创建分支)。 Mercurial 所谓的“命名分支”(据我所知)实际上提交标签仅解释为分支,有时需要对修订进行本地编号来解决歧义。我认为 Mercurial“书签”与 Git 分支非常相似。 MonotoneDarcs。我认为 Subversion 使用的“通过复制进行分支”,其中项目名称和分支名称之间是按约定分隔的,是一个错误的想法。


在 Git 中,修订形成了提交的有向无环图 (DAG)。它是定向的,因为提交有父母。这是一个非常重要的问题:提交的 DAG 中的边是从提交到其父级(或者,在合并提交的情况下,是两个或多个其父级)。提交图是非循环的,这意味着不存在以同一对象开始和结束的链(没有路径)。

Git 术语表 将“分支”定义为 积极的发展路线。这个想法是 Git 分支实现的背后。

分支上的最新提交称为该分支的提示。分支的尖端由分支头引用,这只是此提交的符号名称。在其“松散”形式中,这样的分支头(例如名为“master”的分支)只是 git 存储库内的 refs/heads/ 目录中某处的一个文件(位于 .git 内) > dir),其中包含对分支当前尖端的引用:提交的 SHA-1 标识符(作为十六进制字符串)。

当您在 Git 中创建新提交时,当前签出分支的尖端会向前移动。换句话说,新的提交是在当前分支的顶部创建的,并且分支头前进到新的提交(有点类似于指向堆栈顶部的指针可能前进的方式)。

单个 git 存储库可以跟踪任意数量的分支,但您的工作树(如果有)仅与其中之一相关联(“当前”或“签出”分支)。当前分支由 HEAD 指针给出。 HEAD(通常)是指向当前签出分支(指向分支头名称)的指针,就像分支头是指向分支尖端的指针一样。

例如,如果当前签出的分支是“master”,则 .git/HEAD 文件(代表 HEAD)将包含单个 LF 终止行,其中 ref: refs/heads/master (对 refs/heads/master 的符号引用)和 .git/refs/heads/master (“master”分支的头)将包含例如 LF 终止行0b127cb8ab975e43398a2b449563ccb78c437255,它是“master”分支尖端的 SHA-1 标识符(也就是说,如果当前分支未“打包”:那么您必须查看 .git/packed-参考)。

Git中的一些命令,例如“git commit”或“git reset”操作/更改分支头;其他例如“git checkout”操纵/更改 HEAD(对当前分支的符号引用)。

git log branch”命令显示从分支尖端可到达的所有提交,这意味着分支尖端、其父级、该父级提交的父级(或父级)等。它显示提交 DAG 的一部分。

在 Git 中,删除分支意味着简单地删除分支头。这可能意味着某些提交变得“不可见”、无法访问的 freom 引用(分支和标签),这意味着在某些时候这些提交可能会被垃圾收集并从存储库中删除。但是如果你可以使用“gitbranch -d”删除分支那么这意味着不会丢失任何提交;您可以使用“git Branch -D”强制删除分支。重命名分支只是简单地重命名分支头,即分支尖端的符号引用(符号名称);分支名称不会保存在提交对象中的任何位置。


Git 还有reflogs 的概念,它是分支尖端指向何处(以及何时)的本地历史记录。例如,如果您使用“git commit --amend”修改提交,则分支提示将被修改后的提交替换,并且 HEAD^ 将是修改之前和之后提交的父级,而修改之前和之后的版本将在 reflog 中包含条目修改后。如果您使用“git重置”回滚历史记录,reflog将包含回滚之前旧分支提示的信息。

简而言之,reflog 为 git 命令提供了额外的安全性和轻松恢复。

I don't know much about branching models in VCS other than Git. I'd say that in any DVCS you can implement branching by cloning (you create a branch by doing a clone). Mercurial so-called "named branches" are (from what I understand it) in fact commit labels only interpreted as a branch, sometimes requiring local numbering of revisions to resolve ambiguity. Mercurial "bookmarks" ar, I think, quite similar to Git branches. The two DVCS that have very different concept of branching are Monotone and Darcs. I think that "branching by copying" that Subversion uses, where separation between project name and branch name is by convention, is a wrong idea.


In Git revisions form a directed acyclic graph (DAG) of commits. It is directed, because commits have parents. That is a very important issue: edges in DAG of commits are from commit to its parent (or, in the case of merge commit, two or more its parents). Graph of commits is acyclic, which means that there is no chain (no path) that begins and ends with the same object.

Git glossary defines "branch" as an active line of development. This idea is behind an implementation of branches in Git.

The most recent commit on a branch is referred to as the tip of that branch. The tip of the branch is referenced by a branch head, which is just a symbolic name for this commit. In its "loose" form such branch head (for example for branch named 'master') is just a file somewhere in refs/heads/ directory inside git repository (inside .git dir), which contains reference to current tip of a branch: its SHA-1 identifier of commit (as hexadecimal string).

When you create a new commit in Git, the tip of currently checked out branch moves forward. In other words the new commit is created on top of tip of current branch, and branch head advances to the new commit (somewhat similarly to how the pointer to top of stack might advance).

A single git repository can track an arbitrary number of branches, but your working tree (if you have any) is associated with just one of them (the "current" or "checked out" branch). Current branch is given by the HEAD pointer. HEAD is (usually) pointer to currently checked out branch (to a branch head name), just like branch heads are pointers to tips of branches.

For example if currently checked out branch is 'master', then .git/HEAD file (representing HEAD) would contain single LF terminated line with ref: refs/heads/master (a symbolic reference to refs/heads/master), and .git/refs/heads/master (head of 'master' branch) would contain for example LF terminated line 0b127cb8ab975e43398a2b449563ccb78c437255, whihc is SHA-1 identifier to tip of 'master' branch (that is if current branch is not "packed": then you have to take a look at .git/packed-refs).

Some commands in Git, such as "git commit" or "git reset" manipulate / change branch head; other such as "git checkout" manipulate / change HEAD (symbolic reference to current branch).

"git log branch" command shows all commit reachable from branch tip, which means tip of branch, its parent, parent (or parents) of that parent commit etc. It shows part of a DAG of commits.

In Git deleting a branch means simply removing a branch head. That might mean that some commits become "invisible", unreachable freom refs (branches and tags), which means that at some time those commits could get garbage collected and removed from repository. But if you can delete branch with "git branch -d <branchname>" then that means that no commits would be lost; you can force branch deletion with "git branch -D <branchname>". Renaming a branch is simply a matter of renaming branch head, a symbolic reference (symbolic name) of branch tip; branch names are not saved anywhere in the commit object.


Git has also concept of reflogs, which is a local history of where branch tip pointed (and when). For example if you amend a commit with "git commit --amend", branch tip would get replaced with amended commit, and HEAD^ would be parent of commit before and after amending, while there would be entry in reflog for version before amending and after amending. If you rewind history using "git reset", reflog would contain information of old branch tip before rewinding.

In short reflog gives additional safety and easy recovery to git commands.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文