Git 工作原理,基本操作,创建与合并分支

前东家使用 Perforce 做版本控制,现东家使用 Git。

Git 工作原理

Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。
这类系统(CVS,Subversion,Perforce,Bazaar 等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容:


其他系统在每个版本中记录着各个文化的具体差异

Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一链接。Git 的工作方式就如下图所示:


Git保存每次更新时的文件快照

工作区 Working Directory VS 暂存区 Staging Directory

Git 工作流

本地仓库由 Git 维护的三棵“树“组成:

  • 工作目录 Working Directory,它持有实际文件
    例如,我们添加了一个文件 NewFile.txt,那它就是在工作目录 Working Directory 中:

    工作目录 Working Directory

  • 暂存区 Staging Directory 或者叫 Index Directory,缓存区域,临时保存文件的改动
    我们通过 git add 命令添加到暂存区 Staging Directory 或者叫 Index Directory:

    暂存区 Staging Directory 或者叫 Index Directory

  • 仓库 Head 区,指向最后一次提交的结果
    我们通过 git commit 命令实际提交改动到 Head 区:

    image.png

通过下面这个图可以清晰的表示不同区的切换:
引用自:http://marklodato.github.io/visual-git-guide/index-zh-cn.html

不同区的切换

文件的三种状态

  • 已修改 (modified):表示修改了某个文件,但还没有提交保存
  • 已暂存 (staged):表示把已修改的文件放在下次提交时要保存的清单中
  • 已提交 (committed):表示该文件已经被安全的保存在本地数据库中了

版本库

工作区中的隐藏目录 .git,就是 Git 的版本库。
.git 目录结构如下:

.git 目录结构

其中:

  • HEAD 文件:指示目前被检出的分支

    HEAD 文件

  • index文件:保存暂存区信息

  • objects 目录:存储所有数据内容

    objects 目录

  • refs 目录:存储指向数据(分支)的提交对象的指针

    refs 目录

什么是 HEAD

HEAD 默认指向指向当前分支的最新提交。


HEAD 默认指向指向当前分支的最新提交

也可以通过 git show HEAD 命令看到当前 HEAD 的位置:

通过 git show HEAD 命令看到当前 HEAD 的位置

  • HEAD 可以指向某个分支,如果指向某个分支,则永远指向分支上的最新提交
  • 当然 HEAD 也可以指向某个提交

什么是 origin 和 master

通过 Git 来修改代码,主要包括如下三步:

  • 从 Git 取数据 git clone
    • 涉及到 远程服务器下的某个仓库下的某个分支 remote server/remote repository/remote branch
  • 改动代码
    • 涉及到 本地某个仓库下的某个分支 local repository/local branch
  • 将改动传回 Git git push
    • 涉及到 远程服务器下的某个仓库下的某个分支 remote server/remote repository/remote branch

涉及到两个 repository:

  • 远程仓库 Remote Repository,在远程服务器上
  • 本地仓库 Local Repository,在本地工作区上

找到最近创建的一个 Github 项目:https://github.com/chenxiangcyr/spring-cloud-config-repo-demo
现在我们通过如下命令 Clone 到本地:
git clone git@github.com:chenxiangcyr/spring-cloud-config-repo-demo.git

在 Clone 完成之后,Git 会自动为你将此远程仓库命名为 origin,并下载其中所有的数据,建立一个指向它的 master 分支的指针。
因此 origin 是远程仓库的名字,master 是分支的名字。
我们用 (远程仓库名)/(分支名) 这样的形式表示远程分支,所以 origin/master 指向的是一个远程分支 remote branch。我们实际上是从这个远程分支 remote branch 中 Clone 数据到本地,但你无法在本地更改远程分支 remote branch 中的数据。
通过 git branch -r 可以查看远程分支:

查看远程分支

我们也可以通过 cat .git/config 看到 origin 的含义:

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
[remote "origin"]
    url = git@github.com:chenxiangcyr/spring-cloud-config-repo-demo.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
    remote = origin
    merge = refs/heads/master

同时,Git 会建立一个属于你自己的本地 master 分支,它指向的是你刚刚从远程分支 remote branch 传到你本地的副本。
通过 git branch 可以查看本地分支:

本地分支

总结:

  • master 就是本地分支 local branch
  • origin/master 是远程分支 remote branch
  • remotes/origin/masterorigin/master 的指向是相同的
  • 我们可以通过 git diff origin/master master 来显示 本地分支 与 远程分支 的区别

随着你不断的改动文件,git add, git commitmaster 的指向会自动移动。最终你还要通过 git push 命令来将修改推送到远程分支。
git push origin master:master

  • 我们看到有两个 master,其中前面一个为 source,后面一个为 destination
  • 该命令可以理解为,在本地仓库中找到名字为 master 的分支,使用它去更新远程仓库下名字为 master 的分支,如果远程仓库下不存在名字是 master 的分支,那么新建一个。
  • git push origin master (省略了<destination>,等价于 git push origin master:master

Git 基本操作

引用自:http://marklodato.github.io/visual-git-guide/index-zh-cn.html


上面的四条命令在工作目录、暂存目录(也叫做索引)和仓库之间复制文件。

  • git add files 把当前文件放入暂存区域。

  • git commit 给暂存区域生成快照并提交。

  • git reset files 用来撤销最后一次 git add files,你也可以用 git reset 撤销所有暂存区域文件。
    例如我们修改了文件 NewFile.txt(添加了内容 123),通过 git add NewFile.txt 放入暂存区域,随后通过 git reset NewFile.txt 来撤销,但是修改的内容没有被撤销(依然是 123):

    git reset files 用来撤销最后一次 git add files

  • git checkout files 把文件从暂存区域复制到工作目录,用来丢弃本地修改。
    例如在上面的基础上执行 git checkout NewFile.txt,这时候 NewFile.txt 所修改的内容被撤销了。

    git checkout files 把文件从暂存区域复制到工作目录,用来丢弃本地修改

也可以跳过暂存区域直接从仓库取出文件或者直接提交代码:


  • git commit -a相当于运行 git add 把所有当前目录下的文件加入暂存区域再运行 git commit
  • git commit files 进行一次包含最后一次提交加上工作目录中文件快照的提交,并且文件被添加到暂存区域。
  • git checkout HEAD files 从仓库历史中回滚到复制最后一次提交到工作区。

diff 查看两次提交之间的变动

  • git diff NexFile.txt 查看工作目录与暂存区的变动
  • git diff HEAD NexFile.txt 查看工作目录与仓库中 HEAD 的变动
  • git diff --cached NexFile.txt 查看暂存区与仓库中 HEAD 的变动
  • git diff <branch> NexFile.txt 查看工作目录与仓库中某个分支的变动
  • git diff <commit> <commit> 查看两次 commit 之间的变动
    diff 查看两次提交之间的变动

checkout

checkout 命令用于从历史提交(或者暂存区域)中拷贝文件到工作目录,也可用于切换分支。

  • 当给定某个文件名(或者打开 -p 选项,或者文件名和 -p选项同时打开)时,Git 会从指定的提交中拷贝文件到暂存区域和工作目录。比如,git checkout HEAD NewFile.txt 会将提交节点 HEAD~ (即当前提交节点的父节点)中的 NewFile.txt 复制到工作目录并且加到暂存区域中。
  • 当不指定文件名,而是给出一个(本地)分支时,那么 HEAD 标识会移动到那个分支(也就是说,我们“切换”到那个分支了),然后暂存区域和工作目录中的内容会和 HEAD 对应的提交节点一致。
  • 如果既没有指定文件名,也没有指定分支名,而是一个标签、远程分支、SHA-1值或者是像 master~3 类似的东西,就得到一个匿名分支,称作 detached HEAD(被分离的HEAD标识)。

reset

reset 命令把当前分支指向另一个位置,并且有选择的变动工作目录和索引。也用来在从历史仓库中复制文件到索引,而不动工作目录。

如果不给选项,那么当前分支指向到那个提交。如果用 --hard 选项,那么工作目录也更新,如果用 --soft选项,那么都不变。
如果没有给出提交点的版本号,那么默认用 HEAD。这样,分支指向不变,但是索引会回滚到最后一次提交,如果用 --hard 选项,工作目录也同样。

reset

cherry pick

cherry-pick 命令"复制"一个提交节点并在当前分支做一次完全一样的新提交。

cherry pick

rebase

交互式变基 (rebase)。它可以用来编辑提交信息,或者将多个提交压缩成一个提交。
git rebase -i HEAD~n

Git 创建与合并分支

Git 关于分支的操作主要包括:

  • 查看分支git branch
  • 创建分支git branch <name>
  • 切换分支git checkout <name>
  • 创建 + 切换分支git checkout -b <name>
  • 合并某分支到当前分支git merge <name>
  • 删除分支git branch -d <name>

每次提交,Git 都把它们串成一条时间线,这条时间线就是一个分支。在 Git 里,这个分支叫主分支,即 master 分支。HEAD 严格来说不是指向提交,而是指向 mastermaster 才是指向提交的,所以, HEAD 指向的就是当前分支。每次提交,master 分支都会向前移动一步,这样,随着你不断提交,master 分支的线也越来越长:

一个具体的 Case

我们目前在 master 分支上,下一阶段要开发一个新需求付款,因此需要在一个新的分支 payment 上开发,测试,最后合并到 master 分支上。

创建新分支:

git checkout -b payment

以上操作相当于:

git branch payment
git checkout payment

随后我们在 payment 分支在不断的向前推进:

这时候,突然来了一个线上的 Issue,需求紧急修复,因此我们停下手动上的工作,需要在一个新的分支 bugfix 上开发,测试,最后合并到 master 分支上。

创建新分支:

git checkout -b bugfix

随后我们在 bugfix 分支上进行修改:

测试没问题后,合并到 master 分支上:

git checkout master
git merge bugfix

在合并的时候,由于当前 master 分支所指向的提交是 bugfix 的直接上游,所以 Git 只是简单的将指针向前移动。 换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧,这就叫做 “快进(fast-forward)”。

随后我们上线该紧急修复,然后可以使用命令来删除 bugfix 分支:

git branch -d bugfix

接下来,我们切换到之前的 payment 分支上继续工作:

测试没问题后,合并到 master 分支上:

git checkout master
git merge payment

这和你之前合并 bugfix 分支的时候看起来有一点不一样。在这种情况下,你的开发历史从一个更早的地方开始分叉开来。 因为 master 分支所在提交并不是 payment 分支所在提交的直接祖先,Git 不得不做一些额外的工作。

出现这种情况的时候,Git 会使用两个分支的末端所指的快照(蓝色部分)以及这两个分支的工作祖先(黄色部分),做一个简单的三方合并。


引用:
Git 教程 创建与合并分支
3.2 Git 分支 - 分支的新建与合并
浅析 Git 思想和工作原理
图解Git
Git 的origin和master分析(转)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 在 版本回退 里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条...
    sidney_c阅读 287评论 0 0
  • Git 基础 基本原理 客户端并不是只提取最新版本的文件快照,而是把代码仓库完整的镜像下来。这样一来,任何一处协同...
    __silhouette阅读 15,765评论 5 147
  • 夏日伊始的思念 缠绕着太阳炽热的光线 跨进南回归线 落满地的秋叶 层层叠叠 满是回忆的积淀 晴天夜空的星星 是你寄...
    惠施chen阅读 187评论 0 2
  • 总有一天你会和我遇见 像好友一样却失散多年 你一句我一句单纯聊天 说你过的还不错在以前 靓丽光鲜映入了我眼帘 讲我...
    沈九壹阅读 173评论 0 0
  • 又是一阵风雨来袭 而我独往其中 别问我,我是谁 一场从未相遇的运 而你不曾遇见 而这不曾怀恋 而我失去了记忆 只是...
    念及我x阅读 169评论 0 1