git branch, fork, fetch, merge, rebase和clone之间有什么区别?

我想了解Git中的分支、fork和克隆之间的区别?

类似地,当我执行git fetch而不是git pull时,这意味着什么?

同样,与merge相比,rebase意味着什么?

我怎样才能把自己压在一起?

它们是如何使用的,为什么使用,它们代表什么?

GitHub是怎么回事?

173818 次浏览

克隆只是存储库的副本。从表面上看,它的结果相当于svn checkout,在那里您可以从其他存储库下载源代码。像Subversion这样的集中式VCS和像Git这样的DVCSs之间的区别在于,在Git中,当你克隆时,你实际上复制了整个源存储库,包括所有的历史和分支。现在,您的机器上有了一个新的存储库,您所做的任何提交都将进入该存储库。没有人会看到任何更改,直到您将这些提交推到另一个存储库(或原始存储库),或者直到有人从您的存储库中提取提交(如果它是公共可访问的)。

分支是存储库中的东西。从概念上讲,它代表了一个发展的主线。你通常有一个主分支,但你也可能有一个分支,在那里你正在处理一些特性xyz,另一个是修复bug abc。当您签出一个分支时,您所做的任何提交都将保留在该分支上,而不会与其他分支共享,直到您将它们与相关分支合并或将它们重基到相关分支上。当然,Git在涉及到分支时似乎有点奇怪,直到您了解如何实现分支的底层模型。我不想自己解释(我认为我已经说得太多了),我将链接到Git如何建模分支和提交的“计算机科学”解释,摘自Git网站:

http://eagain.net/articles/git-for-computer-scientists/

分叉实际上并不是一个Git概念,它更像是一个政治/社会理念。也就是说,如果有些人对项目的进展方式不满意,他们可以独立于原始开发人员,自行获取源代码。这将被认为是一个分叉。Git使得分叉变得很容易,因为每个人都已经有了自己的源代码“主”副本,所以它就像切断与原始项目开发人员的联系一样简单,不需要像使用SVN那样从共享存储库导出历史记录。

编辑:因为我不知道像GitHub这样的网站所使用的“fork”的现代定义,请看看我下面的评论和Michael Durrant的回答是以获取更多信息。

Git

这个答案包括GitHub,因为许多人也问过这个问题。

局部存储库

Git(本地)有一个目录(.git),你提交你的文件,这是你的“本地存储库”。这与SVN等系统不同,在SVN中,您可以立即添加并提交到远程存储库。

Git通过保存整个文件来存储更改的文件的每个版本。在这方面,它也不同于SVN,因为您可以访问任何单独的版本,而无需通过增量更改来“重新创建”它。

Git根本不“锁定”文件,因此避免了编辑时的“独占锁定”功能(我想到了像pvcs这样的旧系统),所以所有文件都可以编辑,即使是在脱机时。它实际上做了一个惊人的工作,在拉取/推到远程存储库(如GitHub)期间合并文件更改(在同一个文件中!)。唯一需要手动更改(实际编辑文件)的情况是,如果两个更改涉及同一行代码。


分支机构

分支允许你保存主代码(“主”分支),复制(一个新分支),然后在那个新分支中工作。如果工作需要一段时间,或者自创建分支以来主分支获得了大量更新,那么应该针对主分支进行合并或重基(通常是为了更好的历史记录和更容易解决冲突)。完成后,将在分支中所做的更改合并到主存储库中。许多组织对每一项工作都使用分支,无论它是一个特性、bug还是杂务项目。其他组织只在重大变更(如版本升级)时使用分支。

Fork:使用分支时,您可以控制和管理分支,而使用Fork时,其他人可以控制接受代码。

一般来说,有两种主要的方法来做分支。第一种方法是将大多数更改保留在主分支上,只在更大、运行时间更长的事情上使用分支,比如您希望有两个分支可用于不同需求的版本更改。第二种方法是为每个特性请求、bug修复或杂务创建一个分支,然后手动决定何时将这些分支合并到主分支中。虽然这听起来很乏味,但这是一种常见的方法,也是我目前正在使用和推荐的方法,因为这使主分支更干净,而且我们将主分支推广到生产环境中,因此我们只需要通过重基和合并分支来完成、测试代码。

将分支“导入”给master的标准方法是执行merge。分支也可以“重基”;“清理”历史。它不会影响当前状态,这样做是为了提供一个“更干净”的历史记录。

基本上,这个想法是您从某个点(通常是从master)分支。因为你分支了,'master'本身已经从那个分支点向前移动了。如果你在分支中所做的所有更改都与当前master状态的所有最新更改相对应,那么它将更“干净”(更容易解决问题,历史也更容易理解)。因此,这个过程是:保存更改;获取'new' master,然后重新应用(这是rebase部分)更改。请注意,就像merge一样,rebase可能会导致必须手动解决的冲突(即编辑和修复)。

一个需要注意的指导原则:
. 只有当分支是本地的,并且您还没有将其推到远程时,才会重新赋值!
这主要是因为重基可以改变其他人看到的历史,其中可能包括他们自己的提交

跟踪分支

这些分支被命名为origin/branch_name(而不是branch_name)。当您将代码从远程存储库中推入或拉出时,这实际上是发生这种情况的机制。例如,当你git push一个名为building_groups的分支时,你的分支首先去到origin/building_groups,然后去到远程存储库。类似地,如果执行git fetch building_groups,检索到的文件将放在origin/building_groups分支中。然后,您可以选择将该分支合并到本地副本中。我们的实践是始终执行git fetch和手动合并,而不仅仅是git pull(它在一步中完成上述两项)。

获取新的分支。

获得新分支:在克隆的初始点,你将拥有所有的分支。然而,如果其他开发人员添加分支并将它们推送到远程,则需要一种方法来“了解”这些分支及其名称,以便能够在本地将它们拉下来。这是通过git fetch完成的,它将使用跟踪分支(例如,origin/)将所有新的和更改的分支获取到本地存储库。一旦fetched,可以git branch --remote列出跟踪分支,git checkout [branch]实际切换到任何给定的分支。

合并

合并是将来自不同分支或同一分支的不同版本的代码更改组合在一起的过程(例如当本地分支和远程分支不同步时)。如果已经在分支中开发了工作,并且该工作已经完成、准备就绪并经过测试,则可以将其合并到master分支中。这是通过git checkout master切换到master分支完成的,然后是git merge your_branch。合并将把所有不同的文件和即使是对同一文件的不同更改放在一起。这意味着它将实际更改文件中的代码以合并所有更改。

在执行mastercheckout时,还建议执行git pull origin master以获得合并到本地主服务器中的远程主服务器的最新版本。如果远程主机改变了,即moved forward,你将看到在git pull期间反映的信息。如果是这种情况(master被更改),建议你git checkout your_branch,然后rebase它到master,这样你的更改实际上会在“新的”master上“重放”。然后你将继续掌握最新的master,如下一段所示。

如果没有冲突,那么master将添加新的更改。如果存在冲突,这意味着相同的文件对类似的代码行有更改,无法自动合并。在这种情况下,git merge new_branch将报告有冲突需要解决。你可以通过编辑文件来“解决”这些问题(文件中会有两项更改),选择你想要的更改,删除你不想要的更改行,然后保存文件。更改用分隔符标记,如========<<<<<<<<

一旦你解决了任何冲突,你将再次git addgit commit这些更改来继续合并(你将在这个过程中从git获得反馈来指导你)。

当这个过程不能很好地工作时,你会发现git merge --abort非常方便地重置东西。

交互式重基和压缩/重新排序/删除提交

如果你已经完成了很多小步骤的工作,例如,你每天都将代码作为“半成品”提交,你可能想要将这些小的提交“压缩”成几个更大的提交。当您希望与同事一起进行代码审查时,这可能特别有用。你不想重放你所采取的所有“步骤”(通过提交),你只想说这里是我在一次提交中对此工作的所有更改的最终效果(diff)。

在考虑是否这样做时,要评估的关键因素是多个提交是针对同一个文件还是针对多个文件(在这种情况下,最好压缩提交)。这是用交互式重基工具完成的。这个工具允许你压缩提交、删除提交、改写消息等。例如,git rebase -i HEAD~10 (注意:这是__ABC1,而不是-)会显示以下内容:

interactive rebasein Git

但是要小心,小心地使用这个工具。每次执行一次压缩/删除/重新排序,退出并保存提交,然后重新进入工具。如果提交不是连续的,你可以重新排序它们(然后根据需要进行压缩)。实际上,你也可以在这里删除提交,但你真的需要确定你在做什么!

在Git存储库中有两种主要的协作方法。第一种,如上所述,是直接通过人们拉或推的分支。这些协作者在远程存储库中注册了他们的SSH密钥。这将允许他们直接推送到该存储库。缺点是您必须维护用户列表。 另一种方法——分叉——允许任何人“分叉”存储库,基本上是在他们自己的Git存储库帐户中创建一个本地副本。然后他们可以进行更改,并在完成后发送“拉请求”(实际上更多的是来自他们的“推”和实际存储库维护者的“拉”请求)以让代码被接受

第二种方法使用fork, 要求某人维护存储库的用户列表。


GitHub

GitHub(一个远程存储库)是一个远程源,如果你有(或被添加到)这样的存储库,你通常会推送和拉那些提交的更改,所以本地和远程实际上是非常不同的。另一种考虑远程存储库的方式是,它是位于远程服务器上的.git目录结构。

当你'fork' -在GitHub web浏览器GUI中,你可以点击这个按钮Image of fork button -你在你的 GitHub帐户中创建了一个代码的副本('克隆')。第一次这样做的时候可能会有点微妙,所以要确保你看看代码库是在谁的存储库下面列出的——要么是原始所有者,要么是“从谁派生出来的”,例如,像这样:

 forked repository name的图片

一旦有了本地副本,就可以按照自己的意愿进行更改(通过将它们拉到本地机器)。当你完成后,你提交一个“拉请求”到原来的存储库所有者/管理员(听起来很花哨,但实际上你只需要点击这个:图片的拉请求按钮),他们'拉'它。

对于一起编写代码的团队来说,更常见的是“克隆”存储库(单击存储库主屏幕上的“复制”图标)。然后,在本地键入git clone 并粘贴。这将在本地设置你,你也可以推拉到(共享)GitHub位置。

克隆

正如GitHub部分所指出的,克隆是存储库的副本。当你有一个远程存储库时,你对它的URL发出git clone命令,然后你就得到了存储库的本地副本或克隆。这个克隆有一切,文件,主分支,其他分支,所有现有的提交,整个shebang。你添加和提交的就是这个克隆,然后你将这些提交推到远程存储库本身。正是这种本地/远程概念使Git(以及类似于它的系统,如Mercurial)成为一个DVCS (分布式版本控制系统),而不是更传统的CVSs(代码版本控制系统),如SVN、PVCS、CVS等,在这些系统中,您可以直接提交到远程存储库。

可视化

核心概念的可视化可以在
中看到 http://marklodato.github.com/visual-git-guide/index-en.html
http://ndpsoftware.com/git-cheatsheet.html#loc=index < / p >

如果你想要一个可视化的显示变化是如何工作的,你不能用一个GUI来代替可视化工具gitg (macOS的gitx),我称之为“地铁地图”(尤其是伦敦地铁),它非常适合显示谁做了什么,事情是如何变化的,发散和合并等。

您还可以使用它来添加、提交和管理您的更改!

 gitg/gitx接口的图像

尽管gitg/gitx相当少,但GUI工具的数量在不断增加。许多Mac用户使用brother - bard的gitx分支,对于Linux来说,智能git是一个很好的选择,它具有直观而强大的界面:

Image of smart-git GUI

请注意,即使使用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 '

AND我有以下“git别名”;在我的~/.gitconfig文件-为什么有这些 因此分支补全(使用TAB键)有效!

这些是:

[alias]
co = checkout
cob = checkout -b

示例用法:git co [branch] <-分支的制表符补全将工作。

GUI学习工具

你可能会发现https://learngitbranching.js.org/在学习一些基本概念时很有用。屏幕截图:enter image description here
视频:https://youtu.be/23JqqcLPss0 < / p >

最后,7个关键的救星!

  1. 你做了改变,添加并提交它们(但不要推送),然后哦!你知道你是主人吧!

     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 !
    
  2. 当你在本地分支中工作时,你弄乱了一些文件,只是想回到你上次执行git pull时的文件:

     git reset --hard origin/master  # You will need to be comfortable doing this!
    
  3. 你开始在本地进行修改,你编辑了半打文件,然后,哦,糟糕,你仍然在主(或其他)分支:

     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
    
  4. 你在当前分支中搞砸了一个特定的文件,并希望基本上“重置”该文件(丢失更改)到上次你从远程存储库中取出它的样子:

     git checkout your/directories/filename
    

    这实际上重置了文件(像许多Git命令一样,它在这里所做的事情并没有很好地命名)。

  5. 你在本地做了一些更改,你想要确保当你做git resetrebase时不会丢失它们:当我不确定我是否会在Git中搞砸或丢失重要的更改时,我经常手动复制整个项目(cp -r ../my_project ~/)。

  6. 你正在重基,但事情变得一团糟:

     git rebase --abort # To abandon interactive rebase and merge issues
    
  7. 将你的Git分支添加到你的PS1提示符(参见https://unix.stackexchange.com/a/127800/10043),例如:

    提示图像

    分支是selenium_rspec_conversion

以下是奥利弗·斯蒂尔(Oliver Steele)描绘的这一切是如何结合在一起的:

enter image description here

只是补充一下,关于分叉的特别说明。

从技术上讲,克隆回购和分叉回购是同一件事,认识到这一点很好。做的事:

git clone $some_other_repo

你可以拍拍自己的背——你刚刚完成了另一个回购。

Git,作为一个VCS,实际上都是关于克隆分叉的。除了使用远程UI(如cgit)“只是浏览”之外,git回购几乎没有什么不涉及分叉在某些时候克隆回购的事情。

然而,

  • 当有人说我分叉了X回购,他们的意思是他们已经创建 一个复制的回购在其他地方的意图暴露它 另一些,例如展示一些实验,或应用不同 访问控制机制(例如;允许没有Github的人访问但是

    .与公司内部账户合作)

    事实:回购最有可能是由其他命令创建的 git clone,它最有可能托管在服务器上的某个地方 而不是某人的笔记本电脑,而且很可能略有不同 格式(这是一个“裸回购”,即。没有工作树)都是公正的 技术细节。< / p > 它很可能包含不同的分支集, 标签或提交最有可能是他们在第一个中这样做的原因 的地方。< / p >

    (当你点击“fork”时,Github所做的只是克隆添加 Sugar:它为你复制了回购,把它放在你的账户下,记录 将“叉从”某地,添加远程命名为“上游”,并最 重要的是,播放漂亮的动画)

  • 当有人说我克隆了repo X时,他们的意思是他们已经创建了 一个克隆的回购本地笔记本电脑或桌面上的意图 研究它,玩它,贡献它,或者从源头上构建一些东西

Git的美妙之处在于它使这一切完美地结合在一起:所有这些回购共享提交链的公共部分,因此可以在你认为合适的时候在所有这些回购之间安全地(见下面的注释)合并更改。


注意:“安全”,只要你不重写链的公共部分,只要变化不冲突。

Fork Vs. Clone——这两个词的意思都是复制

请参阅此图。 (原自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')

  • 一个拷贝到你的远程回购(云),链接到乔的
  • 一个副本,然后你可以克隆到你的本地回购和F*%$ up
  • 当你完成后,你可以回到你的遥控器
  • 然后,您可以通过单击pull-request来询问Joe是否希望在他的项目中使用它

克隆

  • 拷贝到本地回购(硬盘)