Git 版本控制
Git 配置
查询配置信息
- 列出当前配置:
git config --list
; - 列出repository配置:
git config --local --list
; - 列出全局配置:
git config --global --list
; - 列出系统配置:
git config --system --list
;
其他配置
- 配置解决冲突时使用哪种差异分析工具,比如要使用vimdiff:
git config --global merge.tool vimdiff
; - 配置git命令输出为彩色的:
git config --global color.ui auto
; - 配置git使用的文本编辑器:
git config --global core.editor vi
;
Git相关的配置文件:
Git\etc\gitconfig
C:\Users\ [用户名称] \ .gitconfig
设置用户名与邮箱(全局配置)
git config --global user.name "[name]" |
Git 基本理论
四个工作区域
- 工作区(Workspace):本地电脑存放项目文件的地方。
- 暂存区(Index/Stage):用于临时存放你的改动,事实上它只是一个文件,保存即将提交到文件列表信息。在使用git管理项目文件的时候,其本地的项目文件会多出一个.git的文件夹,将这个.git文件夹称之为版本库。其中.git文件夹中包含了两个部分,一个是暂存区(Index或者Stage),顾名思义就是暂时存放文件的地方,通常使用add命令将工作区的文件添加到暂存区里;
- 本地仓库(Repository):安全存放数据的位置,这里面有你提交到所有版本的数据。其中HEAD指向最新放入仓库的版本。即.git文件夹里还包括git自动创建的master分支,并且将HEAD指针指向master分支。使用commit命令可以将暂存区中的文件添加到本地仓库中;
- 远程仓库(Remote):将项目代码托管在远程git服务器上。
本地的三个区域确切的说应该是git仓库中HEAD指向的版本。
Directory:使用Git管理的一个目录,也就是一个仓库,包含我们的工作空间和Git的管理空间。
WorkSpace:需要通过Git进行版本控制的目录和文件,这些目录和文件组成了工作空间。
.git:存放Git管理信息的目录,初始化仓库的时候自动创建。
Index/Stage:暂存区,或者叫待提交更新区,在提交进入repo之前,我们可以把所有的更新放在暂存区。
Local Repo:本地仓库,一个存放在本地的版本库;HEAD会只是当前的开发分支(branch)。
Stash:隐藏,是一个工作状态保存栈,用于保存/恢复WorkSpace中的临时状态。
工作流程
Git的工作流程一般是这样的:
1. 在工作目录中添加、修改文件;
2. 将需要进行版本管理的文件放入暂存区域;
3. 将暂存区域的文件提交到git仓库。
因此,git管理的文件有三种状态:已修改(modified),已暂存(staged),已提交(committed)
日常开发时代码实际上放置在工作区中,通过add等这些命令将代码文教提交给暂存区(Index/Stage),也就意味着代码全权交给了git进行管理,之后通过commit等命令将暂存区提交给master分支上,也就是意味打了一个版本,也可以说代码提交到了本地仓库中。另外,团队协作过程中自然而然还涉及到与远程仓库的交互。
Git 项目搭建
创建工作目录
工作目录(WorkSpace)一般就是你希望Git帮助你管理的文件夹,可以是你项目的目录,也可以是一个空目录,建议不要有中文。
本地仓库搭建
(创建本地仓库的方法有两种:一种是创建全新的仓库,另一种是克隆远程仓库。)
1.创建全新的仓库,需要用GIT管理的项目的根目录执行:
在当前目录新建一个Git代码库git init
执行后可以看到,仅仅在项目目录多出了一个.git目录,关于版本等的所有信息都在这个目录里面。
2.克隆远程仓库
克隆远程目录,是将远程服务器上的仓库完全镜像一份至本地。
克隆一个项目和它的整个代码历史(版本信息)git clone [url]
查看文件状态
查看指定文件状态git status [filename]
查看所有文件状态git status
版本控制就是对文件的版本控制,要对文件进行修改、提交等操作,首先要知道文件当前在什么状态,不然可能会提交了现在还不想提交的文件,或者要提交的文件没提交上。
- Untracked: 未跟踪, 此文件在文件夹中, 但并没有加入到git库, 不参与版本控制. 通过git add 状态变为Staged.
- Unmodify: 文件已经入库, 未修改, 即版本库中的文件快照内容与文件夹中完全一致. 这种类型的文件有两种去处, 如果它被修改, 而变为Modified. 如果使用git rm移出版本库, 则成为Untracked文件
- Modified: 文件已修改, 仅仅是修改, 并没有进行其他的操作. 这个文件也有两个去处, 通过git add可进入暂存staged状态, 使用git checkout 则丢弃修改过, 返回到unmodify状态, 这个git checkout即从库中取出文件, 覆盖当前修改 !
- Staged: 暂存状态. 执行git commit则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为Unmodify状态. 执行git reset HEAD filename取消暂存, 文件状态为Modified
比较工作区中当前文件和暂存区之间的差异,也就是修改之后还没有暂存的内容:git diff;指定文件在工作区和暂存区上差异比较:git diff
;
忽略文件
有些时候我们不想把某些文件纳入版本控制中,比如数据库文件,临时文件,设计文件等
在主目录下建立”.gitignore”文件,此文件有如下规则:
- 忽略文件中的空行或以井号(#)开始的行将会被忽略。
- 可以使用Linux通配符。例如:星号( * )代表任意多个字符,问号(?)代表一个字符,方括号([abc])代表可选字符范围,大括号({string1,string2,…})代表可选的字符串等。
- 如果名称的最前面有一个感叹号(!),表示例外规则,将不被忽略。
- 如果名称的最前面是一个路径分隔符(/),表示要忽略的文件在此目录下,而子目录中的文件不忽略。
- 如果名称的最后面是一个路径分隔符(/),表示要忽略的是此目录下该名称的子目录,而非文件(默认文件或目录都忽略)。
#为注释
*.txt #忽略所有 .txt结尾的文件,这样的话上传就不会被选中!
!lib.txt #但lib.txt除外
/temp #仅忽略项目根目录下的TODO文件,不包括其它目录temp
build/ #忽略build/目录下的所有文件
doc/*.txt #会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
传输操作
git add .
提交到暂存区
提交工作区所有文件到暂存区:
git add .
提交工作区中指定文件到暂存区:git add ...
提交工作区中某个文件夹中所有文件到暂存区:git add [dir]
git commit -m "消息内容"
向仓库提交代码
将暂存区中的文件提交到本地仓库中,即打上新版本:
git commit -m "commit_info"
将所有已经使用git管理过的文件暂存后一并提交,跳过add到暂存区的过程:git commit -a -m "commit_info"
提交文件时,发现漏掉几个文件,或者注释写错了,可以撤销上一次提交:git commit --amend
git log
查看提交记录
比较暂存区与上一版本的差异:
git diff --cached
指定文件在暂存区和本地仓库的不同:git diff --cached
查看提交历史:git log;参数-p
展开每次提交的内容差异,用-2
显示最近的两次更新,如git log -p -2
查看远程库信息,使用git remote -v
撤销
用暂存区中的文件覆盖工作目录中的文件:git checkout -- [文件名]
不加 -- [文件名]
则覆盖全部文件。不加 --
则变成了 切换到另一个分支 的命令
将文件从暂存区中删除:git rm --cached [文件名]
将git仓库中指定的更新记录恢复出来,并且覆盖暂存区和工作目录: git reset --hard [commitID]
撤销
- 删除工作区文件,并且也从暂存区删除对应文件的记录:
git rm
;- 从暂存区中删除文件,但是工作区依然还有该文件:
git rm --cached
;- 取消暂存区已经暂存的文件:
git reset HEAD ...
;- 撤销上一次对文件的操作:
git checkout --
。要确定上一次对文件的修改不再需要,如果想保留上一次的修改以备以后继续工作,可以使用stashing和分支来处理;- 隐藏当前变更,以便能够切换分支:
git stash
;- 查看当前所有的储藏:
git stash list
;- 应用最新的储藏:
git stash apply
,如果想应用更早的储藏:git stash apply stash@{2}
;重新应用被暂存的变更,需要加上--index
参数:git stash apply --index
;- 使用apply命令只是应用储藏,而内容仍然还在栈上,需要移除指定的储藏:
git stash drop stash{0}
;如果使用pop命令不仅可以重新应用储藏,还可以立刻从堆栈中清除:git stash pop
;- 在某些情况下,你可能想应用储藏的修改,在进行了一些其他的修改后,又要取消之前所应用储藏的修改。Git没有提供类似于 stash unapply 的命令,但是可以通过取消该储藏的补丁达到同样的效果:
git stash show -p stash@{0} | git apply -R
;同样的,如果你沒有指定具体的某个储藏,Git 会选择最近的储藏:git stash show -p | git apply -R
;更新文件
- 重命名文件,并将已改名文件提交到暂存区:
git mv [file-original] [file-renamed]
;
Git 远程仓库
在托管平台创建仓库
得到指令:git remote add origin git@github.com:[用户名]/[仓库名].git
远程库的名字就是origin,这是Git默认的叫法,也可以改成别的。仓库名与本地仓库名相同。
git push -u origin master
将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。
从现在起,只要本地作了提交,就可以通过git push origin master
把本地master分支的最新修改推送至远程仓库。
Git 分支
分支的主要作用是生成副本,避免影响开发主线。
- 主分支(master):第一次向git仓库提交更新记录时自动产生的一个分支。
- 开发分支(develop):作为开发的分支,基于master分支创建。
- 功能分支(feature):作为开发具体功能的分支基于开发分支创建。
分支命令
git branch
查看分支git branch <name>
创建分支git checkout <name>
切换分支git checkout -b <name>或者git switch -c <name>
创建+切换分支git merge <name>
合并指定分支到当前分支git branch -d <name>
删除分支(分支合并后才允许被删除)(-D 大写强制删除)
git push origin :branch-name
: 远程仓库同步删除掉的分支
注意:开发分支文件后要 commit
后再切换主分支,否则分支文件会出现在主分支里面
最新版本的Git提供了新的git switch命令来切换分支:
创建并切换到新的dev分支,可以使用: git switch -c dev
直接切换到已有的master分支,可以使用: git switch master
分支管理
- 创建分支:
git branch
- 从当前所处的分支切换到其他分支:
git checkout
- 新建并切换到新建分支上:
git checkout -b
;- 删除分支:
git branch -d
;- 将当前分支与指定分支进行合并:
git merge
;- 显示本地仓库的所有分支:
git branch
;- 查看各个分支最后一个提交对象的信息:
git branch -v
;- 查看哪些分支已经合并到当前分支:
git branch --merged
;- 查看当前哪些分支还没有合并到当前分支:
git branch --no-merged
;- 把远程分支合并到当前分支:
git merge
如果是单线的历史分支不存在任何需要解决的分歧,只是简单的将HEAD指针前移,所以这种合并过程可以称为快进(Fast forward),而如果是历史分支是分叉的,会以当前分叉的两个分支作为两个祖先,创建新的提交对象;如果在合并分支时,遇到合并冲突需要人工解决后,再才能提交;- 在远程分支的基础上创建新的本地分支
:git checkout -b /
,如git checkout -b (分支名) (远程仓库名)/(分支名)
;- 从远程分支checkout出来的本地分支,称之为跟踪分支。在跟踪分支上向远程分支上推送内容:
git push
。该命令会自动判断应该向远程仓库中的哪个分支推送数据;在跟踪分支上合并远程分支:git pull
;- 将一个分支里提交的改变移到基底分支上重放一遍:
git rebase
,如git rebase master server
,将特性分支server提交的改变在基底分支master上重演一遍;使用rebase操作最大的好处是像在单个分支上操作的,提交的修改历史也是一根线;如果想把基于一个特性分支上的另一个特性分支变基到其他分支上,可以使用--onto
操作:git rebase --onto
,如git rebase --onto master server client
;使用rebase操作应该遵循的原则是:一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行rebase操作;
分支管理策略
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
git merge --no-ff -m " " [name]
强制禁用Fast forward模式。
以dev和master分支为例,不使用Fast forward模式,merge后就像这样:
实际开发分支管理基本原则:
- master用于发布新版本。
- dev用于开发。
团队合作就是每个人都在dev分支上干活,每个人都有自己的分支,并且时不时地往dev分支上合并。
暂时保存更改
git中可以不提交更改,只提取分支上所有改动并储存,让开发人员得到一个干净的副本,临时转向其它工作。复制到“剪切板”,可以“粘贴“到其它分支。
场景:
- 储存临时改动:
git stash
- 恢复临时改动:
git stash pop
分支BUG解决方案(点击查看)
Git 标签
Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。而含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。
在Git中打标签非常简单,首先,切换到需要打标签的分支上,然后,敲命令git tag
列出现在所有的标签可以用 git tag
默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,可以用git log --pretty=oneline --abbrev-commit
找到历史提交的commit id,然后打上就可以了。git tag [tagname] [commit id]
注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show [tagname]查看标签信息
删除标签用git tag -d [tagname]
推送标签到远程仓库git push [远程仓库名] [tagname]
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除,然后,从远程删除。删除命令也是push,但是格式是这样的:git push origin :refs/tags/[tagname]
使用特定的搜索模式列出符合条件的标签,例如只对1.4.2系列的版本感兴趣:git tag -l "v1.4.2.*"
;
创建一个含附注类型的标签,需要加-a
参数,如git tag -a v1.4 -m "my version 1.4"
;
如果有自己的私钥,可以使用GPG来签署标签,只需要在命令中使用-s
参数:git tag -s v1.5 -m "my signed 1.5 tag"
;
验证已签署的标签:git tag -v ,如git tag -v v1.5
;
创建一个轻量级标签的话,就直接使用git tag命令即可,连-a
,-s
以及-m
选项都不需要,直接给出标签名字即可,如git tag v1.5
;
将标签推送到远程仓库中:git push origin ,如git push origin v1.5
;
将本地所有的标签全部推送到远程仓库中:git push origin --tags
;
相关知识
免密登录
设置本机绑定SSH公钥,可以实现免密码登录。
进入 C:\Users\ [用户名] \ .ssh 目录
生成公钥ssh-keygen
将公钥信息public key 添加到托管平台账户中即可!
远程分支
远程分支是对远程仓库中的分支的索引。它们是一些无法移动的本地分支;只有在GIT进行网络交互时才会更新。远程分支就是书签,提醒着你上次连接远程仓库是上面各分支的位置。
我们用 (远程仓库名)/(分支名) 这样的形式表示远程分支。我们来举一个例子,假如你从GITHUB上克隆了一个项目到本地,GIT会自动为你将次远程仓库命名为origin,并且下载其中的所有数据,建立一个指向它的master分支的指针,在本地命名为origin/master,但是你无法在本地更改其数据,所以Git建立了一个属于你自己的本地的master分支,始于origin上master分支相同的位置,你可以就此开始工作。
注:一次 Git 克隆会建立你自己的本地分支 master 和远程分支 origin/master,并且将它们都指向 origin 上的 master 分支。
你可以沿着本地master分支做一些改动,但是只要你不和服务器通讯,你的 origin/master 指针仍然保持原位不会移动。