我想了解Git中分支(branch)、复刻(fork)和克隆(clone)之间的区别?
类似地,当我执行git fetch
和git pull
时,它们有什么不同意义?
此外,在比较rebase
和merge
时,rebase
是什么意思?
如何将单独的提交压缩在一起?
它们如何被使用、为什么使用它们以及它们代表什么?
GitHub如何发挥作用?
我想了解Git中分支(branch)、复刻(fork)和克隆(clone)之间的区别?
类似地,当我执行git fetch
和git pull
时,它们有什么不同意义?
此外,在比较rebase
和merge
时,rebase
是什么意思?
如何将单独的提交压缩在一起?
它们如何被使用、为什么使用它们以及它们代表什么?
GitHub如何发挥作用?
本答案包括GitHub,因为很多人也问了这个。
Git(本地)有一个目录(.git
),您将文件提交到该目录中,这是您的“本地存储库”。这不同于像SVN这样的系统,您可以立即添加和提交到远程存储库。
Git通过保存整个文件来存储每个更改版本的文件。在这方面它与SVN不同,因为你可以去到任何单独的版本而无需通过增量更改“重新创建”它。
Git根本不会“锁定”文件,因此避免了编辑的“独占锁定”功能(考虑旧系统如pvcs),因此所有文件始终可以进行编辑,即使离线也是如此。它实际上在拉取或从GitHub等远程存储库获取/推送时,在同一文件中合并文件更改(!)。唯一需要手动更改(实际上是编辑文件)的时间是如果两个更改涉及相同的代码行。
分支允许您保存主代码('master' 分支),复制一份(新的分支)并在此新分支中进行工作。如果工作需要一段时间或自从创建分支后 'master' 分支有了很多更新,则应该进行合并或变基(通常更喜欢变基,因为可以获得更好的历史记录和更容易解决冲突)。完成后,您将合并在分支中所做的更改回到主存储库。许多组织针对每个工作单元(无论是一个功能、错误还是日常任务项)都使用分支。其他组织仅在进行重大更改(例如版本升级)时使用分支。
派生:使用分支时,您控制和管理分支,而使用派生时,其他人负责接受代码返回。
总的来说,有两种主要的分支方法。第一种是将大部分更改保留在主分支上,仅在需要不同版本的情况下使用分支,例如版本更改,这样您就可以拥有两个可用于不同需求的分支。第二种方法是基本上为每个功能请求、错误修复或琐事创建一个分支,然后手动决定何时将这些分支合并到主主分支中。虽然听起来很繁琐,但这是一种常见的方法,也是我目前使用和推荐的方法,因为这使主分支保持清洁,我们将其推广到生产,因此我们只希望通过重新基础和合并分支来获得已完成、经过测试的代码。这些分支的名称为origin/branch_name
(与仅使用branch_name
相对)。当您将代码推送和拉取到远程存储库时,实际上就是通过此机制进行的。例如,当您git push
一个名为building_groups
的分支时,您的分支首先进入origin/building_groups
,然后才会进入远程存储库。同样,如果您执行git fetch building_groups
,则检索到的文件将放置在您的origin/building_groups
分支中。然后,您可以选择将此分支合并到本地副本中。我们的惯例始终是进行git fetch
和手动合并,而不是只进行git pull
(它可以一次性完成上述两个步骤)。
获取新分支:在克隆的初始点,您将拥有所有分支。然而,如果其他开发者添加了分支并将其推送到远程,则需要一种方法来“知道”这些分支及其名称,以便能够将它们本地拉下来。这是通过使用跟踪分支(例如origin/
)执行 git fetch
来完成的,该命令将把所有新的和更改的分支获取到本地存储库中。一旦进行了fetch
,就可以使用 git branch --remote
列出跟踪分支,并使用 git checkout [branch]
实际切换到任何一个分支。
master
分支中。这可以通过git checkout master
切换到master
分支,然后使用git merge your_branch
完成。合并将把所有不同的文件和甚至是同一文件的不同更改合并在一起。这意味着它实际上会更改文件内部的代码以合并所有更改。
当执行 checkout
命令切换到 master
分支时,建议同时执行 git pull origin master
命令,以获取远程主分支的最新版本并合并到本地主分支中。如果远程主分支发生变化,即 向前推进
,你会在执行 git pull
时看到反映这种变化的信息。如果是这种情况(主分支已更改),建议先执行 git checkout your_branch
命令,然后将其与主分支 rebase
,以便你的更改实际上被 '重放' 在 '新' 主分支之上。然后,你可以按照下一段所示的方式继续更新主分支。
git merge new_branch
将报告需要解决的冲突。您可以通过编辑文件(其中包含两个更改)来“解决”它们,选择您想要的更改,删除您不想要的更改行,然后保存文件。更改使用分隔符标记,例如========
和<<<<<<<<
。git add
和 git commit
进行更改以继续合并(在此过程中,您将得到来自 git 的反馈以指导您)。git merge --abort
非常方便,可重置事物。GitHub(一个远程仓库)是一个远程源,通常情况下,您会将提交的更改推送到该仓库中,如果您有这样的仓库(或被添加到其中),那么本地和远程实际上是非常不同的。另一种思考远程仓库的方式是它是一个位于远程服务器上的 .git
目录结构。
当你“fork” - 在 GitHub 网页版 GUI 中,你可以点击这个按钮 - 你就在 你的 GitHub 账户中创建了代码的副本(“克隆”)。第一次做这件事可能有点微妙,所以请确保您查看代码库是由原始所有者还是“派生自”的列表 - 如下所示:
一旦你拥有本地副本,你可以随心所欲地进行更改(通过将它们拉取到本地机器并推送)。当你完成后,然后向原始仓库所有者/管理员提交一个“拉取请求”(听起来很花哨,但实际上你只需点击这个:),他们会将其“拉入”。git clone
并粘贴。这将在本地设置好,你也可以推送和拉取到(共享的)GitHub 位置。git clone
命令,然后您就会得到一个本地副本或克隆版本的仓库。这个克隆版本包含所有内容,包括文件、主分支、其他分支、所有现有提交和整个项目。您可以在这个克隆版本上进行添加和提交,然后将这些提交推送到远程仓库。正是这种本地/远程的概念使得Git(以及类似它的系统,如Mercurial)成为DVCS(Distributed Version Control System),而不是更传统的CVSs(Code Versioning Systems),如SVN、PVCS、CVS等,在这些系统中,您直接向远程仓库提交。
核心概念的可视化展示可以在以下链接查看:
http://marklodato.github.com/visual-git-guide/index-en.html 和
http://ndpsoftware.com/git-cheatsheet.html#loc=index
gitg
(macOS上的gitx
),它有一个图形界面,我称之为“地铁地图”(尤其是伦敦地铁),非常适合展示谁做了什么,事物如何改变、分歧和合并等等。虽然 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别名”- 为什么要这样做?所以这些是:
[alias]
co = checkout
cob = checkout -b
示例用法:git co [branch]
<- 分支的选项卡完成将起作用。
您可能会发现https://learngitbranching.js.org/在学习一些基本概念方面非常有用。屏幕截图:
视频:https://youtu.be/23JqqcLPss0
You make changes, add and commit them (but don't push) and then oh! you realize you are in master!
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 !
You mess up some files while working in a local branch and simply want to go back to what you had the last time you did a git pull
:
git reset --hard origin/master # You will need to be comfortable doing this!
You start making changes locally, you edit half a dozen files and then, oh crap, you're still in the master (or another) branch:
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
You mess up one particular file in your current branch and want to basically 'reset' that file (lose changes) to how it was the the last time you pulled it from the remote repository:
git checkout your/directories/filename
This actually resets the file (like many Git commands it is not well named for what it is doing here).
You make some changes locally, you want to make sure you don't lose them while you do a git reset
or rebase
: I often make a manual copy of the entire project (cp -r ../my_project ~/
) when I am not sure if I might mess up in Git or lose important changes.
You are rebasing but things gets messed up:
git rebase --abort # To abandon interactive rebase and merge issues
Add your Git branch to your PS1
prompt (see https://unix.stackexchange.com/a/127800/10043), e.g.
The branch is selenium_rspec_conversion
.
克隆就是仓库的一个副本。在表面上,它的结果等同于svn checkout
,您从其他仓库下载源代码。中心化版本控制系统(如Subversion)和分布式版本控制系统(如Git)之间的区别在于,在Git中,当您进行克隆时,实际上是在复制整个源代码仓库,包括所有历史和分支。现在,在您的计算机上有一个新的仓库,任何您所做的提交都将存储到该仓库中。除非您将这些提交推送到另一个仓库(或原始仓库),否则没有人会看到任何更改,或者直到其他人从您的仓库中拉取提交(如果它是公开可访问的)。
分支是仓库内的一部分。在概念上,它表示一条开发线程。通常您有一个主分支,但您也可以有一个分支,用于开发某些特性xyz,以及另一个分支,用于修复错误abc。当您检出分支后,您所做的任何提交都将留在该分支上,并且不与其他分支共享,直到您将它们合并或重新基于相关的分支。当然,当涉及到分支时,Git似乎有些奇怪,直到您查看分支实现的基本模型。相比之下,我已经说得太多了,我会链接到Git网站上有关Git如何模拟分支和提交的“计算机科学”解释:
http://eagain.net/articles/git-for-computer-scientists/
一个 fork 实际上并不是 Git 的概念,而是一个政治/社会思想。也就是说,如果有人对项目的进展不满意,他们可以获取源代码并在与原始开发者分离的情况下进行工作。这被认为是一个 fork。Git 使得 fork 变得容易,因为每个人都已经拥有自己的源代码“主”副本,所以只需要与原始项目开发者断开联系,而不需要像 SVN 那样从共享仓库中导出历史记录。编辑:由于我不知道现代“fork”的定义,例如 GitHub 等网站的使用,请查看评论以及我的回答下面的 Michael Durrant 提供的更多信息。以下是奥利弗·斯蒂尔(Oliver Steele)提供的图示,展示了所有相关内容的相互关系:
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')
Fork
Clone
补充其他人的意见,有关fork的说明。
需要明白的是,从技术上讲,复制存储库和派生存储库是相同的。请执行以下操作:
git clone $some_other_repo
你可以自豪地为自己鼓掌——你刚刚fork了其他的repo。
Git作为版本控制系统,实际上就是关于forking的。除了使用远程UI(如cgit)“只是浏览”之外,几乎没有什么与git repo无关的事情不涉及在某个时间点上forking克隆repo。
然而,
当有人说“我fork了repo X”,他们的意思是他们已经在别处创建了一个repo的克隆,目的是将其公开给其他人,例如展示一些实验,或应用不同的访问控制机制(例如允许没有Github访问权限但具有公司内部帐户的人进行协作)。
这个repo很可能是使用与git clone不同的其他命令创建的,它很可能托管在服务器上而不是某个人的笔记本电脑上,并且格式略有不同(它是一个“bare repo”,即没有工作树),这些都只是技术细节。
它很可能包含不同的分支、标签或提交记录,这很可能是他们首先做这件事的原因。
(当你点击“fork”时,Github所做的只是添加了一些糖:它为你克隆了repo,将其放在你的帐户下,记录了“forked from”的位置,添加了一个名为“upstream”的远程,并且最重要的是播放了漂亮的动画。)
当有人说“我clone了repo X”,他们的意思是他们已经在本地计算机上创建了一个repo的克隆,目的是研究它、玩耍、为它做出贡献或从源代码中构建一些东西。
Git的美妙之处在于它可以完美地将这些内容结合在一起:所有这些repo共享块提交链的公共部分,因此可以根据需要安全(请参见下面的注释)在所有这些repo之间来回合并更改。
注:只要不重写链的公共部分,并且更改没有冲突,“安全”就是保证的。