Git与GitHub(中)
前言
这部分将讲述Git相关的基本知识和操作方法。
各种Git命令操作,需要鼠标右键选择,Git Bash here
来开始。
基本操作
git init
——初始化仓库要使用Git进行版本管理,必须先初始化仓库。Git是使用
git init
命令进行初始化的。命令示例:1
git init
如果初始化成功,执行了
git init
命令的目录下就会生成.git
目录。这个目录里存储着管理当前目录内容所需的仓库数据。.git
目录默认隐藏,需要开启查看隐藏文件才可以查看到在Git中,我们将这个目录的内容称为“附属于该仓库的工作树”。文件的编辑等操作在工作树中进行,然后记录到仓库中,以此管理文件的历史快照。如果想将文件恢复到原先的状态,可以从仓库中调取之前的快照,在工作树中打开。开发者可以通过这种方式来获取以往的文件,具体的操作指令后面会说明。
git status
——查看仓库状态git status
命令用于显示Git仓库的状态。工作树和仓库在被操作的过程中,状态会不断发生变化。在Git操作的过程中,要常看仓库状态。命令示例:
1
2
3
4
5
6git status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)结果显示我当前处于
master
分支下。关于分支我们会在后面讲到。接着还显示了没有可提交的内容。所谓提交(Commit),是指“记录工作树中所有文件的当前状态”。没有可提交内容,就是说明我们当前建立这个仓库中还没有记录任何文件的任何状态。这里,创建一共Readme文件作为示例,为第一次提交做前期准备。
可以看到在Untracked files中显示了Readme.md文件。类似地只要对Git的工作树或仓库进行操作,
git status
命令的显示结果就会发生变化。git add
——向暂存区中添加文件如果只是用Git仓库的工作树创建了文件,那么该文件并不会被计入Git仓库的版本管理对象当中。因此我们使用
git status
命令查看ReadMe.md文件时,它会显示在Untracked files里。要想让文件成为Git仓库的管理对象,就需要用
git add
命令将其加入暂存区(Stage或者Index)中。暂存区是提交之前的一个临时区域。将ReadMe.md文件加入暂存区后,
git status
命令的显示结果发生了变化。可以看到,ReadMe.md文件显示在Changes to be committed中了。git commit
——保存仓库的历史记录git commit
命令可以将当前暂存区中的文件实际保存到仓库的历史记录中。通过这些记录,我们就可以在工作树中复原文件。记录一行提交信息
命令示例:
1
git commit -m "这里是提交信息,是对提交的描述"
记录详细的提交信息
上述命令示例,提交了一行简单的提交信息,如果希望更加详细的描述提交信息,就不需要加
-m
,直接执行git commit
命令。如下所示:1
2不需要加-m了
git commit执行后编辑器会启动显示如下效果:
1
2
3
4
5
6
7
8
Please enter the commit message for your changes. Lines starting
with '#' will be ignored, and an empty message aborts the commit.
# On branch master
Changes to be committed:
new file: ReadMe.md在编辑器中记述提交信息的格式如下(可以不用遵守这个格式,此格式只是提高可读性):
- 第一行:用一行文字简要描述提交信息
- 第二行:空行
- 第三行以后:记录更改的原因和详细的内容
只要按照上面的格式输入,今后便可以通过确认日志命令或者工具看到这些记录。命令示例:
1
2
3
4
5
6
7
8
9
10这是一行注释
这是详细的注释信息,可以一直写,都是文件更改注释描述
Please enter the commit message for your changes. Lines starting
with '#' will be ignored, and an empty message aborts the commit.
# On branch master
Changes to be committed:
new file: ReadMe.md在以
#
标为注释的Changes to be committed(要提交的更改)栏中,可以查看本次提交中包含的文件。将要提交信息按格式记录完毕后,保存然后关闭编辑器,以#
标注为注释的行不必删除,随后,刚才记录的提交信息就会被提交。中止提交
如果在编辑器启动后想中止提交,直接将提交信息留空关闭编辑器即可。
查看提交后的状态
执行完毕
git commit
命令后再来查看当前状态:
git log
——查看提交日志git log
命令可以查看以往仓库中的提交日志。包括什么人在什么时候进行了提交和合并,以及操作前后的差别等。现在来查看之前git commit
的命令是否被记录了:可以看到已经记录了我们的提交记录。**
commit
后面显示的“4b68….”,是指向这个提交的哈希值。Git
的其他命令中,在指向提交时也会用到这个哈希值。Author
是提交人的用户名和邮箱地址。Date
栏显示的提交时间,然后后面就是提交的信息。**只显示提交信息的第一行
如果只想让程序显示第一行简述信息,可以在
git log
命令后加上--pretty=short
。1
git log --pretty=short
只显示指定目录,文件的日志
只要在
git log
命令后面加上目录名或者文件名就可以显示于该目录或者文件相关的记录。1
2命令示例:
git log ReadMe.md显示文件的改动
如果想查看文件提交所带来的改动,可以加上
-p
参数。1
git log -p
如果只想查看某文件的提交日志以及前后差异可以使用如下格式:
1
2
3
4
5命令格式:
git log -p 文件名
命令示例:
git log -p ReadMe.md
git diff
——查看更改前后的差别git diff
命令可以查看工作树,暂存区,最新提交之间的差别。现在我们在之前提交的ReadMe.md中写点东西:
1
这是新写的东西
查看工作树和暂存区的区别
执行
git diff
命令,查看工作树与暂存区的差别。因为我们并没有使用
git add
命令将工作树的文件添加到暂存区,所以程序只显示工作树与最新提交状态之间的差别。+
号标出的是新添加的行,被删除的行用-
标出,由此可以看出只添加了一行。现在使用
git add
将修改后的文件添加到暂存区查看工作树和最新提交的差别
如果我们现在执行
git diff
命令,由于工作树和暂存区的状态没有差别,结果是什么也不会显示。要查看与最新提交的差别,需要执行如下命令:1
git diff HEAD
然后我们就可以直接进行提交
git commit
了,提交完毕后使用git log
命令检查一下提交是否被记录。
如果进入
git log
日志最后发现了<End>
,说明进入日志文件,这个时候按q
就可以退出
分支的操作
在进行多个并行作业时,我们会用到分支。在这类并行开发的过程中,往往同时存在多个最新代码状态。如下图所示,从master
分支创建分支A和分支B后,每个分支中都拥有自己的最新代码。master
分支是Git默认创建的分支,因此基本上所有开发都是以这个分支为中心进行的。
不同的分支中,可以同时进行完全不同的作业。等该分支的作业完成之后再与master
分支合并。比如A分支的作业结束后再与master
合并。如下图:
git branch
——显示分支一览表git branch
命令可以将分支名列表显示,同时可以确认当前所在的分支。可以看到
master
分支左侧有*
号,表示这是我们当前所在的分支。也就是说,我们正在master
分支下进行开发。结果中没有显示其他分支名,表示本地仓库中只存在master
一个分支。git checkout -b
——创建,切换分支如果想以当前的
master
分支为基础创建新的分支,需要用到git checkout -b
命令。命令格式如下:1
git checkout -b 需要创建的分支名称
实际上
git checkout -b
命令,是下面的命令合并效果:1
2
3
4创建分支test
git branch test
指针切换到分支test,之前一直默认在主分支即master上
git checkout test如果在
test
分支的左边有*
号,则表示目前指针指向该分支,如果这个时候正常开发修改代码,再提交的话,代码就会提交到test
分支下。像这样不断对一个分支进行提交的操作,称为“培育分支”。如果我们想要快速切换回上一个分支,可以使用
-
代替分支名,命令示例:1
git checkout -
特性分支
Git与SVN等集中型版本管理系统不同,创建分支不需要连接中央仓库,所以能够轻松创建分支。因此,当今大部分工作流程中都用到了特性(Topic)分支。
特性分支顾名思义,是集中实现单一特性(主题),除此之外不进行任何作业的分支。在日常快开发中,往往会创建数个特性分支,同时在此之外再保留一个随时可以发布软件的稳定分支。稳定分支的角色同时由
master
分支担当。例如之前创建的分支A,这一分支主要实现A需求,除A需求之外不进行任何作业,即便在开发过程中发现了BUG,也需要创建新的分支,在新的分支中进行。
基于特定主题的作业在特性分支中进行,主题完成后再与
master
分支合并。只要保持这样一个开发流程,就能保证master
分支可以随时供人查看。这样一来,其他开发者也可以放心大胆的从master
分支创建新的特性分支。主干分支
主干分支是特性分支的原点,同时也是合并的终点。一般来说是以
master
分支作为主干分支。git merge
合并分支现在假设我们前面创建的
test
分支以及实现完毕了,想要将它合并到主干分支master
中,首先切换到master
分支上:1
git checkout master
然后合并
test
分支。为了在历史记录记录下来本次分支合并,需要创建合并提交。**因此,在合并时加上--no-ff参数
**。1
git merge --no-ff test
然后编辑器弹出,添加一定的描述,关闭编辑器后,就完成分支合并了。
git log --graph
——以图表形式查看分支用
git log --graph
命令查看,能很清楚地看到很多信息。
更改提交的操作
git reset
——回溯历史版本
在前面基础操作,以及可以在实现功能后进行提交,累积提交日志作为历史记录,借此不断培育一款软件。
Git的另一特性是可以灵活的操作历史版本,借助分散仓库的优势,可以在不影响其他仓库的前提下对历史版本进行操作。
现在进行版本回溯,然后再创建一个B
特性分支,Git目前结构如下:
我们需要先回溯到分支A
的之前的分支节点,然后创建一个名为B
的特性分支。要让仓库的HEAD,暂存区,当前工作树回溯到指定状态,需要使用git reset --hard
命令。只要提高目标时间点的哈希值。就可以完全恢复到该时间点状态。命令示例:
1 | 需要注意的是,每个哈希值不一样,操作的时候看自己的哈希值 |
现在来创建新分支B,并且在新分支中,在ReadMe.md文件中添加一些文字,然后提交ReadMe.md文件。
现在分支状态如下:
现在需要恢复到分支A
合并后的状态,使用git log
只能查看当前状态为终点的历史记录。所以需要使用git reflog
命令,查看当前仓库的操作日志。在日志中找出回溯历史之前的哈希值,然后通过git reset --hard
命令回溯到历史之前的状态。
从日志里可以看到我们所有的操作,只要不进行Git的GC(Garbage Colloection,垃圾回收)就可以通过日志随意调取历史状态。可以了解到我们要恢复的时间点哈希值为:7092c19b410daac6937a906966b78565e6432771,其实我们只需要输入前面的7092c19即可执行恢复。
恢复后的状态:
消除冲突
现在只需要合并分支B
就可以满足我们想要的状态:
执行合并后:
这是,系统告诉我们ReadMe.md合并过程中发生冲突(Conflict)。系统在合并ReadMe.md文件时,分支A更改的部分和分支B更改的部分发生了冲突。
所以需要解决冲突来完成合并。
查看冲突部分并将其解决
使用编辑器打开ReadMe.md查看文件,会发现文件的内容变成如下示例类似样子:
==========
以上的部分是当前HEAD的内容,以下的内容是要合并的分支B中的内容,现在将其该成我们想要的样子现在内容被修改成了我们想要保存的样子,冲突解决
提交最后的解决方案
现在来执行
git add
命令和git commit
命令来完成提交
git commit --amend
——修改提交信息
要修改上一条提交信息,可以使用git commit --amend
命令,执行该命令后,会弹出编辑器,对内容进行修改后,关闭编辑器即可完成修改。
git rebase -i
——压缩历史
在合并特性分支之前,如果发现已提交的内容中有些许拼写错误等,不妨提交一个修改,然后将这个修改包含到前一个提交之中,压缩成一个历史记录。
现在我们新开一个分支C
并且修改其中的内容来作为后面修正。
然后提交故意修改错误的内容,可以通过如下命令来一次执行到暂存区再到commit
1 | git commit -am |
修正拼写错误
现在来修正刚刚的错误,修正完毕后查看修正前后的差别。
然后进行提交
这种后来进行修正的错误,我们不希望在历史记录中看到这类提交,因为健全的历史记录不需要它们。如果在第一次合并中就发现并修正这些错误,也就不会出现这类提交了。
更改历史
因此,我们需要来更改历史。将修正的内容与之前的提交合并,在历史记录合并为一次完美的提交。因此,我们需要使用
git rebase
命令:1
git rebase -i HEAD~2
用上述命令,可以选定当前分支包含HEAD(最新提交)在内的两个最新历史记录为对象,并在编辑器中打开。
将6aca246的修正错误历史记录压缩到92d473d的ReadMe.md中,按照下述的方法,将6aca246左侧的
pick
部分删除,改为fixup
。1
2pick 92d473d ReadMe.md
fixup 6aca246 修正了部分错误修改完成后,关闭编辑器,系统提示
Rebase
成功:也就是说将两个提交作为对象,合并到了第一次提交的时候成了一个新的提交。
现在再查看提交日志会发现最新的提交日志已经合并一个切之前写的描述信息也已经不见了,这证明提交已经被修改了。
合并到
master
分支现在将分支
C
可以合并到主分支master
了。
推送至远程仓库
Git是分散型版本管理系统,前面的操作都是针对单一本地仓库的操作。现在来尝试对远程仓库进行操作。现在首先在GitHub上创建一个仓库,不需要勾选README
选项。仓库名称随意,此处演示远程仓库名称为TestGit。
git remote add
——添加远程仓库
**在GitHub上创建的仓库路径为”git@github.com:用户名/仓库名称.git”**。现在来使用git remote add
命令将其设置成本地仓库的远程仓库。
1 | 命令格式: |
根据上述命令,将地址为:git@github.com:L-Seraphine/TestGit.git远程仓库的名称设置为:test(标识符)。可以通过标识符快速表示该地址的远程仓库。
git push
——推送至远程仓库
推送至
master
分支如果想将当前分支下本地仓库中的内容推送给远程仓库,需要用到
git push
命令。现在假定我们在master
分支下进行操作。1
git push -u test master
执行如上命令,当前分支的内容会被推送给远程仓库TestGit的
mastet
分支。-u
参数可以在推送时,将远程仓库的master
分支设置为本地仓库当前分支的upstream(上游)。添加这个参数,将来运行git pull
命令从远程仓库获取内容时,本地仓库的这个分支就可以直接从远程仓库的master
分支获取内容,省去了另外添加参数的麻烦。执行该操作后,当前本地仓库的
master
分支的内容就会被推送到GitHub的远程仓库中。推送至
master
以外的分支除了
master
分支之外,远程仓库也可以创建其他分支。现在我们在本地仓库创建分支D
,并将它推送至远程的同名分支。现在,在远程仓库的GitHub页面就可以看到分支
D
了。
从远程仓库获取
上部分我们将GitHub上的远程仓库push
了分支D。现在,所有能够访问这个远程仓库的人都可以获取分支D
并加以修改。
git clone
——获取远程仓库
git clone
命令在很早之前就使用过,语法格式和操作方法不再说明。
执行git clone
命令后默认我们会处于master
分支下,同时系统会自动将origin
设置为该远程仓库的标识符。也就是说,当前本地仓库的master
分支和远程仓库的master
分支的内容是完全一致的。
我们使用git branch -a
命令查看当前分支的相关信息。添加-a
参数可以同时显示本地仓库和远程仓库的分支信息。
获取远程仓库的分支D
使用如下命令将指定获取远程仓库的分支:
1 | git checkout -b D origin/D |
现在假定我们是另一个开发者,需要做一个新的提交。在ReadMe.md文件中添加一行文字,如下:
然后提交即可:
然后远程推送即可:
git pull
——获取最新的远程仓库分支
当远程仓库的内容更新了,需要拉取,则可以通过如下命令:
1 | git pull 远程仓库标识符 分支名称 |
来获取远程仓库,对本地仓库进行更新。