图解Git工作原理

本文图解Git中的最常用命令。如果你稍微理解Git的工作原理,这篇文章能够让你理解的更透彻。

图解Git工作原理

基本用法

图解Git工作原理

图解Git工作原理

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

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

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

  • git reset – files用来撤销最后一次git add files,你也可以用git reset撤销所有暂存区域文件。

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

你可以用git reset -p,git checkout -p,or git add -p进入交互模式。

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

图解Git工作原理

  • git commit -a相当于运行git add把所有当前目录下的文件加入暂存区域再运行。

  • git commit files进行一次包含最后一次提交加上工作目录中文件快照的提交。并且文件被添加到暂存区域。

  • git checkout HEAD – files回滚到复制最后一次提交。

 
图解Git工作原理

约定

图解Git工作原理

后文中以下面的形式使用图片。

图解Git工作原理

绿色的5位字符表示提交的ID,分别指向父节点。分支用橘色显示,分别指向特定的提交。当前分支由附在其上的HEAD标识。这张图片里显示最后5次提交,ed489是最新提交。master分支指向此次提交,另一个maint分支指向祖父提交节点。

图解Git工作原理

命令详解

图解Git工作原理

Diff

有许多种方法查看两次提交之间的变动,下面是一些示例。

图解Git工作原理

Commit

提交时,Git用暂存区域的文件创建一个新的提交,并把此时的节点设为父节点。然后把当前分支指向新的提交节点。下图中,当前分支是master。在运行命令之前,master指向ed489,提交后,master指向新的节点f0cec并以ed489作为父节点。

图解Git工作原理

即便当前分支是某次提交的祖父节点,Git会同样操作。下图中,在master分支的祖父节点maint分支进行一次提交,生成了1800b。这样,maint分支就不再是master分支的祖父节点。此时,合并[1](或者衍合[2])是必须的。

图解Git工作原理

如果想更改一次提交,使用git commit –amend。Git会使用与当前提交相同的父节点进行一次新提交,旧的提交会被取消。

图解Git工作原理

另一个例子是分离HEAD提交[3],后文讲。

Checkout

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

当给定某个文件名(或者打开-p选项,或者文件名和-p选项同时打开)时,Git会从指定的提交中拷贝文件到暂存区域和工作目录。比如,git checkout HEAD~ foo.c会将提交节点HEAD~(即当前提交节点的父节点)中的foo.c复制到工作目录并且加到暂存区域中。(如果命令中没有指定提交节点,则会从暂存区域中拷贝内容。)注意当前分支不会发生变化。

图解Git工作原理

当不指定文件名,而是给出一个(本地)分支时,那么HEAD标识会移动到那个分支(也就是说,我们“切换”到那个分支了),然后暂存区域和工作目录中的内容会和HEAD对应的提交节点一致。

新提交节点(下图中的a47c3)中的所有文件都会被复制(到暂存区域和工作目录中);只存在于老的提交节点(ed489)中的文件会被删除;不属于上述两者的文件会被忽略,不受影响。

图解Git工作原理

如果既没有指定文件名,也没有指定分支名,而是一个标签、远程分支、SHA-1值或者是像master~3类似的东西,就得到一个匿名分支,称作detached HEAD(被分离的HEAD标识)。

这样可以很方便地在历史版本之间互相切换。比如说你想要编译1.6.6.1版本的Git,你可以运行git checkout v1.6.6.1(这是一个标签,而非分支名),编译,安装,然后切换回另一个分支,比如说git checkout master。然而,当提交操作涉及到“分离的HEAD”时,其行为会略有不同,详情见在下面。

图解Git工作原理

HEAD标识处于分离状态时的提交操作

当HEAD处于分离状态(不依附于任一分支)时,提交操作可以正常进行,但是不会更新任何已命名的分支。(你可以认为这是在更新一个匿名分支。)

图解Git工作原理

一旦此后你切换到别的分支,比如说master,那么这个提交节点(可能)再也不会被引用到,然后就会被丢弃掉了。注意这个命令之后就不会有东西引用2eecb。

图解Git工作原理

但是,如果你想保存这个状态,可以用命令git checkout -b name来创建一个新的分支。

图解Git工作原理

Reset

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

如果不给选项,那么当前分支指向到那个提交。如果用–hard选项,那么工作目录也更新,如果用–soft选项,那么都不变。

图解Git工作原理

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

图解Git工作原理

如果给了文件名(或者-p选项),那么工作效果和带文件名的checkout差不多,除了索引被更新。

图解Git工作原理

Merge

Merge命令把不同分支合并起来。合并前,索引必须和当前提交相同。如果另一个分支是当前提交的祖父节点,那么合并命令将什么也不做。另一种情况是如果当前提交是另一个分支的祖父节点,就导致fast-forward合并。指向只是简单的移动,并生成一个新的提交。

图解Git工作原理

否则就是一次真正的合并。默认把当前提交(ed489 如下所示)和另一个提交(33104)以及他们的共同祖父节点(b325c)进行一次三方合并[4]。结果是先保存当前目录和索引,然后和父节点33104一起做一次新提交。

图解Git工作原理

Cherry Pick

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

图解Git工作原理

Rebase

衍合是合并命令的另一种选择。合并把两个父分支合并进行一次提交,提交历史不是线性的。衍合在当前分支上重演另一个分支的历史,提交历史是线性的。本质上,这是线性化的自动的 cherry-pick。

图解Git工作原理

上面的命令都在topic分支中进行,而不是master分支,在master分支上重演,并且把分支指向新的节点。注意旧提交没有被引用,将被回收。

要限制回滚范围,使用–onto选项。下面的命令在master分支上重演当前分支从169a6以来的最近几个提交,即2c33a。

图解Git工作原理

同样有git rebase –interactive让你更方便的完成一些复杂操作,比如丢弃、重排、修改、合并提交。没有图片体现这些,细节看这里:git-rebase(1)[5]。

 
图解Git工作原理

技术说明

图解Git工作原理

文件内容并没有真正存储在索引(.git/index)或者提交对象中,而是以blob的形式分别存储在数据库中(.git/objects),并用SHA-1值来校验。索引文件用识别码列出相关的blob文件以及别的数据。

对于提交来说,以树(tree)的形式存储,同样用对于的哈希值识别。树对应着工作目录中的文件夹,树中包含的 树或者blob对象对应着相应的子目录和文件。每次提交都存储下它的上一级树的识别码。

如果用detached HEAD提交,那么最后一次提交会被the reflog for HEAD引用。但是过一段时间就失效,最终被回收,与git commit –amend或者git rebase很像。

相关链接:

  1. http://marklodato.github.io/visual-git-guide/index-zh-cn.html#merge
  2. http://marklodato.github.io/visual-git-guide/index-zh-cn.html#rebase
  3. http://marklodato.github.io/visual-git-guide/index-zh-cn.html#detached
  4. http://en.wikipedia.org/wiki/Three-way_merge
  5. http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html#_interactive_mode

转自:https://mp.weixin.qq.com/s/BrciK1YqQlIGrfOFfJQ9EA

常用的Git命令清单

来自:CSDN,作者:dqy19901103

链接:https://blog.csdn.net/dqy19901103/article/details/107380601

常用Git命令清单

一般来说,日常使用只要记住下图6个命令,就可以了。但是熟练使用,恐怕要记住60~100个命令。

名次解释

常用的Git命令清单

下面是我整理的常用 Git 命令清单。几个专用名词的译名如下: Workspace:工作区 Index / Stage:暂存区 Repository:仓库区(或本地仓库) Remote:远程仓库

一、新建代码库

# 在当前目录新建一个Git代码库

nbsp;git init

# 新建一个目录,将其初始化为Git代码库

nbsp;git init [project-name]

# 下载一个项目和它的整个代码历史

nbsp;git clone [url]

二、配置

Git的设置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)

# 显示当前的Git配置

nbsp;git config --list

# 编辑Git配置文件

nbsp;git config -e [--global]

# 设置提交代码时的用户信息

nbsp;git config [--global] user.name "[name]"

nbsp;git config [--global] user.email "[email address]"

# 颜色设置
git config --global color.ui true                         # git status等命令自动着色
git config --global color.status auto
git config --global color.diff auto
git config --global color.branch auto
git config --global color.interactive auto
git config --global --unset http.proxy                    # remove  proxy configuration on git

三、增加/删除文件

# 添加指定文件到暂存区

nbsp;git add [file1] [file2] ...

# 添加指定目录到暂存区,包括子目录

nbsp;git add [dir]

# 添加当前目录的所有文件到暂存区

nbsp;git add .

# 添加每个变化前,都会要求确认
# 对于同一个文件的多处变化,可以实现分次提交

nbsp;git add -p

# 删除工作区文件,并且将这次删除放入暂存区

nbsp;git rm [file1] [file2] ...

# 停止追踪指定文件,但该文件会保留在工作区

nbsp;git rm --cached [file]

# 改名文件,并且将这个改名放入暂存区

nbsp;git mv [file-original] [file-renamed]

四、代码提交

# 提交暂存区到仓库区

nbsp;git commit -m [message]

# 提交暂存区的指定文件到仓库区

nbsp;git commit [file1] [file2] ... -m [message]

# 提交工作区自上次commit之后的变化,直接到仓库区

nbsp;git commit -a

# 提交时显示所有diff信息

nbsp;git commit -v

# 将add和commit合为一步

nbsp;git commit -am 'message'

# 使用一次新的commit,替代上一次提交
# 如果代码没有任何新变化,则用来改写上一次commit的提交信息

nbsp;git commit --amend -m [message]

# 重做上一次commit,并包括指定文件的新变化

nbsp;git commit --amend [file1] [file2] ...

五、分支

# 列出所有本地分支

nbsp;git branch

# 列出所有远程分支

nbsp;git branch -r

# 列出所有本地分支和远程分支

nbsp;git branch -a

# 新建一个分支,但依然停留在当前分支

nbsp;git branch [branch-name]

# 新建一个分支,并切换到该分支

nbsp;git checkout -b [branch]

# 新建一个分支,指向指定commit

nbsp;git branch [branch] [commit]

# 新建一个分支,与指定的远程分支建立追踪关系

nbsp;git branch --track [branch] [remote-branch]

# 切换到指定分支,并更新工作区

nbsp;git checkout [branch-name]

# 切换到上一个分支

nbsp;git checkout -

# 建立追踪关系,在现有分支与指定的远程分支之间

nbsp;git branch --set-upstream [branch] [remote-branch]

# 合并指定分支到当前分支

nbsp;git merge [branch]

# 选择一个commit,合并进当前分支

nbsp;git cherry-pick [commit]

# 删除分支

nbsp;git branch -d [branch-name]

# 删除远程分支

nbsp;git push origin --delete [branch-name]

nbsp;git branch -dr [remote/branch]

# 检出版本v2.0

nbsp;git checkout v2.0

# 从远程分支develop创建新本地分支devel并检出

nbsp;git checkout -b devel origin/develop

# 检出head版本的README文件(可用于修改错误回退)
git checkout -- README                                    

六、标签

# 列出所有tag

nbsp;git tag

# 新建一个tag在当前commit

nbsp;git tag [tag]

# 新建一个tag在指定commit

nbsp;git tag [tag] [commit]

# 删除本地tag

nbsp;git tag -d [tag]

# 删除远程tag

nbsp;git push origin :refs/tags/[tagName]

# 查看tag信息

nbsp;git show [tag]

# 提交指定tag

nbsp;git push [remote] [tag]

# 提交所有tag

nbsp;git push [remote] --tags

# 新建一个分支,指向某个tag

nbsp;git checkout -b [branch] [tag]

七、查看信息

# 显示有变更的文件

nbsp;git status

# 显示当前分支的版本历史

nbsp;git log

# 显示commit历史,以及每次commit发生变更的文件

nbsp;git log --stat

# 搜索提交历史,根据关键词

nbsp;git log -S [keyword]

# 显示某个commit之后的所有变动,每个commit占据一行

nbsp;git log [tag] HEAD --pretty=format:%s

# 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件

nbsp;git log [tag] HEAD --grep feature

# 显示某个文件的版本历史,包括文件改名

nbsp;git log --follow [file]

nbsp;git whatchanged [file]

# 显示指定文件相关的每一次diff

nbsp;git log -p [file]

# 显示过去5次提交

nbsp;git log -5 --pretty --oneline

# 显示所有提交过的用户,按提交次数排序

nbsp;git shortlog -sn

# 显示指定文件是什么人在什么时间修改过

nbsp;git blame [file]

# 显示暂存区和工作区的差异

nbsp;git diff

# 显示暂存区和上一个commit的差异

nbsp;git diff --cached [file]

# 显示工作区与当前分支最新commit之间的差异

nbsp;git diff HEAD

# 显示两次提交之间的差异

nbsp;git diff [first-branch]...[second-branch]

# 显示今天你写了多少行代码

nbsp;git diff --shortstat "@{0 day ago}"

# 显示某次提交的元数据和内容变化

nbsp;git show [commit]

# 显示某次提交发生变化的文件

nbsp;git show --name-only [commit]

# 显示某次提交时,某个文件的内容

nbsp;git show [commit]:[filename]

# 显示当前分支的最近几次提交

nbsp;git reflog

八、远程同步

# 下载远程仓库的所有变动

nbsp;git fetch [remote]

# 显示所有远程仓库

nbsp;git remote -v

# 显示某个远程仓库的信息

nbsp;git remote show [remote]

# 增加一个新的远程仓库,并命名

nbsp;git remote add [shortname] [url]

# 取回远程仓库的变化,并与本地分支合并

nbsp;git pull [remote] [branch]

# 上传本地指定分支到远程仓库

nbsp;git push [remote] [branch]

# 强行推送当前分支到远程仓库,即使有冲突

nbsp;git push [remote] --force

# 推送所有分支到远程仓库

nbsp;git push [remote] --all

九、撤销

# 恢复暂存区的指定文件到工作区

nbsp;git checkout [file]

# 恢复某个commit的指定文件到暂存区和工作区

nbsp;git checkout [commit] [file]

# 恢复暂存区的所有文件到工作区

nbsp;git checkout .

# 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变

nbsp;git reset [file]

# 重置暂存区与工作区,与上一次commit保持一致

nbsp;git reset --hard

# 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变

nbsp;git reset [commit]

# 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致

nbsp;git reset --hard [commit]

# 重置当前HEAD为指定commit,但保持暂存区和工作区不变

nbsp;git reset --keep [commit]

# 新建一个commit,用来撤销指定commit
# 后者的所有变化都将被前者抵消,并且应用到当前分支

nbsp;git revert [commit]

# 暂时将未提交的变化移除,稍后再移入

nbsp;git stash

nbsp;git stash pop

十、其他

git init                                                  # 初始化本地git仓库(创建新仓库)
git config --global user.name "xxx"                       # 配置用户名
git config --global user.email "xxx@xxx.com"              # 配置邮件
git config --global color.ui true                         # git status等命令自动着色
git config --global color.status auto
git config --global color.diff auto
git config --global color.branch auto
git config --global color.interactive auto
git config --global --unset http.proxy                    # remove  proxy configuration on git
git clone git+ssh://git@192.168.53.168/VT.git             # clone远程仓库
git status                                                # 查看当前版本状态(是否修改)
git add xyz                                               # 添加xyz文件至index
git add .                                                 # 增加当前子目录下所有更改过的文件至index
git commit -m 'xxx'                                       # 提交
git commit --amend -m 'xxx'                               # 合并上一次提交(用于反复修改)
git commit -am 'xxx'                                      # 将add和commit合为一步
git rm xxx                                                # 删除index中的文件
git rm -r *                                               # 递归删除
git log                                                   # 显示提交日志
git log -1                                                # 显示1行日志 -n为n行
git log -5
git log --stat                                            # 显示提交日志及相关变动文件
git log -p -m
git show dfb02e6e4f2f7b573337763e5c0013802e392818         # 显示某个提交的详细内容
git show dfb02                                            # 可只用commitid的前几位
git show HEAD                                             # 显示HEAD提交日志
git show HEAD^                                            # 显示HEAD的父(上一个版本)的提交日志 ^^为上两个版本 ^5为上5个版本
git tag                                                   # 显示已存在的tag
git tag -a v2.0 -m 'xxx'                                  # 增加v2.0的tag
git show v2.0                                             # 显示v2.0的日志及详细内容
git log v2.0                                              # 显示v2.0的日志
git diff                                                  # 显示所有未添加至index的变更
git diff --cached                                         # 显示所有已添加index但还未commit的变更
git diff HEAD^                                            # 比较与上一个版本的差异
git diff HEAD -- ./lib                                    # 比较与HEAD版本lib目录的差异
git diff origin/master..master                            # 比较远程分支master上有本地分支master上没有的
git diff origin/master..master --stat                     # 只显示差异的文件,不显示具体内容
git remote add origin git+ssh://git@192.168.53.168/VT.git # 增加远程定义(用于push/pull/fetch)
git branch                                                # 显示本地分支
git branch --contains 50089                               # 显示包含提交50089的分支
git branch -a                                             # 显示所有分支
git branch -r                                             # 显示所有原创分支
git branch --merged                                       # 显示所有已合并到当前分支的分支
git branch --no-merged                                    # 显示所有未合并到当前分支的分支
git branch -m master master_copy                          # 本地分支改名
git checkout -b master_copy                               # 从当前分支创建新分支master_copy并检出
git checkout -b master master_copy                        # 上面的完整版
git checkout features/performance                         # 检出已存在的features/performance分支
git checkout --track hotfixes/BJVEP933                    # 检出远程分支hotfixes/BJVEP933并创建本地跟踪分支
git checkout v2.0                                         # 检出版本v2.0
git checkout -b devel origin/develop                      # 从远程分支develop创建新本地分支devel并检出
git checkout -- README                                    # 检出head版本的README文件(可用于修改错误回退)
git merge origin/master                                   # 合并远程master分支至当前分支
git cherry-pick ff44785404a8e                             # 合并提交ff44785404a8e的修改
git push origin master                                    # 将当前分支push到远程master分支
git push origin :hotfixes/BJVEP933                        # 删除远程仓库的hotfixes/BJVEP933分支
git push --tags                                           # 把所有tag推送到远程仓库
git fetch                                                 # 获取所有远程分支(不更新本地分支,另需merge)
git fetch --prune                                         # 获取所有原创分支并清除服务器上已删掉的分支
git pull origin master                                    # 获取远程分支master并merge到当前分支
git mv README README2                                     # 重命名文件README为README2
git reset --hard HEAD                                     # 将当前版本重置为HEAD(通常用于merge失败回退)
git rebase
git branch -d hotfixes/BJVEP933                           # 删除分支hotfixes/BJVEP933(本分支修改已合并到其他分支)
git branch -D hotfixes/BJVEP933                           # 强制删除分支hotfixes/BJVEP933
git ls-files                                              # 列出git index包含的文件
git show-branch                                           # 图示当前分支历史
git show-branch --all                                     # 图示所有分支历史
git whatchanged                                           # 显示提交历史对应的文件修改
git revert dfb02e6e4f2f7b573337763e5c0013802e392818       # 撤销提交dfb02e6e4f2f7b573337763e5c0013802e392818
git ls-tree HEAD                                          # 内部命令:显示某个git对象
git rev-parse v2.0                                        # 内部命令:显示某个ref对于的SHA1 HASH
git reflog                                                # 显示所有提交,包括孤立节点
git show HEAD@{5}
git show master@{yesterday}                               # 显示master分支昨天的状态
git log --pretty=format:'%h %s' --graph                   # 图示提交日志
git show HEAD~3
git show -s --pretty=raw 2be7fcb476
git stash                                                 # 暂存当前修改,将所有至为HEAD状态
git stash list                                            # 查看所有暂存
git stash show -p stash@{0}                               # 参考第一次暂存
git stash apply stash@{0}                                 # 应用第一次暂存
git grep "delete from"                                    # 文件中搜索文本“delete from”
git grep -e '#define' --and -e SORT_DIRENT
git gc
git fsck
# 生成一个可供发布的压缩包

nbsp;git archive

参考文章

  • 常用 Git 命令清单 – 阮一峰的网络日志
  • 老铁,这年头不会点Git真不行
  • 一些命令-github
    Home-geeeeeeeeek/git-recipes Wiki-GitHub

推荐↓↓↓

 

常用的Git命令清单

前端开发

一文讲透 Git 底层数据结构和原理

一文讲透 Git 底层数据结构和原理

阿里妹导读:本文将系统分享 Git 底层知识:对象生命周期变化,底层数据结构,数据包文件结构,数据包文件索引,以及详细分析对象查询流程和算法。

文末福利:程序员需要哪些软技能?

状态模型

一文讲透 Git 底层数据结构和原理

上图描述了 git 对象的在不同的生命周期中不同的存储位置,通过不同的 git 命令改变 git 对象的存储生命周期。
工作区 (workspace)
 
就是我们当前工作空间,也就是我们当前能在本地文件夹下面看到的文件结构。初始化工作空间或者工作空间 clean 的时候,文件内容和 index 暂存区是一致的,随着修改,工作区文件在没有 add 到暂存区时候,工作区将和暂存区是不一致的。
暂存区 (index)
 
老版本概念也叫 Cache 区,就是文件暂时存放的地方,所有暂时存放在暂存区中的文件将随着一个 commit 一起提交到 local repository 此时 local repository 里面文件将完全被暂存区所取代。暂存区是 git 架构设计中非常重要和难理解的一部分。
本地仓库 (local repository)
 
git 是分布式版本控制系统,和其他版本控制系统不同的是他可以完全去中心化工作,你可以不用和中央服务器 (remote server) 进行通信,在本地即可进行全部离线操作,包括 log,history,commit,diff 等等。完成离线操作最核心是因为 git 有一个几乎和远程一样的本地仓库,所有本地离线操作都可以在本地完成,等需要的时候再和远程服务进行交互。
远程仓库 (remote repository)
 
中心化仓库,所有人共享,本地仓库会需要和远程仓库进行交互,也就能将其他所有人内容更新到本地仓库把自己内容上传分享给其他人。结构大体和本地仓库一样。
文件在不同的操作下可能处于不同的 git 生命周期,下面看看一个文件变化的例子。
文件变化
 

一文讲透 Git 底层数据结构和原理

对象模型
 
仓库结构
 
git 分布式的一个重要体现是 git 在本地是有一个完整的 git 仓库也就是 .git 文件目录,通过这个仓库,git 就可以完全离线化操作。在这个本地化的仓库中存储了 git 所有的模型对象。下面是 git 仓库的 tree 和相关说明:
 

一文讲透 Git 底层数据结构和原理

git 主要有四个对象,分别是 Blob,Tree, Commit, Tag 他们都用 SHA-1 进行命名。
你可以用 git cat-file -t 查看每个 SHA-1 的类型,用 git cat-file -p 查看每个对象的内容和简单的数据结构。git cat-file 是 git 的瑞士军刀,是底层核心命令。
Blob 对象
 
只用于存储单个文件内容,一般都是二进制的数据文件,不包含任何其他文件信息,比如不包含文件名和其他元数据。
Tree 对象
对应文件系统的目录结构,里面主要有:子目录 (tree),文件列表 (blob),文件类型以及一些数据文件权限模型等。
如下图输出:
 git cat-file -t ed807a4d010a06ca83d448bc74c6cc79121c07c3tree git cat-file -p ed807a4d010a06ca83d448bc74c6cc79121c07c3100644 blob 36a982c504eb92330573aa901c7482f7e7c9d2e6    .cise.yml100644 blob c439a8da9e9cca4e7b29ee260aea008964a00e9a    .eslintignore100644 blob 245b35b9162bec4ef798eb05b533e6c98633af5c    .eslintrc100644 blob 10123778ec5206edcd6e8500cc78b77e79285f6d    .gitignore100644 blob 1a48aa945106d7591b6342585b1c29998e486bf6    README.md100644 blob 514f7cb2645f44dd9b66a87f869d42902174fe40    abc.json040000 tree 8955f46834e3e35d74766639d740af922dcaccd3    cli_list100644 blob f7758d0600f6b9951cf67f75cf0e2fabcea55771    dep.json040000 tree e2b3ee59f6b030a45c0bf2770e6b0c1fa5f1d8c7    doc100644 blob e3c712d7073957c3376d182aeff5b96f28a37098    index.js040000 tree b4aadab8fc0228a14060321e3f89af50ba5817ca    lib040000 tree 249eafef27d9d8ebe966e35f96b3092d77485a79    mock100644 blob 95913ff73be1cc7dec869485e80072b6abdd7be4    package.json040000 tree e21682d1ebd4fdd21663ba062c5bfae0308acb64    src040000 tree 91612a9fa0cea4680228bfb582ed02591ce03ef2    static040000 tree d0265f130d2c5cb023fe16c990ecd56d1a07b78c    task100644 blob ab04ef3bda0e311fc33c0cbc8977dcff898f4594    webpack.config.js100644 blob fb8e6d3a39baf6e339e235de1a9ed7c3f1521d55    webpack.dll.config.js040000 tree 5dd44553be0d7e528b8667ac3c027ddc0909ef36    webpack
 
详细解释如下:

一文讲透 Git 底层数据结构和原理

Commit 对象
 
是一次修改的集合,当前所有修改的文件的一个集合,可以类比一批操作的“事务”。是修改过的文件集的一个快照,随着一次 commit 操作,修改过的文件将会被提交到 local repository 中。通过 commit 对象,在版本化中可以检索出每次修改内容,是版本化的基石。
 
 git cat-file -t fbf9e415f77008b780b40805a9bb996b37a6ad2ccommit git cat-file -p fbf9e415f77008b780b40805a9bb996b37a6ad2ctree bd31831c26409eac7a79609592919e9dcd1a76f2parent d62cf8ef977082319d8d8a0cf5150dfa1573c2b7author xxx  1502331401 +0800committer xxx  1502331401 +0800修复增量bug
详细解释如下:
 

一文讲透 Git 底层数据结构和原理

Tag 对象
 
tag 是一个”固化的分支”,一旦打上 tag 之后,这个 tag 代表的内容将永远不可变,因为 tag 只会关联当时版本库中最后一个 commit 对象。
分支的话,随着不断的提交,内容会不断的改变,因为分支指向的最后一个 commit 不断改变。所以一般应用或者软件版本的发布一般用 tag。
git 的 Tag 类型有两种:
1  lightweight (轻量级)
 
创建方式:
 
git tag tagName
这种方式创建的 Tag,git 底层不会创建一个真正意义上的 tag 对象,而是直接指向一个 commit 对象,此时如果使用 git cat-file -t tagName 会返回一个 commit。
 git cat-file -t v4commit git cat-file -p v4tree ceab4f96440655b0ff1a783316c95450fa1fb436parent 7f23c9ca70ce64fc58e8c7507c990c6c6a201d3dauthor 与水  1506224164 +0800committer 与水  1506224164 +0800
rawtest2
2  annotated (含附注)
创建方式:
 
git tag -a tagName -m''
 
这种方式创建的标签,git 底层会创建一个 tag 对象,tag 对象会包含相关的 commit 信息和 tagger 等额外信息,此时如果使用 git cat-file -t tagname 会返回一个 tag。
 git cat-file -t v3tag git cat-file -p v3object d5d55a49c337d36e16dd4b05bfca3816d8bf6de8   //commit 对象SHA-1type committag v3tagger xxx  1506230900 +0800
与水测试标注型tag
    

一文讲透 Git 底层数据结构和原理

总结:所有对象模型之间的关系大致如下:

一文讲透 Git 底层数据结构和原理

存储模型
 
概念
 
git 区别与其他 vcs 系统的一个最主要原因之一是:git 对文件版本管理和其他 vcs 系统对文件版本的实现理念完成不一样。这也就是 git 版本管理为什么如此强大的最核心的地方。
Svn 等其他的 VCS 对文件版本的理念是以文件为水平维度,记录每个文件在每个版本下的 delta 改变。
Git 对文件版本的管理理念却是以每次提交为一次快照,提交时对所有文件做一次全量快照,然后存储快照引用。
Git 在存储层,如果文件数据没有改变的文件,Git 只是存储指向源文件的一个引用,并不会直接多次存储文件,这一点可以在 pack 文件中看见。
如下图所示:

一文讲透 Git 底层数据结构和原理

存储
 
随着需求和功能的不断复杂,git 版本的不断更新,但是主要的存储模型还是大致不变。如下图所示:

一文讲透 Git 底层数据结构和原理

检索模型
 
cd .git/objects/→ ls03   28   7f   ce   d0   d5   e6   f9   info pack
git 的对象有两种:
一种是松散对象,就是在如上 .git/objects 的文件夹 03 28 7f ce d0 d5 e6 f9 等,这些文件夹只有 2 个字符开头,其实就是每个文件 SHA-1 值的前 2 个字母,最多有 #OXFF 256 个文件夹。
一种是打包压缩对象,打包压缩之后的对象主要存在的是 pack 文件中,主要用于文件在网络上传输,减少网络消耗。
为了节省存储空间,可以手动触发打包压缩操作 (git gc),将松散对象打包成 pack 文件对象。也可以将 pack 文件解压缩成松散对象 (git unpack-objects)
→ cd pack→ lspack-efbf3149604d24e6ea427b025da0c59245b2c2ea.idx  pack-efbf3149604d24e6ea427b025da0c59245b2c2ea.pack
为了加快 pack 文件的检索效率,git 基于 pack 文件会生成相应的索引 idx 文件。
pack 文件
 
pack 文件设计非常精密和巧妙,本着降低文件大小,减少文件传输,降低网络开销和安全传输的原则设计的。
 
pack 文件设计的概图如下:

一文讲透 Git 底层数据结构和原理

pack 文件主要有三部分组成,Header, Body, Trailer
  • Header 部分主要 4-byte “PACK”, 4-byte “版本号”, 4-byte “Object 条目数”。
 
  • Body 部分主要是一个个 Git 对象依次存储,存储位置在 idx 索引文件中记录改对象在 pack 文件中的偏移量 offset。
 
  • Trailer 部分主要是所有 Objects 的名 (SHA-1)的校验和,为了安全可靠的文件传输。
下面我们看具体的 pack 文件:

一文讲透 Git 底层数据结构和原理

从上图可知:通过 idx 索引文件在 pack 文件中定位到对象之后,对象的结构主要 Header 和 Data 两部分。
1  Header 部分

一文讲透 Git 底层数据结构和原理

Header 中首 8-bits:1-bit 是 MSB,接着的 3-bits 表示的是当前对象类型,主要有 6种存储类型,接着的 4-bits 是用于表示该 Object 展开的 (length) 大小的一部分,只是一部分,完整的大小取决于MSB和接下来的多个 bits,完整算法如下:
 
  • 如果 8-bits 中第一位是 1,表示下一个字节还是 header 的一部分,用于表示该对象展开的大小。
 
  • 如果 8-bits 中第一位是 0,表示从下一个字节开始,将是数据 Data 文件。
 
  • 如果对象类型是 OBJ_OFS_DELTA 类型, 表示的是 Delta 存储,当前 git 对象只是存储了增量部分,对于基本的部分将由接下来的可变长度的字节数用于表示 base object 的距离当前对象的偏移量,接下来的可变字节也是用 1-bit MSB 表示下一个字节是否是可变长度的组成部分。对偏移量取负数,就可知 base 对象在当前对象的前面多少字节。
 
  • 如果对象类型是 OBJ_REF_DELTA 类型,表示的是 Delta 存储,当前 git 对象只是存储了增量部分,对于基本的部分,用 20-bytes 存储 Base Object 的 SHA-1 。
 
2  Data 部分
是经过 Zlib 压缩过的数据。可能是全部数据,也有可能是 Delta 数据,具体看 Header 部分的存储类型,如果是 OBJ_OFS_DELTA 或者 OBJ_REF_DELTA 此处存储的就是增量 (Delta) 数据,此时如果要取得全量数据的话,需要递归的找到最 Base Object,然后 apply delta 数据,在 base object 基础上进行 apply delta 数据也是非常精妙的,此文暂不做介绍。
从上面可以很清晰知道 pack 文件格式,我们再从本地仓库中一探究竟:
不是增量 delta 格式:
SHA-1 type size size-in-packfile offset-in-packfile
增量 delta 格式:
SHA-1 type size size-in-packfile offset-in-packfile depth base-SHA-1
 git verify-pack -v pack-efbf3149604d24e6ea427b025da0c59245b2c2ea.packcb5a93c4cf9c0ee5b7153a3a35a4fac7a7584804 commit 275 189 12399334856af4ca4b49c0008a25b6a9f524e40350 commit 69 81 201 1 cb5a93c4cf9c0ee5b7153a3a35a4fac7a7584804e0efbd5121c31964af1615cf24135a7c6c11cc1d commit 268 187 2827bc9a5e0199bd4a6d4d223ce7e13239631df9635 commit 29 41 469 1 e0efbd5121c31964af1615cf24135a7c6c11cc1d2e43c62f6ff99c88d20329487137f8dbabc8b3ec commit 220 157 510b6f173085f49f109a00b2a3f08a7dc499cc47f1f commit 220 157 6670466b3f1aadde74234f7dd3f4ef7f1505c50fb0c commit 220 157 82476c5e45f8e295226b1bc5c8c7e2bc98d7eae6be1 commit 74 85 981 1 b6f173085f49f109a00b2a3f08a7dc499cc47f1f2729f1fa896d384b49a2f5c53d483eacc0929ebb commit 172 127 10663cc58df83752123644fef39faab2393af643b1d2 blob   2 11 119362189d1a10cc2a544c4e5b9c4aba9493cf5782dc blob   8 15 1204a9a5aecf429fd8a0d81fbd5fd37006bfa498d5c1 blob   4 13 12192b8982f7c281964658d2cd8b6c17b541533dd277 tree   104 105 123292c4aafa39ee387a1f8237f00c78c499aebaf0b2 tree   104 105 1337223b7836fb19fdf64ba2d3cd6173c6a283141f78 blob   2 11 14421756ca64f21724f350fe2cc5cfb218883e314c3d tree   71 80 1453e11ddfa79f01b01a8e1553bbffaa2d6c03ae9f6e tree   71 80 1533f70f10e4db19068f79bc43844b49f3eece45c4e8 blob   2 11 1613e982b6207b10a869164e2c8d19d25ffb059e6a16 tree   66 73 1624f2e9f73f27124916344e0fd03bb449bc6feca59d tree   66 74 1697d09da444f461d7cee3679666a1ded5ab79832ed0 tree   33 44 1771non delta: 18 objectschain length = 1: 3 objectspack-efbf3149604d24e6ea427b025da0c59245b2c2ea.pack: ok
如 399334856af4ca4b49c0008a25b6a9f524e40350(SHA-1) 表示对象的 base object SHA-1 是 cb5a93c4cf9c0ee5b7153a3a35a4fac7a7584804,base 对象最大深度 (depth) 为 1,如果 cb5a93c4cf9c0ee5b7153a3a35a4fac7a7584804 还有引用对象,则改变 depth 为 2。
pack Header 中最后 4-bytes 用于表示的 pack 文件中 objects 的数量,最多 2 的 32 次方个对象,所以一些大的工程中有多个 pack 文件和多个 idx 文件。
文件的 size (文件解压缩后大小) 有什么用呢,这个是为了方便我们进行解压的时候,设置流的大小,也就是方便知道流有多大。这里 size 不是说明下一个文件的偏移量,偏移量都是来自索引文件,见下面 idx:
index 文件
 

一文讲透 Git 底层数据结构和原理

由于 version1 比较简单,下面用 version2 为例子:
分层模式:Header,Fanout,SHA,CRC,Offset,Big File Offset,Trailer。
Header 层
 
version2 的 Header 部分总共有 8-bytes,version 1 的 header 部分是没有的,前 4-bytes 总是 255, 116, 79, 99 因为这个也是版本 1 的开头四个字节,后面 4-bytes 用于表示的是版本号,在当前就是 version 2。
Fanout 层
 
fanout 层是 git 的亮点设计,也叫 Fanout Table(扇表)。fanout 数组中存储的是相关对象的数目,数组下标是对应 16 进制数。fanout 最后一个存储的是整个 pack 文件中所有对象的总数量。Fanout Table 是整个 git 检索的核心,通过它我们可以快速进行查询,用于定位 SHA 层的数组起始 – 终止下标,定位好 SHA 层范围之后,就可以对 SHA 层进行二分查找了,而不用对所有对象进行二分查找。
fanout 总共 256 个,刚好是十六进制的 #0xFF。fanout 数组用 SHA 的前面 2 个字符作为下标(对应 .git/objects 中的松散文件目录名,将 16 进制的目录名转换 10 进制数字),里面值就是用这两个字符开头的文件数量,而且是逐层累加的,后面的数组数量是包含前面数组的数据的个数的一个累加。

一文讲透 Git 底层数据结构和原理

举例如下:
1)如果数组下标为 0,且 Fanout[0] = 10 代表着 #0x00 开头的 SHA-1 值的总数为  10 个。
2) 如果数组下标为 1,且 Fanout[1] = 15 代表着小于 #0x01 开头的 SHA-1 值的总数为 15 个,从 Fanout[0] = 10 知 Fanout[1] = (15-10)
为什么 git 设计上 Fanout[n] 会累加 Fanout[n-1] 的数量?这个主要是为了快速确定 SHA 层检索的初始位置,而不用每次去把前面所有 fanout[..n-1] 数量进行累加。
SHA 层
 
是所有对象的 SHA-1 的排序,按照名称排序,按照名称进行排序是为了用二分搜索进行查找。每个 SHA-1 值占 20-bytes。
CRC 层
 
由于文件打包主要解决网络传输问题,网络传输的时候必须通过 crc 进行校验,避免传输过程中的文件损坏。CRC 数组对应的是每个对象的 CRC 校验和。
Offset 层
 
是由 4 byte 字节所组成,表示的是每个 SHA-1 文件的偏移量,但是如果文件大于 2G 之后,4 byte 字节将无法表示,此时将:
4 byte 中的第一 bit 就是 MSB,如果是 1 表示的是文件的偏移量是放在第 6 层去存储,此时剩下的 31-bits 将表示文件在 Big File Offset 中的偏移量,也就是图中的,通过 Big File Offset 层 就可以知道对象在 pack 中的 offset。
 
4 byte 中的第一 bit 就是 MSB,如果是 0 31-bits 表示的存储对象在 packfile 中的文件偏移量,此时不涉及 Big File Offset 层
 
Big File Offset 层
 
用于存储大于 2G 的文件的偏移量。如果文件大于 2G,可以通过 offset 层最后 31 bits 决定在 big file offset 中的位置,big file offset 通过 8 bytes 来表示对象在 pack 文件中的位置,理论上可以表示 2 的 64 次方文件大小。
Trailer 层
 
包含的是 packfile checksum 和关联的 idx 的 checksum。
索引流程
 
从上面的分层知道 git 设计的巧妙。git 索引文件偏移量的查询流程如下:

一文讲透 Git 底层数据结构和原理

查询算法
 
通过 idx 文件查询 SHA-1 对应的偏移量:
 

一文讲透 Git 底层数据结构和原理

在 pack 文件中通过偏移量找到对象:
 

一文讲透 Git 底层数据结构和原理

如果是普通的存储类型。定位到的对象就是用 Zlib 压缩之后的对象,直接解压缩即可。
如果是 Delta 类型需要 递归查出 Delta 的 Base 对象,然后再把 delta data 应用到 base object 上(可参考 git-apply-delta)
参考资料
 
git 大多资料主要介绍是 git 使用,很少系统去讲解底层数据结构和原理。本文通过多个开源代码入手,结合 git 文档,参考相关 git 开发者或相关研究文章,git 邮件列表等。下面是我探究觉得比较可靠的资料文档集。
 
参考文档
https://stackoverflow.com/questions/8198105/how-does-git-store-files
https://www.npmjs.com/package/git-apply-delta
https://git-scm.com/book/en/v2/Git-Internals-Packfiles
https://codewords.recurse.com/issues/three/unpacking-git-packfiles
http://shafiulazam.com/gitbook/7_the_packfile.html
http://wiki.jikexueyuan.com/project/git-community-book/packfile.html
http://documentup.com/skwp/git-workflows-book
http://www.runoob.com/git/git-workspace-index-repo.html
http://shafiulazam.com/gitbook/1_the_git_object_model.html
http://eagain.net/articles/git-for-computer-scientists/
https://www.kernel.org/pub/software/scm/git/docs/user-manual.html#object-details
https://stackoverflow.com/documentation/git/topics
https://stackoverflow.com/search?page=2&tab=Votes&q=user%3a1256452%20%5bgit%5d
http://git.oschina.net/progit/9-Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86.html#9.5-The-Refspec
https://codewords.recurse.com/issues/three/unpacking-git-packfiles
http://shafiulazam.com/gitbook/7_the_packfile.html
https://w.org/pub/software/scm/git/docs/user-manual.html#object-details
git 源码
 
sha1_file.c sha1_object_info_extended 读取对象
sha1_file.c find_pack_entry_one 从索引中寻找
 
其他 git 源码
 
go-git https://github.com/src-d/go-git
gitgo https://github.com/ChimeraCoder/gitgo

转自:https://mp.weixin.qq.com/s/l5JU9e6_HrS_-ixiBIrqsA

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

 

来源:机器之心

git merge、git rebase、git reset、git revert、git fetch、git pull、git reflog……你知道这些 git 命令执行的究竟是什么任务吗?如果你还有些分不清楚,那千万不能错过这篇文章。在本文中,熟知 JavaScript、TypeScript、GraphQL、Serverless、AWS、Docker 和 Golang 的 21 岁年轻软件顾问 Lydia Hallie 通过动图形式直观地介绍了这些常用 git 命令的工作过程,包你过目不忘。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

尽管 Git 是一款非常强大的工具,但如果我说 Git 用起来简直是噩梦,大多数人也会认同我的说法。我发现在使用 Git 时,在头脑里可视化地想象它会非常有用:当我执行一个特定命令时,这些分支会如何交互,又会怎样影响历史记录?为什么当我在 master 上执行硬重启,force push 到原分支以及 rimraf 我们的 .git 文件夹时,我的同事哭了?
我觉得创建一些最常用且最有用的 Git 命令的可视化示例会是一个完美的用例!下面我将介绍的很多命令都有可选参数——你可以使用这些参数来改变对应命令的行为。而我的示例只会涵盖命令的默认行为,而不会添加(或添加太多)可选配置!

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

本文作者Lydia Hallie。
合并
拥有多个分支是很方便的,这样可以将不同的新修改互相隔离开,而且还能确保你不会意外地向生产代码推送未经许可或破损的代码修改。但一旦这些修改得到了批准许可,我们就需要将其部署到我们的生产分支中!
可将一个分支的修改融入到另一个分支的一种方式是执行 git merge。Git 可执行两种类型的合并:fast-forward 和 no-fast-forward。现在你可能分不清,但我们马上就来看看它们的差异所在。
Fast-forward (—ff)
在当前分支相比于我们要合并的分支没有额外的提交(commit)时,可以执行 fast-forward 合并。Git 很懒,首先会尝试执行最简单的选项:fast-forward!这类合并不会创建新的提交,而是会将我们正在合并的分支上的提交直接合并到当前分支。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

完美!现在,我们在 dev 分支上所做的所有改变都合并到了 master 分支上。那么 no-fast-forward 又是什么意思呢?
No-fast-foward (—no-ff)
如果你的当前分支相比于你想要合并的分支没有任何提交,那当然很好,但很遗憾现实情况很少如此!如果我们在当前分支上提交我们想要合并的分支不具备的改变,那么 git 将会执行 no-fast-forward 合并。
使用 no-fast-forward 合并时,Git 会在当前活动分支上创建新的 merging commit。这个提交的父提交(parent commit)即指向这个活动分支,也指向我们想要合并的分支!

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

没什么大不了的,完美的合并!现在,我们在 dev 分支上所做的所有改变都合并到了 master 分支上。
合并冲突
尽管 Git 能够很好地决定如何合并分支以及如何向文件添加修改,但它并不总是能完全自己做决定。当我们想要合并的两个分支的同一文件中的同一行代码上有不同的修改,或者一个分支删除了一个文件而另一个分支修改了这个文件时,Git 就不知道如何取舍了。
在这样的情况下,Git 会询问你想要保留哪种选择?假设在这两个分支中,我们都编辑了 README.md 的第一行。
工作流一目了然,看小姐姐用动图展示 10 大 Git 命令
如果我们想把 dev 合并到 master,就会出现一个合并冲突:你想要标题是 Hello! 还是 Hey!?

当尝试合并这些分支时,Git 会向你展示冲突出现的位置。我们可以手动移除我们不想保留的修改,保存这些修改,再次添加这个已修改的文件,然后提交这些修改。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

完成!尽管合并冲突往往很让人厌烦,但这是合理的:Git 不应该瞎猜我们想要保留哪些修改。

变基(Rebasing)
我们刚看到可通过执行 git merge 将一个分支的修改应用到另一个分支。另一种可将一个分支的修改融入到另一个分支的方式是执行 git rebase。
git rebase 会将当前分支的提交复制到指定的分支之上。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

完美,现在我们在 dev 分支上获取了 master 分支上的所有修改。
变基与合并有一个重大的区别:Git 不会尝试确定要保留或不保留哪些文件。我们执行 rebase 的分支总是含有我们想要保留的最新近的修改!这样我们不会遇到任何合并冲突,而且可以保留一个漂亮的、线性的 Git 历史记录。
上面这个例子展示了在 master 分支上的变基。但是,在更大型的项目中,你通常不需要这样的操作。git rebase 在为复制的提交创建新的 hash 时会修改项目的历史记录。
如果你在开发一个 feature 分支并且 master 分支已经更新过,那么变基就很好用。你可以在你的分支上获取所有更新,这能防止未来出现合并冲突。
交互式变基(Interactive Rebase)
在为提交执行变基之前,我们可以修改它们!我们可以使用交互式变基来完成这一任务。交互式变基在你当前开发的分支上以及想要修改某些提交时会很有用。

在我们正在 rebase 的提交上,我们可以执行以下 6 个动作:
  • reword:修改提交信息;

  • edit:修改此提交;

  • squash:将提交融合到前一个提交中;

  • fixup:将提交融合到前一个提交中,不保留该提交的日志消息;

  • exec:在每个提交上运行我们想要 rebase 的命令;

  • drop:移除该提交。

很棒!这样我们就能完全控制我们的提交了。如果你想要移除一个提交,只需 drop 即可。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

如果你想把多个提交融合到一起以便得到清晰的提交历史,那也没有问题!

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

交互式变基能为你在 rebase 时提供大量控制,甚至可以控制当前的活动分支。
 
重置(Resetting)

当我们不想要之前提交的修改时,就会用到这个命令。也许这是一个 WIP 提交或者可能是引入了 bug 的提交,这时候就要执行 git reset。

git reset 能让我们不再使用当前台面上的文件,让我们可以控制 HEAD 应该指向的位置。

软重置

软重置会将 HEAD 移至指定的提交(或与 HEAD 相比的提交的索引),而不会移除该提交之后加入的修改!

假设我们不想保留添加了一个 style.css 文件的提交 9e78i,而且我们也不想保留添加了一个 index.js 文件的提交 035cc。但是,我们确实又想要保留新添加的 style.css 和 index.js 文件!这是软重置的一个完美用例。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

输入 git status 后,你会看到我们仍然可以访问在之前的提交上做过的所有修改。这很好,这意味着我们可以修复这些文件的内容,之后再重新提交它们!

硬重置

有时候我们并不想保留特定提交引入的修改。不同于软重置,我们应该再也无需访问它们。Git 应该直接将整体状态直接重置到特定提交之前的状态:这甚至包括你在工作目录中和暂存文件上的修改。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

Git 丢弃了 9e78i 和 035cc 引入的修改,并将状态重置到了 ec5be 的状态。

 

还原(Reverting)
另一种撤销修改的方法是执行 git revert。通过对特定的提交执行还原操作,我们会创建一个包含已还原修改的新提交。

假设 ec5be 添加了一个 index.js 文件。但之后我们发现其实我们再也不需要由这个提交引入的修改了。那就还原 ec5be 提交吧!

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

完美!提交 9e78i 还原了由提交 ec5be 引入的修改。在撤销特定的提交时,git revert 非常有用,同时也不会修改分支的历史。
拣选(Cherry-picking)
当一个特定分支包含我们的活动分支需要的某个提交时,我们对那个提交执行 cherry-pick!对一个提交执行 cherry-pick 时,我们会在活动分支上创建一个新的提交,其中包含由拣选出来的提交所引入的修改。

假设 dev 分支上的提交 76d12 为 index.js 文件添加了一项修改,而我们希望将其整合到 master 分支中。我们并不想要整个 dev 分支,而只需要这个提交!

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

现在 master 分支包含 76d12 引入的修改了。
 
取回(Fetching)
如果你有一个远程 Git 分支,比如在 GitHub 上的分支,当远程分支上包含当前分支没有的提交时,可以使用取回。比如当合并了另一个分支或你的同事推送了一个快速修复时。

通过在这个远程分支上执行 git fetch,我们就可在本地获取这些修改。这不会以任何方式影响你的本地分支:fetch 只是单纯地下载新的数据而已。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

现在我们可以看到自上次推送以来的所有修改了。这些新数据也已经在本地了,我们可以决定用这些新数据做什么了。
 
拉取(Pulling)
尽管 git fetch 可用于获取某个分支的远程信息,但我们也可以执行 git pull。git pull 实际上是两个命令合成了一个:git fetch 和 git merge。当我们从来源拉取修改时,我们首先是像 git fetch 那样取回所有数据,然后最新的修改会自动合并到本地分支中。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

很好,我们现在与远程分支完美同步了,并且也有了所有最新的修改!
 
Reflog
每个人都会犯错,但犯错其实没啥!有时候你可能感觉你把 git repo 完全搞坏了,让你想完全删了了事。

git reflog 是一个非常有用的命令,可以展示已经执行过的所有动作的日志。包括合并、重置、还原,基本上包含你对你的分支所做的任何修改。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

如果你犯了错,你可以根据 reflog 提供的信息通过重置 HEAD 来轻松地重做!

假设我们实际上并不需要合并原有分支。当我们执行 git reflog 命令时,我们可以看到这个 repo 的状态在合并前位于 HEAD@{1}。那我们就执行一次 git reset,将 HEAD 重新指向在 HEAD@{1} 的位置。

工作流一目了然,看小姐姐用动图展示 10 大 Git 命令

我们可以看到最新的动作已被推送给 reflog。

原文链接:https://dev.to/lydiahallie/cs-visualized-useful-git-commands-37p1

真香!GitHub 核心功能都免费开放了

4 月 15 日,凌晨收到 GitHub 官方发来的邮件,宣布 GitHub 的所有核心功能对所有人都免费

真香!GitHub 核心功能都免费开放了


1.  此前私有仓库对已经对个人免费,从今天起现在也对团队账号免费,并且取消了协作者的人数限制;

这意味着,团队现在可以在一个地方管理他们的工作:CI/CD、项目管理、代码审查、包,等等。GitHub 称,希望每个人都能在开发者喜欢的平台上发布优秀的软件。

 

真香!GitHub 核心功能都免费开放了

 

此外,如果你们团队需要高级功能(如「Code owners」)、企业功能(如 SAML) 或个性化支持,那还是要使用 GitHub 的付费计划咯。PS:「Code owners」用来识别存储在库中的代码拥有者,查看其做所做的更改。


2. 收费服务的费用下调

 

> GitHub Team 的月费从 9 刀/人降到 4 刀/人;

> GitHub Pro 的月费从 7 刀到 4 刀;

真香!GitHub 核心功能都免费开放了

从今天起,费用和 Plan 方面的详细变动:

  • 面向组织的 GitHub Free 立即可用,不再限制私有仓库的人数;

  • 以前用 Team for Open Source 的所有组织,直接转成 GitHub Free;

  • 面向个人开发人员的 GitHub Free 现在包括无限的协作者;

  • 使用 GitHub Free 的组织和个人将获得 GitHub Community Support;

  • GitHub Pro 现在将包括 2GB 的 Packages 存储和 10GB 的数据传输,月费降至 4 美元;

  • GitHub Team 单个用户的月费用降至 4 美元;

  • 自 5 月 14 日起,GitHub Team 每月将为私有仓库新增到 3000 分钟的 Actions 时间;

如果你在今天之前已经买了 GitHub Pro 或 Team 的年费,那将在年度账单到期之前,按比例退还差价。例如,你已经订阅了 2 个月,并且是按年计费的,你将会收到剩余 10 个月差价的退款。退款将在大约 30 天内处理。

有网友感慨说,自从有了微软这个富爸爸后,GitHub 不差钱了。

转自:https://mp.weixin.qq.com/s/cBhuc8xqVqGAfpMCc9kebQ