文末有全部内容的下载链接
git
的操作可以通过命令的形式如执行,日常使用就如下图6个命令即可
实际上,如果想要熟练使用,超过60多个命令需要了解,下面则介绍下常见的的git
命令
Git
自带一个 git config
的工具来帮助设置控制 Git
外观和行为的配置变量,在我们安装完git
之后,第一件事就是设置你的用户名和邮件地址
后续每一个提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改
设置提交代码时的用户信息命令如下:
一个git
项目的初始有两个途径,分别是:
在日常工作中,代码常用的基本操作如下:
关于提交信息的格式,可以遵循以下的规则:
远程操作常见的命令:
git checkout [file] 恢复暂存区的指定文件到工作区
git checkout [commit] [file] 恢复某个commit的指定文件到暂存区和工作区
git checkout . 恢复暂存区的所有文件到工作区
git reset [commit] 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
git reset --hard 重置暂存区与工作区,与上一次commit保持一致
git reset [file] 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
git revert [commit] 后者的所有变化都将被前者抵消,并且应用到当前分支
reset
:真实硬性回滚,目标版本后面的提交记录全部丢失了
revert
:同样回滚,这个回滚操作相当于一个提价,目标版本后面的提交记录也全部都有
你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作,但又不想提交这些杂乱的代码,这时候可以将代码进行存储
git stash 暂时将未提交的变化移除
git stash pop 取出储藏中最后存入的工作状态进行恢复,会删除储藏
git stash list 查看所有储藏中的工作
git stash apply <储藏的名称> 取出储藏中对应的工作状态进行恢复,不会删除储藏
git stash clear 清空所有储藏中的工作
git stash drop <储藏的名称> 删除对应的某个储藏
git
常用命令速查表如下所示:
一般情况下,出现分支的场景有如下:
具体情况就是,多个分支修改了同一个文件(任何地方)或者多个分支修改了同一个文件的名称
如果两个分支中分别修改了不同文件中的部分,是不会产生冲突,直接合并即可
应用在命令中,就是push
、pull
、stash
、rebase
等命令下都有可能产生冲突情况,从本质上来讲,都是merge
和patch
(应用补丁)时产生冲突
在本地主分值master
创建一个a.txt
文件,文件起始位置写上master commit
,如下:
然后提交到仓库:
创建一个新的分支featurel1
分支,并进行切换,如下:
git checkout -b featurel1
然后修改a.txt
文件首行文字为 featurel commit
,然后添加到暂存区,并开始进行提交到仓库:
然后通过git checkout master
切换到主分支,通过git merge
进行合并,发现不会冲突
此时a.txt
文件的内容变成featurel commit
,没有出现冲突情况,这是因为git
在内部发生了快速合并
如果当前分支的每一个提交(commit)都已经存在另一个分支里了,git 就会执行一个“快速向前”(fast forward)操作
git 不创建任何新的提交(commit),只是将当前分支指向合并进来的分支
如果此时切换到featurel
分支,将文件的内容修改成featrue second commit
,然后提交到本地仓库
然后切换到主分支,如果此时在a.txt
文件再次修改,修改成mastet second commit
,然后再次提交到本地仓库
此时,master
分支和feature1
分支各自都分别有新的提交,变成了下图所示:
这种情况下,无法执行快速合并,只能试图把各自的修改合并起来,但这种合并就可能会有冲突
现在通过git merge featurel
进行分支合并,如下所示:
从冲突信息可以看到,a.txt
发生冲突,必须手动解决冲突之后再提交
而git status
同样可以告知我们冲突的文件:
打开a.txt
文件,可以看到如下内容:
git
用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容:
现在要做的事情就是将冲突的内容进行更改,对每个文件使用 git add
命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git
就会将它们标记为冲突已解决然后再提交:
此时master
分支和feature1
分支变成了下图所示:
使用git log
命令可以看到合并的信息:
当Git
无法自动合并分支时,就必须首先解决冲突,解决冲突后,再提交,合并完成
解决冲突就是把Git
合并失败的文件手动编辑为我们希望的内容,再提交
fork
,英语翻译过来就是叉子,动词形式则是分叉,如下图,从左到右,一条直线变成多条直线
转到git
仓库中,fork
则可以代表分叉、克隆 出一个(仓库的)新拷贝
包含了原来的仓库(即upstream repository,上游仓库)所有内容,如分支、Tag、提交
如果想将你的修改合并到原项目中时,可以通过的 Pull Request 把你的提交贡献回 原仓库
clone
,译为克隆,它的作用是将文件从远程代码仓下载到本地,从而形成一个本地代码仓
执行clone
命令后,会在当前目录下创建一个名为xxx
的目录,并在这个目录下初始化一个 .git
文件夹,然后从中读取最新版本的文件的拷贝
默认配置下远程 Git
仓库中的每一个文件的每一个版本都将被拉取下来
branch
,译为分支,其作用简单而言就是开启另一个分支, 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线
Git
处理分支的方式十分轻量,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷
在我们开发中,默认只有一条master
分支,如下图所示:
通过git branch
可以创建一个分支,但并不会自动切换到新分支中去
通过git checkout
可以切换到另一个testing
分支
当你在github
发现感兴趣开源项目的时候,可以通过点击github
仓库中右上角fork
标识的按钮,如下图:
点击这个操作后会将这个仓库的文件、提交历史、issues和其余东西的仓库复制到自己的github
仓库中,而你本地仓库是不会存在任何更改
然后你就可以通过git clone
对你这个复制的远程仓库进行克隆
后续更改任何东西都可以在本地完成,如git add
、git commit
一系列的操作,然后通过push
命令推到自己的远程仓库
如果希望对方接受你的修改,可以通过发送pull requests
给对方,如果对方接受。则会将你的修改内容更新到仓库中
整体流程如下图:
在github
中,开源项目右侧存在code
按钮,点击后则会显示开源项目url
信息,如下图所示:
通过git clone xxx
则能完成远程项目的下载
可通过git branch
进行查看当前的分支状态,
如果给了--list
,或者没有非选项参数,现有的分支将被列出;当前的分支将以绿色突出显示,并标有星号
以及通过git branch
创建一个新的分支出来
其三者区别如下:
先回顾两个命令的定义
再来看一次git
的工作流程图,如下所示:
可以看到,git fetch
是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中
而git pull
则是将远程主机的最新内容拉下来后直接合并,即:git pull = git fetch + git merge
,这样可能会产生冲突,需要手动解决
在我们本地的git
文件中对应也存储了git
本地仓库分支的commit ID
和 跟踪的远程分支的commit ID
,对应文件如下:
使用 git fetch
更新代码,本地的库中master
的commitID
不变
但是与git
上面关联的那个orign/master
的commit ID
发生改变
这时候我们本地相当于存储了两个代码的版本号,我们还要通过merge
去合并这两个不同的代码版本
也就是fetch
的时候本地的master
没有变化,但是与远程仓关联的那个版本号被更新了,接下来就是在本地merge
合并这两个版本号的代码
相比之下,使用git pull
就更加简单粗暴,会将本地的代码更新至远程仓库里面最新的代码版本,如下图:
一般远端仓库里有新的内容更新,当我们需要把新内容下载的时候,就使用到git pull
或者git fetch
命令
用法如下:
git fetch <远程主机名> <远程分支名>:<本地分支名>
例如从远程的origin
仓库的master
分支下载代码到本地并新建一个temp
分支
git fetch origin master:temp
如果上述没有冒号,则表示将远程origin
仓库的master
分支拉取下来到本地当前分支
这里git fetch
不会进行合并,执行后需要手动执行git merge
合并,如下:
git merge temp
两者的用法十分相似,pull
用法如下:
git pull <远程主机名> <远程分支名>:<本地分支名>
例如将远程主机origin
的master
分支拉取过来,与本地的branchtest
分支合并,命令如下:
git pull origin master:branchtest
同样如果上述没有冒号,则表示将远程origin
仓库的master
分支拉取下来与本地当前分支合并
相同点:
不同点:
在使用 git
进行版本管理的项目中,当完成一个特性的开发并将其合并到 master
分支时,会有两种方式:
git rebase
与 git merge
都有相同的作用,都是将一个分支的提交合并到另一分支上,但是在原理上却不相同
用法上两者也十分的简单:
将当前分支合并到指定分支,命令用法如下:
git merge xxx
将当前分支移植到指定分支或指定commit
之上,用法如下:
git rebase -i
常见的参数有--continue
,用于解决冲突之后,继续执行rebase
git rebase --continue
通过git merge
将当前分支与xxx
分支合并,产生的新的commit
对象有两个父节点
如果“指定分支”本身是当前分支的一个直接子节点,则会产生快照合并
举个例子,bugfix
分支是从master
分支分叉出来的,如下所示:
合并 bugfix
分支到master
分支时,如果master
分支的状态没有被更改过,即 bugfix
分支的历史记录包含master
分支所有的历史记录
所以通过把master
分支的位置移动到bugfix
的最新分支上,就完成合并
如果master
分支的历史记录在创建bugfix
分支后又有新的提交,如下情况:
这时候使用git merge
的时候,会生成一个新的提交,并且master
分支的HEAD
会移动到新的分支上,如下:
从上面可以看到,会把两个分支的最新快照以及二者最近的共同祖先进行三方合并,合并的结果是生成一个新的快照
同样,master
分支的历史记录在创建bugfix
分支后又有新的提交,如下情况:
通过git rebase
,会变成如下情况:
在移交过程中,如果发生冲突,需要修改各自的冲突,如下:
rebase
之后,master
的HEAD
位置不变。因此,要合并master
分支和bugfix
分支
从上面可以看到,rebase
会找到不同的分支的最近共同祖先,如上图的B
然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件(老的提交X
和Y
也没有被销毁,只是简单地不能再被访问或者使用)
然后将当前分支指向目标最新位置D
, 然后将之前另存为临时文件的修改依序应用
从上面可以看到,merge
和rebasea
都是合并历史记录,但是各自特性不同:
通过merge
合并分支会新增一个merge commit
,然后将两个分支的历史联系起来
其实是一种非破坏性的操作,对现有分支不会以任何方式被更改,但是会导致历史记录相对复杂
rebase
会将整个分支移动到另一个分支上,有效地整合了所有分支上的提交
主要的好处是历史记录更加清晰,是在原有提交的基础上将差异内容反映进去,消除了 git merge
所需的不必要的合并提交
reset
用于回退版本,可以遗弃不再使用的提交
执行遗弃时,需要根据影响的范围而指定不同的参数,可以指定是否复原索引或工作树内容
在当前提交后面,新增一次提交,抵消掉上一次提交导致的所有变化,不会改变过去的历史,主要是用于安全地取消过去发布的提交
当没有指定ID
的时候,默认使用HEAD
,如果指定ID
,那么就是基于指向ID
去变动暂存区或工作区的内容
// 没有指定ID, 暂存区的内容会被当前ID版本号的内容覆盖,工作区不变
git reset
// 指定ID,暂存区的内容会被指定ID版本号的内容覆盖,工作区不变
git reset
日志ID
可以通过查询,可以git log
进行查询,如下:
commit a7700083ace1204ccdff9f71631fb34c9913f7c5 (HEAD -> master)
Author: linguanghui
Date: Tue Aug 17 22:34:40 2021 +0800
second commit
commit e31118663ce66717edd8a179688a7f3dde5a9393
Author: linguanghui
Date: Tue Aug 17 22:20:01 2021 +0800
first commit
常见命令如下:
–mixed(默认):默认的时候,只有暂存区变化
–hard参数:如果使用 --hard 参数,那么工作区也会变化
–soft:如果使用 --soft 参数,那么暂存区和工作区都不会变化
跟git reset
用法基本一致,git revert
撤销某次操作,此次操作之前和之后的 commit
和history
都会保留,并且把这次撤销,作为一次最新的提交,如下:
git revert
如果撤销前一个版本,可以通过如下命令:
git revert HEAD
撤销前前一次,如下:
git revert HEAD^
撤销(revert)被设计为撤销公开的提交(比如已经push)的安全方式,git reset
被设计为重设本地更改
因为两个命令的目的不同,它们的实现也不一样:重设完全地移除了一堆更改,而撤销保留了原来的更改,用一个新的提交来实现撤销
两者主要区别如下:
git revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch时,之前提交合并的代码仍然存在,导致不能够重新合并
但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入
git revert
, 如果分支是提错了没用的并且不想让别人发现这些错误代码,则使用git reset
stash,译为存放,在 git 中,可以理解为保存当前工作进度,会把暂存区和工作区的改动进行保存,这些修改会保存在一个栈上
后续你可以在任何时候任何分支重新将某次的修改推出来,重新应用这些更改的代码
默认情况下,git stash
会缓存下列状态的文件:
但以下状态的文件不会缓存:
如果想要上述的文件都被缓存,可以使用-u
或者--include-untracked
可以工作目录新的文件,使用-a
或者--all
命令可以当前目录下的所有修改
关于git stash
常见的命令如下:
git stash
git stash save
git stash list
git stash pop
git stash apply
git stash show
git stash drop
git stash clear
保存当前工作进度,会把暂存区和工作区的改动保存起来
git stash save
可以用于存储修改.并且将git
的工作状态切回到HEAD
也就是上一次合法提交上
如果给定具体的文件路径,git stash
只会处理路径下的文件.其他的文件不会被存储,其存在一些参数:
–keep-index 或者 -k 只会存储为加入 git 管理的文件
–include-untracked 为追踪的文件也会被缓存,当前的工作空间会被恢复为完全清空的状态
-a 或者 --all 命令可以当前目录下的所有修改,包括被 git 忽略的文件
显示保存进度的列表。也就意味着,git stash
命令可以多次执行,当多次使用git stash
命令后,栈里会充满未提交的代码,如下:
其中,stash@{0}
、stash@{1}
就是当前stash
的名称
git stash pop
从栈中读取最近一次保存的内容,也就是栈顶的stash
会恢复到工作区
也可以通过 git stash pop
+ stash
名字执行恢复哪个stash
恢复到当前目录
如果从stash
中恢复的内容和当前目录中的内容发生了冲突,则需要手动修复冲突或者创建新的分支来解决冲突
将堆栈中的内容应用到当前目录,不同于git stash pop
,该命令不会将内容从堆栈中删除
也就说该命令能够将堆栈的内容多次应用到工作目录中,适应于多个分支的情况
同样,可以通过git stash apply
+ stash
名字执行恢复哪个stash
恢复到当前目录
查看堆栈中最新保存的stash
和当前目录的差异
通过使用git stash show -p
查看详细的不同
通过使用git stash show stash@{1}
查看指定的stash
和当前目录差异
git stash drop
+ stash
名称表示从堆栈中移除某个指定的stash
删除所有存储的进度
当你在项目的一部分上已经工作一段时间后,所有东西都进入了混乱的状态, 而这时你想要切换到另一个分支或者拉下远端的代码去做一点别的事情
但是你创建一次未完成的代码的commit
提交,这时候就可以使用git stash
例如以下场景:
当你的开发进行到一半,但是代码还不想进行提交 ,然后需要同步去关联远端代码时.如果你本地的代码和远端代码没有冲突时,可以直接通过git pull
解决
但是如果可能发生冲突怎么办.直接git pull
会拒绝覆盖当前的修改,这时候就可以依次使用下述的命令:
或者当你开发到一半,现在要修改别的分支问题的时候,你也可以使用git stash
缓存当前区域的代码
git,是一个分布式版本控制软件,最初目的是为更好地管理Linux
内核开发而设计
分布式版本控制系统的客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复
项目开始,只有一个原始版仓库,别的机器可以clone
这个原始版本库,那么所有clone
的机器,它们的版本库其实都是一样的,并没有主次之分
所以在实现团队协作的时候,只要有一台电脑充当服务器的角色,其他每个人都从这个“服务器”仓库clone
一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交
github
实际就可以充当这个服务器角色,其是一个开源协作社区,提供Git
仓库托管服务,既可以让别人参与你的开源项目,也可以参与别人的开源项目
当我们通过git init
创建或者git clone
一个项目的时候,项目目录会隐藏一个.git
子目录,其作用是用来跟踪管理版本库的
Git
中所有数据在存储前都计算校验和,然后以校验和来引用,所以在我们修改或者删除文件的时候,git
能够知道
Git
用以计算校验和的机制叫做 SHA-1 散列(hash,哈希), 这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来,如下:
24b9da6552252987aa493b52f8696cd6d3b00373
当我们修改文件的时候,git
就会修改文件的状态,可以通过git status
进行查询,状态情况如下:
文件状态对应的,不同状态的文件在Git
中处于不同的工作区域,主要分成了四部分:
从上图可以看到,git
日常简单的使用就只有上图6个命令:
但实际上还有很多命令,如果想要熟练使用,还有60个多命令,通过这些命令的配合使用,能够提高个人工作效率和团队协助能力
在git
中,可以存在很多分支,其本质上是一个指向commit
对象的可变指针,而Head
是一个特别的指针,是一个指向你正在工作中的本地分支的指针
简单来讲,就是你现在在哪儿,HEAD 就指向哪儿
例如当前我们处于master
分支,所以HEAD
这个指针指向了master
分支指针
然后通过调用git checkout test
切换到test
分支,那么HEAD
则指向test
分支,如下图:
但我们在test
分支再一次commit
信息的时候,HEAD
指针仍然指向了test
分支指针,而test
分支指针已经指向了最新创建的提交,如下图:
这个HEAD
存储的位置就在.git/HEAD
目录中,查看信息可以看到HEAD
指向了另一个文件
$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
7406a10efcc169bbab17827aeda189aa20376f7f
这个文件的内容是一串哈希码,而这个哈希码正是master
分支上最新的提交所对应的哈希码
所以,当我们切换分支的时候,HEAD
指针通常指向我们所在的分支,当我们在某个分支上创建新的提交时,分支指针总是会指向当前分支的最新提交
所以,HEAD指针 ——–> 分支指针 ——–> 最新提交
在Git
管理下,大家实际操作的目录被称为工作树,也就是工作区域
在数据库和工作树之间有索引,索引是为了向数据库提交作准备的区域,也被称为暂存区域
Git
在执行提交的时候,不是直接将工作树的状态保存到数据库,而是将设置在中间索引区域的状态保存到数据库
因此,要提交文件,首先需要把文件加入到索引区域中。
所以,凭借中间的索引,可以避免工作树中不必要的文件提交,还可以将文件修改内容的一部分加入索引区域并提交
从所在的位置来看:
HEAD 指针通常指向我们所在的分支,当我们在某个分支上创建新的提交时,分支指针总是会指向当前分支的最新提交
工作树是查看和编辑的(源)文件的实际内容
索引是放置你想要提交给 git仓库文件的地方,如工作树的代码通过 git add 则添加到 git 索引中,通过git commit 则将索引区域的文件提交到 git 仓库中
版本控制(Version control),是维护工程蓝图的标准作法,能追踪工程蓝图从诞生一直到定案的过程。此外,版本控制也是一种软件工程技巧,借此能在软件开发的过程中,确保由不同人所编辑的同一程序文件都得到同步
透过文档控制,能记录任何工程项目内各个模块的改动历程,并为每次改动编上序号
一种简单的版本控制形式如下:赋给图的初版一个版本等级“A”。当做了第一次改变后,版本等级改为“B”,以此类推
版本控制能提供项目的设计者,将设计恢复到之前任一状态的选择权
简言之,你的修改只要提到到版本控制系统,基本都可以找回,版本控制系统就像一台时光机器,可以让你回到任何一个时间点
版本控制系统在当今的软件开发中,被认为是理所当然的配备工具之一,根据类别可以分成:
结构如下图所示:
优点:
缺点:
结构如下图所示:
优点:
缺点:
代表工具有SVN
、CVS
:
TortoiseSVN
是一款非常易于使用的跨平台的 版本控制/版本控制/源代码控制软件
CVS
是版本控制系统,是源配置管理(SCM)的重要组成部分。使用它,您可以记录源文件和文档的历史记录
老牌的版本控制系统,它是基于客户端/服务器的行为使得其可容纳多用户,构成网络也很方便
这一特性使得CVS
成为位于不同地点的人同时处理数据文件(特别是程序的源代码)时的首选
结构如下图:
优点:
分布式版本管理系统每个计算机都有一个完整的仓库,可本地提交,可以做到离线工作,则不用像集中管理那样因为断网情况而无法工作
代表工具为Git
、HG
:
Git
是目前世界上最先进的分布式版本控制系统,旨在快速高效地处理从小型到大型项目的所有事务
特性:易于学习,占用内存小,具有闪电般快速的性能
使用Git
和Gitlab
搭建版本控制环境是现在互联网公司最流行的版本控制方式
Mercurial
是一个免费的分布式源代码管理工具。它可以有效地处理任何规模的项目,并提供简单直观的界面
Mercurial
是一种轻量级分布式版本控制系统,采用 Python
语言实现,易于学习和使用,扩展性强
版本控制系统的优点如下:
https://download.csdn.net/download/suli77/87411227