我想了解 Git 中分支、叉子和克隆之间的区别?
同样,当我执行 git fetch
而不是 git pull
时,这意味着什么?
另外,与 merge
相比,rebase
是什么意思?
我怎样才能将个人的承诺挤在一起?
它们是如何使用的,为什么使用它们以及它们代表什么?
GitHub 是如何出现的?
吉特
这个答案包括 GitHub,因为很多人也问过这个问题。
本地存储库
Git(本地)有一个目录(.git
),您可以将文件提交到该目录,这是您的“本地存储库”。这与 SVN 等系统不同,您可以立即添加并提交到远程存储库。
Git 通过保存整个文件来存储文件的每个版本。在这方面它也与 SVN 不同,因为您可以转到任何单独的版本,而无需通过 delta 更改“重新创建”它。
Git 根本不“锁定”文件,因此避免了编辑的“排他锁定”功能(想到像 pvcs 这样的旧系统),因此即使离线也可以随时编辑所有文件。实际上,它在将文件更改(在同一个文件中!)合并到一个远程存储库(如 GitHub)期间完成了一项了不起的工作。唯一需要手动更改(实际上是编辑文件)的情况是两个更改涉及相同的代码行。
分支机构
分支允许您保留主要代码(“主”分支),制作副本(新分支),然后在该新分支中工作。如果工作需要一段时间,或者 master 在创建分支后得到了很多更新,那么应该针对 master 分支进行合并或变基(通常是更好的历史记录和更容易解决冲突的首选)。完成后,将分支中所做的更改合并回主存储库。许多组织对每项工作都使用分支,无论是功能、错误还是杂项。其他组织仅使用分支进行重大更改,例如版本升级。
Fork:使用分支,您可以控制和管理分支,而使用 fork,其他人可以控制接受代码返回。
一般来说,有两种主要的方法来做分支。第一个是将大多数更改保留在主分支上,仅将分支用于更大且运行时间更长的事情,例如版本更改,您希望有两个分支可用于不同的需求。第二个是您基本上为每个功能请求、错误修复或杂务创建一个分支,然后手动决定何时将这些分支实际合并到主主分支中。虽然这听起来很乏味,但这是一种常见的方法,也是我目前使用和推荐的方法,因为这可以保持主分支更清洁,并且它是我们提升到生产环境的主分支,所以我们只需要通过变基和合并分支。
将分支“引入”到 master 的标准方法是执行 merge
。分支也可以“重新定位”以“清理”历史记录。它不会影响当前状态,并且这样做是为了提供“更清晰”的历史记录。
基本上,这个想法是您从某个点(通常从 master)分支。自从你分支以来,'master' 本身已经从那个分支点向前移动了。如果您在分支中所做的所有更改都针对 master 的当前状态及其所有最新更改进行播放,它将是“更干净”(更容易解决问题并且历史将更容易理解)。所以,过程是:保存更改;获取“新”主控,然后重新应用(这是变基部分)再次针对该更改进行更改。请注意,与合并一样,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
完成的,该 git fetch
将使用跟踪分支(例如 origin/
)将所有新的和更改的分支放入本地存储库。 fetch
编辑后,您可以git branch --remote
列出跟踪分支并git checkout [branch]
实际切换到任何给定的分支。
合并
合并是合并来自不同分支或同一分支的不同版本的代码更改的过程(例如,当本地分支和远程不同步时)。如果一个人在一个分支中开发了工作并且该工作已经完成、准备就绪并经过测试,那么它可以合并到 master
分支中。这由 git checkout master
完成以切换到 master
分支,然后是 git merge your_branch
。合并会将所有不同的文件和甚至对同一文件的不同更改结合在一起。这意味着它实际上会更改文件内的代码以合并所有更改。
在执行 master
的 checkout
时,还建议执行 git pull origin master
以将最新版本的远程 master 合并到本地 master。如果远程主机发生更改,即 moved forward
,您将看到在该 git pull
期间反映该情况的信息。如果是这种情况(master 已更改),建议您先git checkout your_branch
,然后将其 rebase
更改为 master,以便您的更改实际上在“新”master 之上“重播”。然后,您将继续使 master 保持最新,如下一段所示。
如果没有冲突,那么 master 将添加新的更改。如果有冲突,这意味着相同的文件在相似的代码行周围有更改,它不能自动合并。在这种情况下,git merge new_branch
将报告存在要解决的冲突。您可以通过编辑文件(其中包含两个更改)、选择您想要的更改、从字面上删除您不想要的更改的行然后保存文件来“解决”它们。更改用分隔符标记,例如 ========
和 <<<<<<<<
。
一旦您解决了任何冲突,您将再次 git add
和 git commit
这些更改以继续合并(您将在此过程中从 git 获得反馈以指导您)。
当该过程无法正常运行时,您会发现 git merge --abort
非常便于重置。
交互式变基和压缩/重新排序/删除提交
如果您已经完成了许多小步骤的工作,例如,您每天将代码提交为“正在进行的工作”,您可能希望将这些小提交“压缩”成几个较大的提交。当您想与同事进行代码审查时,这可能特别有用。您不想重播您采取的所有“步骤”(通过提交),您只想说这是我在一次提交中对这项工作所做的所有更改的最终效果(差异)。
在考虑是否这样做时要评估的关键因素是多个提交是否针对同一个文件或多个文件(在这种情况下最好压缩提交)。这是通过交互式变基工具完成的。此工具可让您压缩提交、删除提交、改写消息等。例如,git rebase -i HEAD~10
(注意:这是 ~
,而不是 -
)会显示以下内容:
https://i.stack.imgur.com/QU4yP.png
不过要小心并“小心翼翼”地使用此工具。一次执行一个 squash/delete/reorder,退出并保存该提交,然后重新进入该工具。如果提交不连续,您可以重新排序它们(然后根据需要压缩)。您实际上也可以在这里删除提交,但是您确实需要确定在执行此操作时正在做什么!
叉子
Git 存储库中有两种主要的协作方法。上面详述的第一个是直接通过人们拉出/推入的分支。这些协作者的 SSH 密钥已注册到远程存储库。这将让他们直接推送到该存储库。缺点是您必须维护用户列表。另一种方法 - 分叉 - 允许任何人“分叉”存储库,基本上是在他们自己的 Git 存储库帐户中制作本地副本。然后他们可以进行更改,并在完成后发送“拉取请求”(实际上,这更像是来自他们的“推送”和对实际存储库维护者的“拉取”请求)以使代码被接受。
第二种方法使用分叉,不需要有人维护存储库的用户列表。
GitHub
GitHub(远程存储库)是一个远程源,如果您拥有(或被添加到)这样的存储库,您通常会将这些已提交的更改推送和拉取,因此本地和远程实际上是完全不同的。考虑远程存储库的另一种方式是,它是一个存在于远程服务器上的 .git
目录结构。
https://i.stack.imgur.com/8Z8IT.png
https://i.stack.imgur.com/UhaOB.png
https://i.stack.imgur.com/XTHBB.png
对于一起处理代码的团队来说,更常见的是“克隆”存储库(单击存储库主屏幕上的“复制”图标)。然后,在本地键入 git clone
并粘贴。这将在本地设置您,您还可以推送和拉到(共享)GitHub 位置。
克隆
如 GitHub 上的部分所述,克隆是存储库的副本。当您拥有远程存储库时,您可以针对其 URL 发出 git clone
命令,然后您将获得该存储库的本地副本或克隆。这个克隆有一切、文件、master 分支、其他分支、所有现有的提交、整个 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
如果您想要直观地显示更改的工作方式,您无法使用可视化工具 gitg
(macOS 的 gitx
)与我称之为“地铁地图”(尤其是伦敦地铁)的 GUI 相比,非常棒用于显示谁做了什么,事情如何变化,分歧和合并等。
您还可以使用它来添加、提交和管理您的更改!
https://i.stack.imgur.com/hDErT.png
尽管 gitg/gitx 相当少,但 GUI 工具的数量仍在不断增加。许多 Mac 用户使用 Brotherbard 的 gitx 分支,对于 Linux,一个不错的选择是 smart-git,它具有直观而强大的界面:
https://i.stack.imgur.com/Pos5H.png
请注意,即使使用 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://i.stack.imgur.com/j9ig2.png
最后,7个关键救星!
您进行更改,添加并提交它们(但不要推送),然后哦!你意识到你是大师! git reset [filename(s)] git checkout -b [name_for_a_new_branch] git add [file(s)] git commit -m “一条有用的信息”瞧!您已将该“主”提交移至其自己的分支!你在本地分支中工作时弄乱了一些文件,只是想回到你上次执行 git pull 时的内容: git reset --hard origin/master # 你需要很舒服地这样做!您开始在本地进行更改,编辑了六个文件,然后,哦,废话,您仍在主(或另一个)分支中: git checkout -b new_branch_name # 只需创建一个新分支 git add 。 # 添加更改文件 git commit -m"your message" # 并提交它们您在当前分支中弄乱了一个特定文件,并希望基本上“重置”该文件(丢失更改)到上次拉取时的样子它来自远程存储库: git checkout your/directories/filename 这实际上会重置文件(就像许多 Git 命令一样,它在这里所做的事情并没有很好地命名)。您在本地进行了一些更改,您想确保在执行 git reset 或 rebase 时不会丢失它们:我经常手动复制整个项目(cp -r ../my_project ~/)不确定我是否会在 Git 中搞砸或丢失重要的更改。您正在变基,但事情变得一团糟: git rebase --abort # 要放弃交互式变基和合并问题将您的 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's answer 以获取更多信息。
这是奥利弗·斯蒂尔 (Oliver Steele) 对这一切如何组合在一起的形象:
https://i.stack.imgur.com/XwVzT.png
叉子VS。克隆 - 两个都表示复制的词
请参阅此diagram. (最初来自 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')
叉子
将其链接到 Joe 的远程仓库(云)的副本
然后您可以克隆到您的本地仓库和 F*%$-up 的副本
完成后,您可以推回遥控器
然后,您可以通过单击 pull-request 来询问 Joe 他是否想在他的项目中使用它
克隆
复制到本地仓库(硬盘)
anidea
推送到他的 repo 并为您节省保持 fork 最新的琐事。 OTOH,如果您无法与 Joe 达成协议,您可以继续开发和使用您的 fork(看看您是否可以让他稍后改变主意)。
只是为了向其他人添加一个特定于分叉的注释。
很高兴意识到从技术上讲,克隆 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 之间来回合并更改,只要你认为合适。
注意:“安全”,只要您不重写链的公共部分,并且只要更改不冲突。