13

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 的部分提交记录和目前的分支情况

clipboard.png

clipboard.png

三种状态

你的文件可能处于尺寸三种状态之一:已提交(committed)、已修改(modified)和已暂存(staged)
由此引入 Git 项目的三个工作区域的概念:Git 仓库、工作目录以、暂存区域。

基本的 Git 工作流程如下:

  1. 在工作目录中修改文件。
  2. 暂存文件,将文件的快照放入暂存区域。
  3. 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。

安装

windows 的我就不说了,需要的请自行百度。
Mac 系统在安装 Xcode 的时候就会自动安装
查看当前版本

git --version
# git version 2.17.2 (Apple Git-113)

初次配置

git 自带 git config 工具来修改 git 的配置文件

  1. /etc/gitconfig 系统上每一个用户及他们仓库的通用配置,带 --system 参数使用 git config 时,会读写这个文件
  2. ~/.gitconfig 是只针对当前用户。 带 --global 参数可读写此文件
  3. 当前目录是正在使用的仓库时,配置文件路径是 .git/config

每一个级别覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。

用户信息

安装完git,第一件事就是设置用户名和邮件地址,这些信息会在每次提交时使用

$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

指定git默认的文本编辑器

默认的编辑器是 vim,如果你想自定义编辑器,可以通过这个指令指定:

$ git config --global core.editor 编辑器名称

查看配置信息

git config --list 可以查看你的 git 配置信息

$ git config --list
user.name=John Doe
user.email=johndoe@example.com
...

初始化 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 以字符图的样子显示分支情况

clipboard.png

clipboard.png

撤消操作

有时候,在提交了之后发现有几个文件没有提交,可以用 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    git@github.com:KyleBing/Diary.git (fetch)
diary    git@github.com:KyleBing/Diary.git (push)

在URL前面如果显示 origin 那是 git 给 URL 仓库的默认名字,

添加远程仓库

git remote add tb git@github.com:KyleBing/TouchbarBBT.git

这样就添加了 git@github.com:KyleBing/TouchbarBBT.git 的仓库,别名为 tb

这时候拉取远程仓库内容的时候,直接用 tb 代替 git@github.com:KyleBing/TouchbarBBT.git

从远程仓库拉取内容

git fetch [仓库别名]

这个指令会拉取远程仓库的所有内容,包括所有分支。

git pull

会拉取远程分支到当前分支,并自动合并。

推送到远程仓库

git push [仓库别名] [分支名]

会将你的项目推送到远程

查看远程仓库

git remote show [仓库别名]

在没有设置仓库别名的时候,git 会默认把这个仓库命名为 origin

$ git remote show tb
* remote tb
  Fetch URL: git@github.com:KyleBing/TouchbarBBT.git
  Push  URL: git@github.com: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

重命名 origintb

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对象索引)
  • 一个提交对象(包含着指向前述树对象的指针和所有提交信息)

clipboard.png

HEAD、 分支 master 是什么东西

clipboard.png

git 的分支,其实是指向不同提交版本的指针。在 git init 初始化的时候,默认生成了一个名为 master 的分支,其实它跟其它分支的等级是一样的,都只是一个分支,默认名为 master,只是我们都懒得给它改名罢了。

git 还有一个名为 HEAD 的指针,作用是指向当前本地库正在的修改的分支,如上图,本地库正在 master 分支上做修改。

创建分支

git branch testing

clipboard.png

上面的指令的结果如上图,在当前版本上新建了一个名为 testing 的分支。

git branch 只是创建一个分支,并不会切换到该分支。

分支切换

git checkout testing

上述操作会把 HEAD 指针指向 testing 分支,作用如下图:

clipboard.png

当前在 testing 分支上,现在新建并提交一个文件,只会保存在 testing 分支上,而 master 分支还停留在前一个提交状态上,也就是说该文件只存在于 testing 分支上。

效果如下图;master 没有动,testing 已经向前移动到了最新的节点。

切换分支的时候会改变本地的目录结构

切换分支的时候,本地文件会变到当前分支的目录结构上,如果由于特殊原因没法切换分支的时候,会提示切换分支失败。

此时我们再切换回 master 分支并做修改后提交,master 指针就会指向新的提交节点。此时,master testing 分别处于不同的提交节点上,如下图:

clipboard.png

git log --online --graph --all

terminal 界面效果如图:

clipboard.png

分支的新建与合并

工作中的常见情景:

  1. 开发一个项目
  2. 为一个新的需求,创建一个分支,
  3. 在新建的分支上开展工作

正常开发过程中,如果出现了一个严重问题,需要紧急修补,可以这么做:

  1. 切换到你线上的主分支
  2. 为紧急任务新建一个分支,在这个分支上修复这个问题
  3. 在测试通过后,回到主分支上,并把解决问题的分支合并到主分支上
  4. 回到工作的分支,继续工作

新建分支

如果项目已经有了一定的提交记录,当前状态是下面这样:

clipboard.png

为了修复一个问题编号是 #53 的问题,需要新建一个分支并在这个分支上开始修复工作

git checkout -b iss53

# 上面的命令是下面两条指令的简写
git branch iss53
git checkout iss53

你已经在分支 iss53 上做了一定的修改并提交了,此时的仓库是这样的:

clipboard.png

此时,公司又有问题需要修改,不必把 iss53 和新问题混在一起修改,直接在原有 master 分支上新建一个分支并在其上面修改即可。

# 切换到 master 分支
git checkout master

# 创建新问题分支 hotfix
git checkout -b hotfix

此时的仓库状态:

clipboard.png

这样,你可以在任意分支之间切换,且不会影响主分支 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 指针执行简单的前移即可,如图:

clipboard.png

hotfix 问题解决并合并到主分支之后,你要回到之前的 iss53 分支继续解决 #53 问题。在切回去之前,你应该先已合并的 hotfix 分支删除,因为合并之后 masterhotfix 指向了同一个位置,已经不需要 hotfix了,执行操作:

# 删除 hotfix 分支
git branch -d hotfix

仓库现在是这样的:

clipboard.png

分支的合并

经过奋力修改后,解决了 #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 进行合并:

clipboard.png

上面的合并后,仓库是这样的:

clipboard.png

合并后需要删除无用的分支

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 <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

出现冲突的时候,git 会在冲突文件中添加标准的冲突标记符,像下面这样子:

clipboard.png

HEAD 所指示的版本在这个区段的上半部分(======= 的上半部分),而 iss53 分支所指示的版本在 ======= 的下半部分。 为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突:

<div id="footer">
please contact us at email.support@github.com
</div>

解决冲突之后,用 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'.

分支开发工作流


KyleBing
659 声望18 粉丝

前端,喜欢 Javascript scss,喜欢做一些实用的小工具