Git 使用
官方文档: https://git-scm.com/doc推荐看看这本介绍 Git 的电子书,看完什么都知道了,介绍的很详细
Git电子书下载 【PDF】 【EPUB】 【MOBI】
我理解的 Git 是个什么东西
Git是现在流行的VCS
(“Centralized Version Control Systems” 版本控制系统)之一。版本控制系统主要目的是,控制项目不同版本,可随时回溯到任何需要的版本。
如软件开发行业,版本控制系统扮演着不可或缺的重要角色。
VCS
可以把软件开发的各个岗位连接起来,各自完成自己的工作,且井井有条,前端和后台的工作同时进行。VCS
可以把所有管理的文件都进行版本标记,如果任何文件修改出错,都可以随时恢复到前面的任何版本。
说白了,VCS
可以把开发人员紧密联合起来,大家同时进行开发,不会出现前后端融合错误。
使用 git 每个用户都会在本地拥有 git 仓库的所有信息,过往记录,所以可以随时随地的提交代码。
git 对文件版本的记录区别于其它 vcs, 其它vcs是对每个版本的文件进行标记,而 git 是对每个版本的所有文件进行快照,并根据项目内的所有文件计算出 hash 值来记录版本号。保证完整性。
git 最让人痴迷的就是分支了 (branch)
来欣赏一下 Bootstrap 的部分提交记录和目前的分支情况
三种状态
你的文件可能处于尺寸三种状态之一:已提交(committed)、已修改(modified)和已暂存(staged)
由此引入 Git 项目的三个工作区域的概念:Git 仓库、工作目录以、暂存区域。
基本的 Git 工作流程如下:
- 在工作目录中修改文件。
- 暂存文件,将文件的快照放入暂存区域。
- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
安装
windows
的我就不说了,需要的请自行百度。
Mac 系统在安装 Xcode 的时候就会自动安装
查看当前版本
git --version
# git version 2.17.2 (Apple Git-113)
初次配置
git 自带 git config
工具来修改 git 的配置文件
-
/etc/gitconfig
系统上每一个用户及他们仓库的通用配置,带--system
参数使用git config
时,会读写这个文件 -
~/.gitconfig
是只针对当前用户。 带--global
参数可读写此文件 - 当前目录是正在使用的仓库时,配置文件路径是
.git/config
每一个级别覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。
用户信息
安装完git,第一件事就是设置用户名和邮件地址,这些信息会在每次提交时使用
$ git config --global user.name "John Doe"
$ git config --global user.email [email protected]
指定git默认的文本编辑器
默认的编辑器是 vim,如果你想自定义编辑器,可以通过这个指令指定:
$ git config --global core.editor 编辑器名称
查看配置信息
git config --list
可以查看你的 git 配置信息
$ git config --list
user.name=John Doe
[email protected]
...
初始化 Git 仓库
在当前目录下初始化仓库
git init
这个命令会将本目录初始化为一个代码仓库,并在目录中增加 .git 目录,里面是关于本仓库的所有信息。
初始化之后,目录中的文件并没有加入到 git 的版本控制中,需要手动将文件添加到 git 的控制列表中,操作见下一步。
$ git add *.c
$ git add LICENSE
$ git commit -m 'initial project version
Git 本地库的常规操作
忽略特定文件
在 git 管理的目录下 新建文件 .gitignore
如:
*.[oa]
*~
上面的文件意思是忽略所有以 .o
.a
的文件,忽略以 ~
结尾的文件
.gitignore
的格式规范:
- 空行 或
#
开头的行会被忽略 - 可以使用标准的 glob 模式匹配
- 以
/
开头防止递归 - 以
/
结尾指定目录 - 用
!
反向选择
glob shell 所使用的简化了的正则表达式
*
0个或任意个字符[abc]
任意其中一个字符?
匹配一个字符[0-9]
表示匹配任意中间的字符**
两个星表示任意中间目录a/**/c
a/b/c
a/d/c
查看当前状态
git status
查看当前目录下的文件状态,如果有文件未加入到跟踪中,会提示你。
git status -s
可以查看简短的报告
git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
??
表示未跟踪的文件A
表示新添加的文件MM
右边的M表示修改后未保存到暂存区,左边表示修改并放入了暂存区
添加文件
如果我们新建了一个名为 README
的文件,git status
会显示文件没有被跟踪,通过以下指令把文件添加到 git 的文件跟踪中。
加入到跟踪中后,没有 commit
之前,文件处于暂存区。
git add README
如果这时候修改了 README
文件,再用 git status
查看状态的时候,会看到提示 changes not staged for commit
,此时如果想在下次 commit
的时候提交这次修改,需要再次 git add
这个文件。git add
可以理解为标记文件到下次提交,而非只是添加文件到跟踪状态
查看区别
在修改了文件之后,没有添加到暂存区之前,可以用 git diff
查看前后版本的区别
这是在文件没有添加到暂存区的时候的查看方式,在存入后,需要用 git diff --cached
来查看不同
提交文件
git commit
这会打开默认的文本编辑器,让你输入此次提交的信息。就像平时用 vi 编辑文本那样,按 i
进入输入模式,然后输入你要写的话,再 esc
wq
保存退出就会提交了。
或者用简短的方式,直接在命令中输入此次提交的信息,多个 -m
参数会作为多个分段。
git commit -m '初次提交'
如果闲每次添加文件麻烦,可以用 -a
参数跳过添加到暂存区的操作,直接在提交的时候把所有已经跟踪的文件上传。
git commit -a
移除文件
移除文件并取消该文件的跟踪,需要用 git rm
指令,并删除本地文件。
如果只是在目录中删除了文件,在 git status
的时候会提示文件没有添加到跟踪中。
如果文件已经添加到暂存区,且修改了文件还没有提交,此时删除文件就需要用 -f
参数了。
只移除文件跟踪,不删除文件
git rm -cached README
git rm
后面还可以跟 glob 模式的匹配字符串
移动文件
git mv
给文件重命名用这个指令
其实它是三个指令的集合
mv README.mb README
git rm README.mb
git add README
所以用 git mv
会更方便一些。
查看历史
git log -p -2
-p
显示不同版本的区别-2
显示最近的再次更新
--stat
可以查看每次更新的简略信息--graph
以字符图的样子显示分支情况
撤消操作
有时候,在提交了之后发现有几个文件没有提交,可以用 git commit --amend
。
git commit -m "initial commit"
git add forgotten_file
git commit --amend
如果在你提交之后,在没有修改文件之前马上添加未提交的文件,再 --amend
就会打开上次提交的注释,接着编辑信息,然后再提交。
Github 远程代码仓库的使用
查看当前目录下的远程仓库 URL
git remote
git remote -v
$ git remote -v
diary [email protected]:KyleBing/Diary.git (fetch)
diary [email protected]:KyleBing/Diary.git (push)
在URL前面如果显示 origin
那是 git 给 URL 仓库的默认名字,
添加远程仓库
git remote add tb [email protected]:KyleBing/TouchbarBBT.git
这样就添加了 [email protected]:KyleBing/TouchbarBBT.git
的仓库,别名为 tb
这时候拉取远程仓库内容的时候,直接用 tb
代替 [email protected]:KyleBing/TouchbarBBT.git
从远程仓库拉取内容
git fetch [仓库别名]
这个指令会拉取远程仓库的所有内容,包括所有分支。
git pull
会拉取远程分支到当前分支,并自动合并。
推送到远程仓库
git push [仓库别名] [分支名]
会将你的项目推送到远程
查看远程仓库
git remote show [仓库别名]
在没有设置仓库别名的时候,git 会默认把这个仓库命名为 origin
$ git remote show tb
* remote tb
Fetch URL: [email protected]:KyleBing/TouchbarBBT.git
Push URL: [email protected]:KyleBing/TouchbarBBT.git
HEAD branch: master
Remote branch:
master tracked
Local ref configured for 'git push':
master pushes to master (local out of date)
给远程仓库重命名
git remote rename origin tb
重命名 origin
为 tb
git remote rm tb
移除远程仓库 tb
定义发布版本 tag
查看标签
git tag
列出已有标签
git tag -l 'v1.8.5'
只查看 v1.8.5 的相关版本
v1.8.5
v1.8.5-rc
...
创建 annotated 标签
添加 annotated 标签,用 -a
参数
git tag -a v1.4 -m "tag version info"
git show v1.4
可以查看标签和对应的提交信息
创建 light-weight 标签
git tag -v1.4 -lw
不需要填写提交信息
后期打标签
查看过往提交记录,然后取一个记录的校验和的部分字符串,把该提交打成标签
git log --pretty=online
# 显示为
86bc9a426f04817fe47c07685ac60e0fcdd33af9 (HEAD -> master, tb/master) 添加外部链接
4200265a6990c78fb5d0667eb0bbb99ab2760fc0 Merge branch 'master' of github.com:KyleBing/TouchbarBBT
6df11cb1ee1726291d8ef20da1a93773a8e40fff Last
6529785f35f5f2fd6333d95430d46e6dc75c590f Update README.md
aa6a05ba99933e5505969d36f66191a499e7c2b8 文章发布前的版本
7eaa5924d0b5e7675d90d9b31b8daedaacaca0ea 定稿
4f2320c2dcbe5ccd9dac551817cbc90653fd377b 调整按钮尺寸大小,正在播放背景色改为黑色
18dce9bf2c84183e9030d5f7e8431ca2c0504a57 Update README.md
af58e9c024ecbe2994d0c3bb21ab112dbed008fa 更新说明文档
04da2e77f336f87827e2cf5801305f075c45d7dd 添加默认 touchbar 文件,添加 最新 Touchbar 文件
d797ff57fd5ff74c233c804fb627c7df04456595 Update README.md
d8a1f7ea817a0aa51ff5b2c1f075e064a9a323f7 Merge branch 'master' of github.com:KyleBing/TouchbarBBT
f9075467294865fe1a31b0879aa2d11071fe1110 修改目录名字,添加实际照片
51fde532cb2d1ef7197d14e66cdaaed46dc8be3a Create README.md
d0611a4e511641e772e56ec7c923291e254a54ad 初始化该仓库,添加基础的配置文件和基础的图标
git tag -a v1.2 51fde
共享标签
git push
并不会把标签传送到远程仓库,显式的推送才会显示。
git push origin v1.3
用 --tags
会把所有不在远程服务器的标签都推送到服务器上
git push origin --tags
删除标签
删除本地标签
git tag -d v1.3
检出标签
git checkout 2.0
Git 别名
git 可以像系统那样设置别名
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git ci -m "infos"
# 跟下面作用一样
git commit -m "infos"
分支
如果项目中新建了三个文件,在提交的时候,Git 仓库中有五个对象:
- 三个 blob 对象(保存着文件快照
- 一个树对象(记录着目录结构和 blob对象索引)
- 一个提交对象(包含着指向前述树对象的指针和所有提交信息)
HEAD、 分支 master 是什么东西
git 的分支,其实是指向不同提交版本的指针。在 git init
初始化的时候,默认生成了一个名为 master
的分支,其实它跟其它分支的等级是一样的,都只是一个分支,默认名为 master
,只是我们都懒得给它改名罢了。
git 还有一个名为 HEAD
的指针,作用是指向当前本地库正在的修改的分支,如上图,本地库正在 master
分支上做修改。
创建分支
git branch testing
上面的指令的结果如上图,在当前版本上新建了一个名为 testing
的分支。
git branch
只是创建一个分支,并不会切换到该分支。
分支切换
git checkout testing
上述操作会把 HEAD
指针指向 testing
分支,作用如下图:
当前在 testing
分支上,现在新建并提交一个文件,只会保存在 testing
分支上,而 master
分支还停留在前一个提交状态上,也就是说该文件只存在于 testing
分支上。
效果如下图;master
没有动,testing
已经向前移动到了最新的节点。
切换分支的时候会改变本地的目录结构
切换分支的时候,本地文件会变到当前分支的目录结构上,如果由于特殊原因没法切换分支的时候,会提示切换分支失败。
此时我们再切换回 master
分支并做修改后提交,master
指针就会指向新的提交节点。此时,master
testing
分别处于不同的提交节点上,如下图:
git log --online --graph --all
terminal 界面效果如图:
分支的新建与合并
工作中的常见情景:
- 开发一个项目
- 为一个新的需求,创建一个分支,
- 在新建的分支上开展工作
正常开发过程中,如果出现了一个严重问题,需要紧急修补,可以这么做:
- 切换到你线上的主分支
- 为紧急任务新建一个分支,在这个分支上修复这个问题
- 在测试通过后,回到主分支上,并把解决问题的分支合并到主分支上
- 回到工作的分支,继续工作
新建分支
如果项目已经有了一定的提交记录,当前状态是下面这样:
为了修复一个问题编号是 #53
的问题,需要新建一个分支并在这个分支上开始修复工作
git checkout -b iss53
# 上面的命令是下面两条指令的简写
git branch iss53
git checkout iss53
你已经在分支 iss53
上做了一定的修改并提交了,此时的仓库是这样的:
此时,公司又有问题需要修改,不必把 iss53
和新问题混在一起修改,直接在原有 master
分支上新建一个分支并在其上面修改即可。
# 切换到 master 分支
git checkout master
# 创建新问题分支 hotfix
git checkout -b hotfix
此时的仓库状态:
这样,你可以在任意分支之间切换,且不会影响主分支 master
的正常运行。
假如,现在已经解决了主线上的问题,需要把 hotfix
分支合并到主线上。需要先切换到主分支 master
上,然后执行合并 hostfix
的操作
# 切到 master 分支
git checkout master
# 合并 hotfix 分支
git merge hotfix
# 结果
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)
可以看到反馈信息中的 fast-forward
提示,这是因为 hotfix
是在 master
分支新建的,并在其基础上做的修改,他们的合并操作只需要 HEAD
指针执行简单的前移即可,如图:
hotfix
问题解决并合并到主分支之后,你要回到之前的 iss53
分支继续解决 #53 问题。在切回去之前,你应该先已合并的 hotfix
分支删除,因为合并之后 master
和 hotfix
指向了同一个位置,已经不需要 hotfix
了,执行操作:
# 删除 hotfix 分支
git branch -d hotfix
仓库现在是这样的:
分支的合并
经过奋力修改后,解决了 #53 问题,需要把 iss53
合并到 master
主分支中
# 切换到 master 主分支
git checkout master
# 合并 iss53 分支到 master
git merge iss53
# 结果
Merge made by the 'recursive' strategy.
index.html | 1 +
1 file changed, 1 insertion(+)
这次 merge 跟之前 hotfix
分支的合并并不一样。这是因为,master 分支所在提交并不是 iss53
分支所在提交的直接祖先,此时需要比对 master
iss53
和 他们相交的 C2
进行合并:
上面的合并后,仓库是这样的:
合并后需要删除无用的分支
git branch -d iss53
遇到冲突时的分支合并
如果前面提到的 iss53
hotfix
两个分支都修改了同一个文件的同一个地方,那么在合并的时候,git自动合并就会失败,需要手动解决冲突:
# 合并 iss53
git merge iss53
# 结果
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
此时查看状态会显示有一个文件未合并(unmerged)
# 查看仓库当前状态
git status
# 结果
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add ..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
出现冲突的时候,git 会在冲突文件中添加标准的冲突标记符,像下面这样子:
HEAD
所指示的版本在这个区段的上半部分(======= 的上半部分),而 iss53
分支所指示的版本在 ======= 的下半部分。 为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突:
解决冲突之后,用 git add
添加冲突文件到暂存区,此时 git 会认为冲突已经解决,并移除冲突标记。
也可以用图形化的工具来解决冲突 git mergetool
会启动合适的合并工具,其帮助信息如下:
git mergetool
This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
# 这里显示支持的工具列表
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html
Normal merge conflict for 'index.html':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (opendiff):
此时查看状态:
# 查看状态
git status
# 结果
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: index.html
完成提交:
# 执行提交
git commit
# 结果
“Merge branch 'iss53'
Conflicts:
index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
# .git/MERGE_HEAD
# and try again.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
# modified: index.html
#
分支管理
git branch 命令不只是创建和删除分支,不加参数时会列出所有分支
# 查看分支列表
git branch
# 结果
iss53
* master
testing
*
表示当前 HEAD
指针的指向,也就是当前所处的分支。
查看每个分支的最后一次提交:
# 查看分支最后提交
git branch -v
# 结果
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch 'iss53'
testing 782fd34 add scott to the author list in the readmes
---gerged
--no-merged
用于查看已合并的和未合并的分支
git branck --merged
# 结果
iss53
* master
可以看到当前 iss53
已合并到 master
中,可以删除 iss53
分支了。
当分支没有合并时,执行删除分支操作会失败:
git branch -d testing
# 结果
error: The branch 'testing' is not fully merged.
If you are sure you want to delete it, run 'git branch -D testing'.