Git初级操作指南
前言: “ git的使用是每个软件开发人员必备的技能,但很多git的初学者,都只是知道常用的一些命令写法,却不知道命令真正对应的作用是什么。网络上大部分git操作教程文章,都是直接罗列了一些常用命令,命令后面注释作用的描述。初学者看过文章之后,可能还是无法举一反三,碰到其他场景,仍然需要再去搜索应该使用什么命令操作。”
本文通过介绍git管理的目录结构,将git常用操作命令基于git结构通过图形的方式展现,让初学者对git操作对应的作用流向印象深刻,从而达到对于更加复杂的应用场景也能思路清晰。
目录
Git操作一般流程
初始化一个仓库
## 新建一个目录作为工作目录
$ mkdir git_test
$ cd git_test
## 在本地初始化git仓库
$ git init
获取远程仓库
## 把远程仓库clone下来
$ git clone git@github.com:xx/xxx.git
## 默认远程仓库名为origin
## 也可以添加远程仓库为origin
$ git remote add origin git@github.com:xx/xxx.git
## 合并远程仓库代码到本地
$ git pull origin master
提交代码
## 添加所有修改
$ git add .
## 提交修改
$ git commit -m "add some files"
## 推送到远程仓库
$ git push
在非常常规的单人开发情况下,或许知道以上命令已经够用了。但一般项目都是团队协作,而且可能有多个分支版本,碰到稍微复杂的场景,仅仅记住这些命令就显得不够了。
清楚地知道每个git操作对应的流程和作用才能应对更多的情况。首先,得要了解一下git的目录结构。
Git目录结构
在Git中,我们将需要进行版本控制的文件目录叫做一个仓库(repository),这个目录里面的所有文件都通过Git来实现版本管理,Git能跟踪并记录在该目录中发生的所有更新。
git管理的目录结构如下图所示:
根据上图,给出每个部分的简要说明:
- Directory:使用Git管理的一个目录,包含我们的工作空间和Git的管理空间。
- WorkSpace:需要通过Git进行版本控制的目录和文件 (也就是我们的项目代码),这些目录和文件组成了工作空间,除了.git之外的都属于工作区。
- .git:存放Git管理信息的目录,初始化仓库的时候自动创建。
- Index/Stage:暂存区,在提交进本地仓库之前,我们可以把所有的更新以内容快照的形式放在暂存区。
- Local Repo:本地仓库,一个存放在本地的版本库;指针HEAD会指向当前的开发分支(branch)。
- Stash:是一个工作状态保存栈,用于保存/恢复WorkSpace中的临时状态。
git命令基本就是围绕这些部分在进行操作,接下来对应着结构看看操作的流程走向。
git操作的流程
git提交流程
如上图,对应之前提到的git提交代码的操作命令:
$ git add .
$ git commit -m "add some files"
$ git push
首先,add命令将工作区(即本地项目代码)的所有修改文件添加到暂存区;然后commit将暂存区内容提交到本地仓库,并添加提交记录message;最后push将本地仓库推送到远程服务器仓库。
git拉取流程
如上图,对应的git拉取远程仓库代码的操作命令:
$ git remote add origin git@github.com:xx/xxx.git
$ git pull origin master
# git pull操作等于fetch + merge
$ git fetch origin master
$ git merge origin/master
pull操作从远程仓库拉取代码并合并到本地工作区,等同于先执行fetch操作再执行merge操作。fetch操作从远程获取最新版本到本地,不会自动合并;merge操作合并对应分支代码到工作区。
注:注意git push和git pull的默认操作,即未指定upstream远程主机名和分支名的情况下,git push会根据git config中的push.default属性进行操作;git pull会自动在本地分支和远程分支之间建立追踪关系(tracking)。本文不展开介绍。
git撤销流程
如上图,对应的git撤销操作命令:
$ git reset HEAD^
$ git reset --soft HEAD^
$ git reset --hard HEAD^
reset 默认的参数为mixed,git reset 操作会将添加到暂存区的更新撤回工作区;
git reset –soft HEAD^ 参数为soft,软回退,即将本地仓库的提交回退一个版本至暂存区,且将HEAD指针重置到上一版本,即将HEAD指针前移一个commit对象 (注:HEAD为指向当前分支的指针,HEAD^为语法糖一个^表示往上一个版本,可以多个,也可以用 HEAD~n );
git reset –hard HEAD^ 硬回退,直接将工作区代码回退至上一版本,且将HEAD指针前移一个commit对象。
git查看操作
git也可以使用命令的形式查看git操作后的对应状态和历史记录。
git status 命令用于显示工作目录和暂存区的状态。使用此命令能看到那些修改被暂存, 哪些没有, 哪些文件没有被git 跟踪(tracked)。
$ git status
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: xx/xxx
#
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: yy/yyy
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
其中,git status输出的内容分为3个分类/组:
- Changes to be committed : 已经在暂存区, 等待提交commit的更新
- Changes not staged for commit : 工作区有修改, 但是没有被添加到暂存区的更新
- Untracked files : 没有跟踪(tracked)过的文件, 即从没有add过的文件
git status不显示已经commit的信息,看项目历史的提交信息要使用 git log。
git log 命令用于显示提交日志信息。
$ git log
commit e745820e4ac11053dedaa2a440ce1b5fbe4de2e3
Author: xiaojun <xiaojun@stargraph.cn>
Date: Sat Apr 21 18:49:42 2018 +0800
feat(xx):xx
commit e5ffc53a4ef483dcc72fc5fbe565373dd03f79e6
Author: xiaojun <xiaojun@stargraph.cn>
Date: Thu Apr 19 22:09:37 2018 +0800
feat(xxx): xxx
默认不用任何参数的话,git log 会按提交时间列出所有的更新,最近的更新排在最上面。每次更新都有一个 commit ID、作者的名字和电子邮件地址、提交时间,最后缩进一个段落显示提交的message。
git log 有许多选项参数可以设置显示的格式和内容,这里不展开介绍。
git show 命令可查看某一次提交详细信息。 git show命令采用SHA-1 commit ID作为参数。
git show
commit e745820e4ac11053dedaa2a440ce1b5fbe4de2e3
Author: xiaojun <xiaojun@stargraph.cn>
Date: Sat Apr 21 18:49:42 2018 +0800
feat(message):xxxx
diff --git a/src/xx
new file mode 100644
index 0000000..5eda378
--- /dev/null
+++ b/xx
@@ -0,0 +1,67 @@
+ //新增代码
+ xxxxx
+ xxx
...
若不指定参数,则展示最近一次提交的详情信息,diff后为更新的文件名,—后为对应文件删除的内容,+++后为添加的内容。
git diff 命令用于显示文件之间的差异。
不加参数,默认情况下用于显示工作区中当前文件和暂存区快照之间的差异,也就是工作区修改之后还没有暂存起来的变化内容。
也可以使用 git diff 比较两次提交之间的差异,git diff 比较在两个分支之间的差异等等。
$ git diff
diff --git xx.js
index d9f99f6..0db60fb 100644
--- a/xxx
+++ b/xxx
@@ -6,7 +6,7 @@
- xxx
+ yyy
了解了以上git操作,还不足以应对大部分应用情况,因为git管理往往还需要多分支管理,下面再介绍git分支的操作。
git分支管理
在进行提交操作时,Git 会保存一个提交对象(commit object)。该提交对象会包含一个指向暂存区内容快照的指针,不仅如此该对象还会包含作者信息、提交message和指向它父对象的指针,而父对象即上一次提交产生的提交对象。这样当前分支保存的记录就像一条链表一样链接起来。Git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。
如上图所示,每一个字母表示一个提交对象,每个对象有指针指向其父对象;HEAD指针指向当前分支的最新版本。
新建分支、切换分支
# 新建develop分支
$ git branch develop
# 切换到develop分支
$ git checkout develop
# 新建并切换到新建的develop分支,等同于以上两步
$ git checkout -b develop
如上图,新建的分支基于当前master分支的记录复制了另一条链;而切换到develop分支之后,HEAD指针则指向develop分支的最新版本。
合并分支
如上图所示,若开发人员在切换不同的分支后都有继续不同的开发,提交了不同的记录,此时不同的分支链就会出现分叉。这种情况非常常见,本地仓库分支可能有多个不同版本,远程仓库分支也会被其他开发人员更新提交记录,所以合并分支让分叉重新还原成一条链,整合版本以便后续开发很重要。
假设上图的情况是,develop分支为某个迭代的开发版本,master分支为正式稳定版本,两个分支上同时都有开发人员做了更新提交,此时需要合并develop分支到master分支整合成正式版本。此时使用merge命令:
# 切换到master分支
$ git checkout master
Switched to branch 'master'
# 合并develop分支到master
$ git merge develop
如图所示,合并操作在将合并结果生成一个新的快照并自动创建了一个新的提交,这个合并提交不只一个父提交对象。
而,合并分支的时候有可能不那么顺利,如果两个分支的分叉上的更新同时修改了同一文件的同一部分,git合并时则会产生冲突,并不能自动创建一个新的合并提交,此时需要手动解决冲突:
$ git merge develop
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Git 会在有冲突的文件中加入标准的冲突解决标记,出现冲突的文件会包含一些特殊区段:
<<<<<<< HEAD:index.html
<div>xxx</div>
=======
<div>
yyy
</div>
>>>>>>> develop:index.html
HEAD 所指向的版本(master分支)代码为 ======= 的上半部分,而 develop 分支在 ======= 的下半部分。解决冲突后,add、commit提交新的记录即可完成合并。
另外再介绍合并代码的两种不同的操作rebase和cherry-pick。
rebase一般用于简化历史提交记录,将提交链合并为一条。
$ git checkout develop
$ git rebase master
rebase master分支到develop, devleop分支的提交将会取消掉,创建新的提交接在master分支链后面。如图所示,历史记录成一条线,相当整洁。
而 cherry-pick 相当于更加细粒度的合并操作。git cherry-pick可以选择某一个分支中的一个或几个commit(s)来进行操作。例如,假设我们有个稳定版本的分支master,另外还有个开发版本的分支develop,我们不能直接把两个分支合并,这样会导致稳定版本混乱,但是又想增加develop中的一个功能到master中,这里就可以使用cherry-pick了。
$ git checkout master
$ git cherry-pick 38361a68
# 38361a68 为develop分支下对应想添加功能的commit ID
$ git checkout develop
$ git log
commit 38361a68138140827b31b72f8bbfd88b3705d77a
Author: xiaojun <xiaojun@stargraph.cn>
Date: Sat Apr 21 18:49:42 2018 +0800
feat(xx):新增功能xx
当执行完 cherry-pick 以后,将会自动在master生成一个新的提交,只更新对应commit的内容;这个新的提交的哈希值和develop中原来的不同,但message一样。
总结:本文希望通过将git操作的流程基于git目录结构图形化,让初学者可以更清晰地了解git命令的作用,能够更加自如地应对其他复杂的应用场景。