我想了解 Git 中分支、叉子和克隆之间的区别?
同样,当我做 agit fetch
而不是 a时,这意味着什么git pull
?
rebase
另外,与 相比是什么意思merge
?
我怎样才能将个人的承诺挤在一起?
它们是如何使用的,为什么使用它们以及它们代表什么?
GitHub 是如何出现的?
这个答案包括 GitHub,因为很多人也问过这个问题。
Git(本地)有一个目录(.git
),您可以将文件提交到该目录,这是您的“本地存储库”。这与 SVN 等系统不同,您可以立即添加并提交到远程存储库。
Git 通过保存整个文件来存储文件的每个版本。在这方面它也与 SVN 不同,因为您可以转到任何单独的版本,而无需通过 delta 更改“重新创建”它。
Git 根本不“锁定”文件,因此避免了编辑的“独占锁定”功能(想到像 pvcs 这样的旧系统),因此即使离线也可以随时编辑所有文件。实际上,它在将文件更改(在同一个文件中!)合并到一个远程存储库(如 GitHub)期间完成了一项了不起的工作。唯一需要手动更改(实际上是编辑文件)的情况是两个更改涉及相同的代码行。
分支允许您保留主要代码(“主”分支),制作副本(新分支),然后在该新分支中工作。如果工作需要一段时间,或者 master 在创建分支后得到了很多更新,那么应该针对 master 分支进行合并或变基(通常是更好的历史记录和更容易解决冲突的首选)。完成后,将分支中所做的更改合并回主存储库。许多组织对每项工作都使用分支,无论是功能、错误还是杂项。其他组织仅使用分支进行重大更改,例如版本升级。
Fork:使用分支,您可以控制和管理分支,而使用 fork,其他人可以控制接受代码返回。
一般来说,有两种主要的方法来做分支。第一个是将大多数更改保留在主分支上,仅将分支用于更大且运行时间更长的事情,例如版本更改,您希望有两个分支可用于不同的需求。第二个是您基本上为每个功能请求、错误修复或杂务创建一个分支,然后手动决定何时将这些分支实际合并到主主分支中。虽然这听起来很乏味,但这是一种常见的方法,也是我目前使用和推荐的方法,因为这可以保持主分支更清洁,并且它是我们提升到生产环境的主分支,所以我们只需要通过变基和合并分支。
将分支“引入”到 master 的标准方法是执行merge
. 分支也可以“重新定位”以“清理”历史记录。它不会影响当前状态,并且这样做是为了提供“更清晰”的历史记录。
基本上,这个想法是您从某个点(通常从 master)分支。自从你分支以来,'master' 本身已经从那个分支点向前移动了。如果您在分支中所做的所有更改都针对 master 的当前状态及其所有最新更改进行播放,它将是“更干净”(更容易解决问题并且历史将更容易理解)。所以,过程是:保存更改;获取“新”主控,然后重新应用(这是变基部分)再次针对该更改进行更改。请注意,与合并一样,rebase 可能会导致您必须手动解决(即编辑和修复)的冲突。
需要注意的一条准则:
仅当分支是本地的并且您尚未将其推送到远程时才重新设置基准!
这主要是因为变基可以改变其他人看到的历史,其中可能包括他们自己的提交。
这些是被命名的分支origin/branch_name
(而不是 just branch_name
)。当您向远程存储库推送和拉取代码时,这实际上是发生这种情况的机制。例如,当您git push
有一个名为的分支时building_groups
,您的分支首先origin/building_groups
会转到远程存储库,然后再转到远程存储库。同样,如果您执行 a git fetch building_groups
,则检索到的文件将放置在您的origin/building_groups
分支中。然后,您可以选择将此分支合并到您的本地副本中。我们的做法是始终进行 agit fetch
和手动合并,而不仅仅是 a git pull
(在一个步骤中完成上述两项)。
获取新分支:在克隆的初始点,您将拥有所有分支。但是,如果其他开发人员添加分支并将它们推送到远程,则需要有一种方法来“了解”这些分支及其名称,以便能够在本地将它们拉下来。这是通过git fetch
使用跟踪分支(例如,)将所有新的和更改的分支获取到本地存储库中来完成的origin/
。fetch
编辑后,可以git branch --remote
列出跟踪分支并git checkout [branch]
实际切换到任何给定的分支。
合并是合并来自不同分支或同一分支的不同版本的代码更改的过程(例如,当本地分支和远程不同步时)。如果一个人在一个分支中开发了工作并且该工作已经完成、准备好并经过测试,那么它可以合并到master
分支中。这是通过git checkout master
切换到master
分支,然后git merge your_branch
。合并会将所有不同的文件甚至对同一文件的不同更改合并在一起。这意味着它实际上会更改文件内的代码以合并所有更改。
在执行checkout
时master
,还建议您将git pull origin master
最新版本的远程主机合并到您的本地主机中。如果远程主机发生了变化,即 ,moved forward
您将看到在此期间反映的信息git pull
。如果是这种情况(主已更改),建议您git checkout your_branch
进行rebase
主控,以便您的更改实际上在“新”主控之上“重播”。然后,您将继续使 master 保持最新,如下一段所示。
如果没有冲突,那么 master 将添加新的更改。如果有冲突,这意味着相同的文件在相似的代码行周围有更改,它不能自动合并。在这种情况下git merge new_branch
,将报告有冲突需要解决。您可以通过编辑文件(其中包含两个更改)、选择您想要的更改、从字面上删除您不想要的更改的行然后保存文件来“解决”它们。更改用 和 等分隔符========
标记<<<<<<<<
。
一旦您解决了任何冲突,您将再次git add
和git commit
这些更改继续合并(在此过程中您将获得来自 git 的反馈以指导您)。
当该过程无法正常工作时,您会发现git merge --abort
重置内容非常方便。
如果您已经完成了许多小步骤的工作,例如,您每天将代码提交为“正在进行的工作”,您可能希望将这些小提交“压缩”成几个较大的提交。当您想与同事进行代码审查时,这可能特别有用。您不想重播您采取的所有“步骤”(通过提交),您只想说这是我在一次提交中对这项工作所做的所有更改的最终效果(差异)。
在考虑是否这样做时要评估的关键因素是多个提交是否针对同一个文件或多个文件(在这种情况下最好压缩提交)。这是通过交互式变基工具完成的。这个工具可以让你压缩提交、删除提交、改写消息等。例如,git rebase -i HEAD~10
(注意:那是 a ~
,而不是 a-
)会显示以下内容:
不过要小心并“小心翼翼”地使用此工具。一次执行一个 squash/delete/reorder,退出并保存该提交,然后重新进入该工具。如果提交不连续,您可以重新排序它们(然后根据需要压缩)。您实际上也可以在这里删除提交,但是您确实需要确定在执行此操作时正在做什么!
Git 存储库中有两种主要的协作方法。上面详述的第一个是直接通过人们拉出/推入的分支。这些协作者的 SSH 密钥已注册到远程存储库。这将让他们直接推送到该存储库。缺点是您必须维护用户列表。另一种方法 - 分叉 - 允许任何人“分叉”存储库,基本上是在他们自己的 Git 存储库帐户中制作本地副本。然后他们可以进行更改,并在完成后发送“拉取请求”(实际上,这更像是来自他们的“推送”和对实际存储库维护者的“拉取”请求)以使代码被接受。
第二种方法使用分叉,不需要有人维护存储库的用户列表。
GitHub(远程存储库)是一个远程源,如果您拥有(或被添加到)这样的存储库,您通常会将这些已提交的更改推送和拉取,因此本地和远程实际上是完全不同的。考虑远程存储库的另一种方式是,它是.git
位于远程服务器上的目录结构。
当您“分叉”时——在 GitHub Web 浏览器 GUI 中,您可以单击此按钮——您在GitHub 帐户中创建代码的副本(“克隆”) 。第一次这样做可能会有点微妙,所以请务必查看代码库在谁的存储库下列出 - 原始所有者或“派生自”以及您,例如,像这样:
获得本地副本后,您可以根据需要进行更改(通过将它们拉到本地机器上)。完成后,您向原始存储库所有者/管理员提交一个“拉取请求”(听起来很花哨,但实际上您只需单击它:),然后他们将其“拉入”。
对于一起处理代码的团队来说,更常见的是“克隆”存储库(单击存储库主屏幕上的“复制”图标)。然后,在本地键入git clone
并粘贴。这将在本地设置您,您还可以推送和拉到(共享)GitHub 位置。
如 GitHub 上的部分所述,克隆是存储库的副本。当您拥有远程存储库时,您可以git clone
针对其 URL 发出命令,然后您最终会获得该存储库的本地副本或克隆。这个克隆有一切,文件,主分支,其他分支,所有现有的提交,整个 shebang。您对这个克隆进行添加和提交,然后远程存储库本身就是您将这些提交推送到的内容。正是这种本地/远程概念使 Git(以及与其类似的系统,例如 Mercurial)成为 DVCS(分布式版本控制系统),而不是更传统的 CVS(代码版本控制系统),例如 SVN、PVCS、CVS 等。您直接提交到远程存储库。
核心概念的可视化可见
http://marklodato.github.com/visual-git-guide/index-en.html和
http://ndpsoftware.com/git-cheatsheet.html#loc=index
如果您想要直观地显示更改是如何工作的,您无法使用我称之为“地铁地图”(尤其是伦敦地铁)的 GUI 来击败可视化工具gitg
(gitx
对于 macOS),它非常适合显示谁做了什么,事物如何变化、分化和合并等。
您还可以使用它来添加、提交和管理您的更改!
尽管 gitg/gitx 相当少,但 GUI 工具的数量仍在不断增加。许多 Mac 用户使用 Brotherbard 的 gitx 分支,对于 Linux,一个不错的选择是 smart-git,它具有直观而强大的界面:
请注意,即使使用 GUI 工具,您也可能会在命令行中执行大量命令。
为此,我的文件中有以下别名~/.bash_aliases
(从我的~/.bashrc
文件中为每个终端会话调用):
# git
alias g='git status'
alias gcob='git checkout -b '
alias gcom='git checkout master'
alias gd='git diff'
alias gf='git fetch'
alias gfrm='git fetch; git reset --hard origin/master'
alias gg='git grep '
alias gits='alias | grep "^alias g.*git.*$"'
alias gl='git log'
alias gl1='git log --oneline'
alias glf='git log --name-status'
alias glp='git log -p'
alias gpull='git pull '
alias gpush='git push '
而且我的~/.gitconfig
文件中有以下“git 别名” - 为什么有这些?
这样分支完成(使用 TAB 键)就可以了!
所以这些是:
[alias]
co = checkout
cob = checkout -b
示例用法:git co [branch]
<- 分支的选项卡完成将起作用。
您可能会发现https://learngitbranching.js.org/对学习一些基本概念很有用。截图:
视频:https ://youtu.be/23JqqcLPss0
您进行更改,添加并提交它们(但不要推送),然后哦!你意识到你是大师!
git reset [filename(s)]
git checkout -b [name_for_a_new_branch]
git add [file(s)]
git commit -m "A useful message"
Voila! You've moved that 'master' commit to its own branch !
您在本地分支中工作时弄乱了一些文件,只是想回到上次执行以下操作时的内容git pull
:
git reset --hard origin/master # You will need to be comfortable doing this!
您开始在本地进行更改,编辑了六个文件,然后,哦,废话,您仍在主(或另一个)分支中:
git checkout -b new_branch_name # just create a new branch
git add . # add the changes files
git commit -m"your message" # and commit them
您弄乱了当前分支中的一个特定文件,并希望基本上“重置”该文件(丢失更改)到您最后一次从远程存储库中提取它的方式:
git checkout your/directories/filename
这实际上会重置文件(就像许多 Git 命令一样,它在这里所做的事情并没有很好地命名)。
您在本地进行了一些更改,您想确保在执行git reset
or时不会丢失它们:当我不确定是否会在 Git 中搞砸或丢失重要信息时rebase
,我经常手动复制整个项目 ( )cp -r ../my_project ~/
变化。
你正在变基,但事情变得一团糟:
git rebase --abort # To abandon interactive rebase and merge issues
将您的 Git 分支添加到PS1
提示符中(请参阅https://unix.stackexchange.com/a/127800/10043),例如
分支是selenium_rspec_conversion
.
克隆只是存储库的副本。从表面上看,它的结果等同于svn checkout
从其他存储库下载源代码。像 Subversion 这样的集中式 VCS 和像 Git 这样的 DVCS 之间的区别在于,在 Git 中,当您克隆时,您实际上是在复制整个源存储库,包括所有历史记录和分支。现在,您的机器上有一个新的存储库,您所做的任何提交都会进入该存储库。在您将这些提交推送到另一个存储库(或原始存储库)或有人从您的存储库中提取提交(如果它是可公开访问的)之前,没有人会看到任何更改。
分支是存储库中的东西。从概念上讲,它代表了一条发展的线索。您通常有一个 master 分支,但您也可能有一个分支正在处理某些功能 xyz,以及另一个用于修复错误 abc 的分支。当您签出一个分支时,您所做的任何提交都将保留在该分支上,并且不会与其他分支共享,直到您将它们与相关分支合并或重新定位到相关分支上。当然,在您查看分支如何实现的底层模型之前,Git 在分支方面似乎有点奇怪。与其自己解释(我已经说得太多了,我想),我将链接到 Git 如何对分支和提交进行建模的“计算机科学”解释,取自 Git 网站:
http://eagain.net/articles/git-for-computer-scientists/
分叉实际上不是 Git 概念,它更像是一种政治/社会理念。也就是说,如果有些人对项目的进展方式不满意,他们可以获取源代码并自己独立于原始开发人员进行处理。那将被视为叉子。Git 使分叉变得容易,因为每个人都已经拥有自己的源代码“主”副本,因此它就像与原始项目开发人员切断联系一样简单,并且不需要像使用 SVN 那样从共享存储库中导出历史记录.
编辑:由于我不知道 GitHub 等网站使用的“fork”的现代定义,请查看评论以及Michael Durrant在我下面的回答以获取更多信息。
这是奥利弗·斯蒂尔 (Oliver Steele) 对这一切如何组合在一起的形象:
叉子VS。克隆 - 两个都表示复制的词
请看这张图。 (最初来自http://www.dataschool.io/content/images/2014/Mar/github1.png)。
.-------------------------. 1. Fork .-------------------------.
| Your GitHub repo | <-------------- | Joe's GitHub repo |
| github.com/you/coolgame | | github.com/joe/coolgame |
| ----------------------- | 7. Pull Request | ----------------------- |
| master -> c224ff7 | --------------> | master -> c224ff7 (c) |
| anidea -> 884faa1 (a) | | anidea -> 884faa1 (b) |
'-------------------------' '-------------------------'
| ^
| 2. Clone |
| |
| |
| |
| |
| | 6. Push (anidea => origin/anidea)
v |
.-------------------------.
| Your computer | 3. Create branch 'anidea'
| $HOME/coolgame |
| ----------------------- | 4. Update a file
| master -> c224ff7 |
| anidea -> 884faa1 | 5. Commit (to 'anidea')
'-------------------------'
(a) - after you have pushed it
(b) - after Joe has accepted it
(c) - eventually Joe might merge 'anidea' (make 'master -> 884faa1')
叉
克隆
只是为了向其他人添加一个特定于分叉的注释。
很高兴意识到从技术上讲,克隆 repo 和 fork repo 是一回事。做:
git clone $some_other_repo
你可以轻拍自己的背——你刚刚分叉了一些其他的回购。
Git,作为 VCS,实际上就是克隆分叉。除了使用诸如 cgit 之类的远程 UI “只是浏览”之外,与 git repo 几乎没有什么关系,它不涉及在某些时候分叉克隆 repo。
然而,
当有人说我分叉了 repo X时,他们的意思是他们已经在其他地方创建了 repo 的克隆,意图将其暴露给其他人,例如展示一些实验,或应用不同的访问控制机制(例如,允许没有Github 访问但使用公司内部帐户进行协作)。
事实是:repo 很可能是使用其他命令创建的
git clone
,它很可能托管在服务器上的某个位置而不是某人的笔记本电脑上,并且很可能格式略有不同(它是一个“裸 repo”,即没有工作树)都只是技术细节。
它很可能包含不同的分支、标签或提交集这一事实很可能是他们首先这样做的原因。
(当您单击“fork”时,Github 所做的只是添加了糖分进行克隆:它为您克隆 repo,将其放在您的帐户下,在某处记录“fork from”,添加名为“upstream”的远程,最重要的是,播放漂亮的动画。)
当有人说我克隆了 repo X时,他们的意思是他们已经在笔记本电脑或台式机上本地创建了 repo 的克隆,目的是研究、使用它、为它做出贡献或从其中的源代码构建一些东西。
Git 的美妙之处在于它使这一切完美地结合在一起:所有这些 repos 共享块提交链的公共部分,因此可以安全地(见下面的注释)在所有这些 repos 之间来回合并更改,只要你认为合适。
注意: “安全”,只要您不重写链的公共部分,并且只要更改不冲突。