Git组成

Git是一个免费、开源的开源分布式版本控制系统,它可以高效快速地处理大小项目。它易于学习、占用空间极小性能而又极为出色,超越了像Subversion、CVS、Perforce和ClearCase这样的SCM(soft configuration management)工具,具有像便宜的本地分支、方便的存储区域和多个工作流这样的特性。

一、Git简介

Git是一个分布式系统,即不同于集中式系统将所有的版本存放在中央服务器里,而是每一个设备上都是一个完整的版本库,这样做的好处不但使你不必依赖于互联网与中央服务器进行版本控制,而且即使一个设备损坏,只要在其他设备中存在你的项目,也不会造成你的数据丢失事故。

当然在实际使用过程中,会选择一台设备作为“中央服务器”(例如github服务器),但这样只是为了有利于你的数据存储、携带以及团队之间的合作开发,并不是真正的中央服务器。

1.1 Git的构成

Git结构

workspace:工作区
index/stage:暂存区
repository:本地仓库
remote:远程仓库

  1. add:将文件从工作区添加到暂存区;
  2. commit:将文件从暂存区提交到本地仓库;
  3. push:将文件本地仓库同步到远程仓库;
  4. pull: git fetchgit merge 的简写,从远程仓库获取代码并合并本地的版本;
  5. fetch/clone:clone将项目克隆到本地,fetch更新远程仓库文件到本地仓库;
  6. checkout:从版本库中恢复文件到工作区

index和repository共同组成版本库(本地)

1.2 简单使用

  • 在当前目录下新建git仓库
    1
    git init
  • 添加文件到暂存区中
    1
    git add fileName
  • 提交文件到本地仓库中
    1
    git commit -m "commit information"
  • 推送本地仓库内容至远程仓库
    1
    git push origin master

1.3 开发中推荐使用流程

  • 贮存本地修改:

    1
    git stash
  • 获取远程仓库代码到本地(不合并):

    1
    git fetch origin
  • 合并远程dev分支到本地分支:

    1
    git rebase origin/dev
  • 恢复本地修改:

    1
    git stash pop

    若发生冲突,解决后,然后 git add .

  • 提交代码:

    1
    git commit -m “update file” 
  • 推送代码

    1
    git push origin master:dev

二、版本控制

利用Git我们可以对项目的文件进行存储、对文件的修改进行控制,是软件开发过程中必备的开发工具。

2.1 差异比较

Git管理的是修改,而并非文件本身,我们可以利用git status我们可以比较workspace和index文件的差异,但是如何比较具体文件的差异时,我们需要用到git diff 命令

  • git status:比较workspace和index中的差异

  • git diff (filename):比较workspace和index中文件的差异

  • git diff --cached (filename)git diff --staged (filename):比较index和上一次提交的差异

  • git diff HEAD (filename):比较workspace和repository的差别

    HEAD指向repository中最新的提交的版本

  • git diff --stat:统计各个文件的具体变化情况worksapce和index

  • git diff --numstat:统计各个文件中变化的行数

git diff输出格式分析

例如这里有一个文件gitDiffDemo.txt,有内容如下:

1
2
3
4
5
6
7
8
9
DragonBall1
DragonBall2
DragonBall3
DragonBall4
DragonBall5
DragonBall6
DragonBall7
DragonBall8
DragonBall9

将此文件提交至暂存区$ git add gitDiffDemo.txt后,将内容修改如下:

1
2
3
4
5
6
7
8
9
DragonBall1
DragonBall2
DragonBall3
DragonBall4
DragonBall5s
DragonBall6
DragonBall7
DragonBall8
DragonBall9

此时使用$ git diff gitDiffDemo.txt查看异同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git diff gitDiffDemo.txt 
diff --git a/gitDiffDemo.txt b/gitDiffDemo.txt
index 9da8892..fa04dee 100644
--- a/gitDiffDemo.txt
+++ b/gitDiffDemo.txt
@@ -2,7 +2,7 @@ DragonBall1
DragonBall2
DragonBall3
DragonBall4
-DragonBall5
+DragonBall5s
DragonBall6
DragonBall7
DragonBall8

让我们来逐行来看输出内容

1
index 9da8892..fa04dee 100644

9da8892..fa04dee表示暂存区具体文件(此处指当前文件夹下的gitDiffDemo.txt)的哈希值,100644表示对象模式,100表示普通文件(regular file),644表示文件操作权限,即rw-r--r--

101: symbolic link 110: git link

1
2
--- a/gitDiffDemo.txt
+++ b/gitDiffDemo.txt

--- 表示变化前的版本(提交过的)
+++ 表示变化后的版本(修改过的)

1
@@ -2,7 +2,7 @@ DragonBall1

一对@@表示一个差异小结,一个差异小结的范围为变更位置上下3行,即一般为7行,上下行内容不足或两个差异点距离过近这个范围可能会缩小或变大。
-2,7: 表示变化前的文件中,差异小结范围为第2行开始连续7行
+2,7: 表示变化后的文件中,差异小结范围为第2行开始连续7行

“DragonBall1”内容为差异小结范围上一行,不用理会。

2.2 快照Id

在Git中,针对每一次commit都会生成相应的快照,即在Git系统中我们可以选择恢复某一个时间点上的版本快照以此达到版本控制的目的,使用git log命令查看commit历史

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
commit 7361c5145897d215c7bd9e9cf5a13c59f5908e7d
Author: WyattWang <cc7722@foxmail.com>
Date: Sun Nov 29 20:14:41 2020 +0800

git tracks changes

commit 4dbe0ff1cd9ade95b0ca83e22ac6dc27619b1cd9
Author: WyattWang <cc7722@foxmail.com>
Date: Sun Nov 29 16:38:14 2020 +0800

append GPL

commit 1076977642fedc84ef89be1a840850b991e434c7
Author: WyattWang <cc7722@foxmail.com>
Date: Sun Nov 29 16:17:24 2020 +0800

add distributed

commit a2fdd707b60ff78bada28410dc0ca8b6dd027e43
Author: WyattWang <cc7722@foxmail.com>
Date: Sun Nov 29 14:11:51 2020 +0800

wrote a readme file

如上所示,每一小段commit信息从上往下包含了commit id、提交者信息、commit时间和commit信息。
如果只是想查看commit id和commit信息,加上--pretty=oneline参数即可

1
2
3
4
5
$ git log --pretty=oneline
7361c5145897d215c7bd9e9cf5a13c59f5908e7d git tracks changes
4dbe0ff1cd9ade95b0ca83e22ac6dc27619b1cd9 append GPL
1076977642fedc84ef89be1a840850b991e434c7 add distributed
a2fdd707b60ff78bada28410dc0ca8b6dd027e43 wrote a readme file

2.3 版本回退

在Git中,HEAD表示当前版本,上一个版本为HEAD^HEAD~1,上上一个版本为HEAD^^HEAD~2,上上个版本为HEAD^^^HEAD~3……

使用git reset命令可以让版本进行回退, 例如回退到上一版本

1
2
$ git reset --mixed HEAD^ 
$ git reset --mixed HEAD~1

回退到指定commit id的版本

1
$ git reset --mixed 4dbe0ff1cd9ade95b0ca83e22ac6dc27619b1cd9

如果从版本B回退到之前的版本A,在A之后的提交信息将会在git log中查询不到commit id,但是如果在当前命令行窗口还可以看到版本B的commit id,或者使用git reflog可以看到版本变动的过程及其id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git reflog
cde5cd3 HEAD@{0}: reset: moving to cde5cd3cf18629fbc9d9f033447478b90355224c
4dbe0ff HEAD@{1}: reset: moving to HEAD^^
d5c7d80 HEAD@{2}: reset: moving to HEAD~1
cde5cd3 HEAD@{3}: commit: 提交2
d5c7d80 HEAD@{4}: commit: 提交
7361c51 HEAD@{5}: commit: git tracks changes
4dbe0ff HEAD@{6}: reset: moving to 4dbe0ff
a2fdd70 HEAD@{7}: reset: moving to a2fdd70
4dbe0ff HEAD@{8}: reset: moving to 4dbe0ff1cd9ade95b0ca83e22ac6dc27619b1cd9
1076977 HEAD@{9}: reset: moving to HEAD^
4dbe0ff HEAD@{10}: commit: append GPL
1076977 HEAD@{11}: commit: add distributed
a2fdd70 HEAD@{12}: commit (initial): wrote a readme file

版本回退的三种模式:

  • mixed: 默认模式,恢复指定版本,保留工作区,清空缓存区,若再次commit则需要重新add
  • soft: 恢复指定版本,保留工作区和缓存区,可直接再次commit
  • hard: 恢复到指定版本,删除工作区和缓存区

在Git中除了使用git reset命令以外,还可以使用git revert命令来达到类似的的效果,但这两者还是有所区别:

  • git reset是将版本回退到之前的版本,但那个版本之后的版本将不会被保留

  • git revert是将指定版本反做,例如按照时间线的版本为a->b->c, 当HEAD为版本c时反做版本b,即在提交版本b时,添加了file1.txt,则反做就是执行相反操作,删除file1.txt

    如果在一个文件中多次修改内容并且进行提交,反做非最后一次提交时,会提示发生冲突,需要手动解决

Git恢复之前版本的两种方法reset、revert(图文详解)

2.4 撤销修改

如果需要撤销在workspace中的修改,可以使用git checkout -- filename命令(注意文件名前的空格)

1
$ git checkout -- readme.md 
  • 如果readme.md自修改后第一次放进index中,则git checkout后版本和repository中的版本是一致的
  • 如果readme.md之前放进过index中,则git checkout后版本和前一次添加到index中的版本保持一致

git checkout后的版本和最新的git addgit commit的文件保持一致

如果需要撤销workspace添加到index中的修改,则可已使用git reset HEAD filename命令

1
$ git reset HEAD readme.md

git reset 不仅可以回退版本,还可以回退index中的修改,即将repository中最新的版本HEAD替换掉index中修改过的文件。

2.5 删除文件

如果工作区中的文件和版本库中的版本一致,使用git rm <filename>命令删除工作区文件,并且将这次删除放入暂存区

1
2
$ git rm readme.md
$ git commit -m "rm readme.md"

如果在workspace中修改过文件,无论有没有添加到index中,即工作区和版本库中文件版本不一致,单纯使用git rm命令会出现错误,此时需要加上参数-f,即使用git rm -f <filename>进行删除

1
$ git rm -f readme.md

如果只是想删除版本库中的文件,但保留工作区文件,则使用git rm --cached <filename>可以删除暂存区文件,并将这次删除的操作添加到暂存区中,即此时该文件处于untracked状态

1
2
$ git rm --cached readme.md
$ git commit -m "untrack reame.md"

2.6 修改历史commit中的Author和Email

1
2
3
4
5
6
$ git filter-branch -f --env-filter "
GIT_AUTHOR_NAME='newName';
GIT_AUTHOR_EMAIL='newEmail@example.com';
GIT_COMMITTER_NAME='oldName';
GIT_COMMITTER_EMAIL='oldEmail@example.com';
" HEAD

三、远程仓库

虽然Git是一个分布式的版本控制工具,并不存在着中央服务器,但是在实际使用中还是存在一个远程仓库,方便用于同步代码或协同开发的。

3.1 添加远程仓库

使用git remote add <仓库名称> <仓库地址>命令可以建立本地仓库和远程仓库的连接

1
$ git remote add origin git@github.com:server-name/learn-git.git

添加完成后可以使用git remote -v查看已关联的远程仓库信息

1
2
3
$ git remote -v
origin git@gitee.com:server-name/learn-git.git (fetch)
origin git@gitee.com:server-name/learn-git.git (push)

以及可以使用git remote rename <old name> <new name>来更新远程仓库名称和使用git remote remove <name>来移除远程仓库名称

3.2 推送文件

使用git push <远程主机名> <本地分支名>:<远程分支名>命令可以推送本地仓库文件至远程仓库,特别的使用加上参数-u,可以给推送的分支加上(跟踪)引用,即使当前分子和多个远程主机存在关联,后面也可以不加任何参数使用git push推送到和本次相同的远程仓库中。

1
2
$ git push -u origin master #第一次
$ git push #后续可简写

如果远程仓库地址为ssh url,则需要提前配置当前主机和远程主机的ssh公私钥

特别的,使用git push -f origin master命令将本地文件推送到远程,并强制覆盖,使用时需要特别小心,谨慎使用

3.3 代码克隆

使用git clone <远程仓库地址>命令可以将远程仓库的文件克隆到本地

1
$ git clone git@gitee.com:server-name/learn-git.git

参考

https://www.cnblogs.com/ims-/p/9747333.html
https://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html
https://blog.csdn.net/qq_42780289/article/details/98353792