Git简介、配置及基础

最近工作中需要用到Git,之前的大部头技术书年代过于久远,按出版日期排序找到了一本《Pro Git》,反正Git这东西会用就行了,就这里简单做个备份。

一、Git是什么

1、Git简要介绍

远古时代手动备份:1.0版、2.0版、再也不改版、打死也不改版、阿西吧版……

而Git作为新一代版本管理系统,可以自由地返回过去的状态,以及记录每一个状态的变更细节,同时支持多人协同共用修改并且记录每个人的修改历史记录。

2、Git存储方式

Git之前,其它版本控制系统大都采用基于差异(delta-based)的方式存储版本,即记录每个文件与初始版本的差异。而Git 更像是把数据看作是对小型文件系统的一系列快照,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。

3、Git的特性

  • 近乎所有操作都是本地执行:Git本地仓库就有项目的完整历史
  • 不需网络即可向本地仓库提交,等有网络时再上传
  • Git通过哈希校验保证数据的完整性
  • 一般只添加数据而不会删除数据,一旦提交到Git中就很难丢失数据

4、Git的三种状态

  • 已修改:修改了文件但未保存到数据库中。对应工作区
  • 已暂存:标记已修改文件的当前版本,包含在下次提交的快照中。对应索引,也就是通俗所讲的暂存区
  • 已提交:已保存到本地数据库中。对应Git仓库

5、Git的工作流程

二、Git安装

见我的博客,有WindowscentOS的详细安装步骤。
链接直达:Git安装与环境配置

三、初次运行Git前的配置

1、配置文件

  • 全局配置:/etc/gitconfig,所有用户及他们仓库的通用配置。命令:git config --system
  • 当前用户配置:~/.gitconfig 或 ~/.config/git/config,当前用户及他们仓库的配置。命令:git config --global
  • 当前仓库配置:.git/config,当前仓库的配置,默认用的就是这个。命令:git config --local

以上每一级别都会覆盖上一级别的配置。

可以通过以下命令查看所有的配置以及它们所在的文件:

$ git config --list --show-origin

2、保存用户信息

每一次的Git提交都会使用这些信息,在多人团队协作中时可以看到哪些东西是由谁提交的,一旦设置不可更改。

(1)全局配置

$ git config --global user.name "名字"
$ git config --global user.email 邮箱

(2)针对特定项目的配置
在项目目录下运行没有global的命令即可。

$ git config user.name "名字"
$ git config user.email 邮箱

3、获取帮助

$ git help <verb>

#例如,要想获得 git config 命令的手册,执行以下命令即可
$ git help config

四、Git基础

1、获取Git仓库

(1)在已存在目录中初始化仓库

在要建立仓库的目录下运行Git bash,执行:

$ git init

如果在一个已存在文件的文件夹(而非空文件夹)中进行版本控制,你应该开始追踪这些文件并进行初始提交。

# 全部提交
$ git add .
# 提交部分文件
$ git add 文件名.扩展名
# 提交信息
$ git commit -m '初始版本'

(2)克隆现有的仓库

当你执行 git clone 命令的时候,默认配置 下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。 事实上,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库。

# 常规克隆命令
$ git clone <url>
# 自定义本地仓库的名字
$ git clone <url> 本地仓库名字

常规克隆命令会在当前目录下创建一个以项目名字命名的目录,并在这个目录下初始化一个 .git 文件夹, 从远程仓库拉取下所有数据放入 .git 文件夹,然后从中读取最新版本的文件的拷贝。

2、记录每次更新到仓库

在成功创建仓库后,Git会监控仓库内文件的状态,当文件被修改后,Git 就将它们标记为已修改文件。在工作时,你可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此反复。

为了例子演示,这里创建了一个文件夹,里面有0.txt1.txt。在初始化仓库后,执行以下命令。

(1)检查当前文件状态

这个只能查看未提交或提交后又做了修改的文件。

$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        0.txt
        1.txt

nothing added to commit but untracked files present (use "git add" to track)

在状态报告中可以看到0.txt1.txt文件出现在 Untracked files 下面,这说明现在的仓库中没有这两个文件,Git 不会自动将之纳入跟踪范围。

当然,这样显示可能会有些繁琐,使用git status -s或者git status --short可以帮你缩短状态命令的输出,以简洁的方式查看更改。大致形式如下:

$ git status -s
# 前面有两列字母,左栏指明了暂存区的状态,右栏指明了工作区的状态
# 右栏没有“M”时,也就代表目前没有未添加到暂存区的改动,可以提交到仓库了

?? LICENSE.txt        #新添加的未跟踪文件
A  lib/git.rb        #已添加到暂存区中的文件
AM lib/git.rb        #添加到暂存区后,又做了修改且修改未添加到暂存区的文件
 M README        #从暂存区提交后,又做了修改且修改未添加到暂存区的文件
M  README        #从暂存区提交后,又做了修改且修改已添加到暂存区
MM Rakefile        #上面一步之后又做了修改,且修改未提交到暂存区

(2)跟踪新文件

使用命令 git add 开始跟踪一个文件。git add 命令使用文件或目录的路径作为参数;如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件。

$ git add 0.txt

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   0.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        1.txt

再运行 git status 命令,会看到0.txt文件已被跟踪,并在Changes to be committed下面,说明此文件目前处于暂存状态。

(3)暂存已修改的文件

修改0.txt文件,在里面输入文本“从0开始”。再次查看状态:

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   0.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   0.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        1.txt

文件0.txt同时出现在了Changes to be committed Changes not staged for commit 下面,这是因为Git 只暂存了你运行 git add 命令时的版本,后面修改了文件之后,已跟踪文件的内容发生了变化,但还没有放到暂存区,所以就同时出现了两个版本。要暂存这次更新,同样需要运行 git add 命令。这里顺便把1.txt也暂存了。

$ git add 0.txt
$ git add 1.txt

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   0.txt
        new file:   1.txt

现在两个文件都已暂存,下次提交时就会一并记录到仓库。

(4)忽略文件

一些比如日志文件,或者编译过程中创建的临时文件无需纳入 Git 的管理,我们可以创建一个名为 .gitignore的文件,列出要忽略的文件的模式。

文件 .gitignore 的格式规范如下:

  • 所有空行或者以 # 开头的行都会被 Git 忽略。
  • 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
  • 匹配模式可以以(/)开头防止递归。
  • 匹配模式可以以(/)结尾指定目录。
  • 要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。
glob 模式是指 shell 所使用的简化了的正则表达式。
匹配字符:星号(*)匹配零个或多个任意字符;[abcd] 匹配任何一个列在方括号中的字符 ; 问号(?)只匹配一个任意字符; 如果在方括号中使用短划线分隔两个字符, 表示所有在这两个字符范围内的都可以匹配 (比如 [0-9] 表示匹配所有 0 到 9 的数字)。
匹配目录:使用两个星号()表示匹配任意中间目录,比如 a//z 可以匹配 a/z 、 a/b/z 或 a/b/c/z 等。

举个栗子:

# 忽略所有的 .a 文件
*.a
# 跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
!lib.a
# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
/TODO
# 忽略任何目录下名为 build 的文件夹
build/
# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
doc/*.txt
# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
doc/**/*.pdf

GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表, 你可以在https://github.com/github/gitignore 找到它。

(5)查看已暂存和未暂存的修改

想要查看具体修改了什么地方,可以用 git diff 命令,它能通过文件补丁的格式更加具体地显示哪些行发生了改变。

我这里对4.txt文件末尾加了一句话“我这里新加了一点东西”。

$ git diff
diff --git a/4.txt b/4.txt
index dd1de8d..26e4c2b 100644
--- a/4.txt
+++ b/4.txt
@@ -1 +1,2 @@
-4123
\ No newline at end of file
+4123
+我这里新加了一点东西
\ No newline at end of file

不加参数直接输入 git diff,此命令比较的是工作目录中当前文件和暂存区域快照之间的差异。 也就是修改之后还没有暂存起来的变化内容。

如果再用git add暂存之后,git diff就为空了。此时,要用git diff --staged或者git diff --cached,比对已暂存文件与最后一次提交的文件差异。

$ git diff --staged
diff --git a/4.txt b/4.txt
index bf0d87a..26e4c2b 100644
--- a/4.txt
+++ b/4.txt
@@ -1 +1,2 @@
-4
\ No newline at end of file
+4123
+我这里新加了一点东西
\ No newline at end of file

(5)提交更新

提交时记录的是放在暂存区域的快照。 任何还未暂存文件的仍然保持已修改状态。

每次准备提交前,先用 git status 看下,你所需要的文件是不是都已暂存起来了, 然后再运行提交命令git commit。写提交信息是个好习惯,方便自己回溯也方便他人理解你的更改。

$ git commit -m "提交信息"

每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。用过虚拟机的应该就清楚了,快照是真的香!

(6)移除文件

一般来说,要从 Git 中移除某个文件,就必须从暂存区域移除,然后提交。Git提供了git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样就不会存在文件在Changes not staged for commit清单中了。

  • 一般移除文件:$ git rm 文件
  • 强制删除文件:之前修改过或已经放到暂存区,$ git rm -f 文件,用于防止误删尚未添加到快照的数据,这样的数据不能被 Git 恢复。
  • 从仓库中删除,但依然保留在工作目录中:$ git rm --cached 文件,这里的文件与 .gitignore 类似,也可以用 glob 模式匹配。注意:这里用 glob模式 时,星号 * 之前需要加反斜杠 “\”。

(7)移动或重命名文件

Git 并不显式跟踪文件移动操作,如果重命名一个文件,Git 的做法是先将旧文件改名为新文件,再用git rm从暂存区中删除旧文件,之后用git add把新文件移动到暂存区中。

不过,Git 只需要一行命令即可完成这个工作:

$ git mv 旧文件 新文件
# 等价于
$ mv 旧文件 新文件
$ git rm 旧文件
$ git add 新文件

3、查看提交历史

常用的查看提交历史的方法如下,注意这些方法都能彼此组合叠加实用:

  • 按时间先后顺序列出所有的提交:git log,最近的更新排在最上面
  • 显示每次提交所引入的差异:git log -p -2,这就是显示最近的两次提交,并按 补丁 的格式输出
  • 查看提交中被修改文件的具体修改信息:git log --stat,可以看到在每次提交的下面列出所有被修改过的文件、有多少文件被修改了以及被修改过的文件的哪些行被移除或是添加了
  • 格式化输出:git log --pretty=参数,可以使用不同于默认格式的方式展示提交历史

    • 每个提交放在一行显示:git log --pretty=oneline,查看大量提交时很有用
    • 定制记录的显示格式:如git log --pretty=format:"%h - %ad , %ar : %s" --date=format:'%Y-%m-%d'就输出了提交的简写哈希值、作者名字、作者修订日期年月日和提交说明的信息。(注:还有一个 %cn 是提交者,作者是实际修改的人,提交者是把修改并入项目的人)
    • 在日志旁以 ASCII 图形显示分支与合并历史:git log --pretty=format:"%h %s" --graph
    • 限制输出长度:用 --since、--after --until、--before

      • 近两周提交:git log --since=2.weeks
      • 2022.7.27到2022.7.28的提交:git log --since="2022-07-27" --until="2022-07-28"
  • 查询匹配指定条件的日志:

    • 只显示最近的n调提交:git log -n
    • 只显示指定作者的提交:git log --author ""
    • 只显示指定提交者的提交:git log --committer ""
    • 只显示提交说明中包含指定字符串的提交:git log --grep ""
    • 只显示添加或删除了该字符串的提交:$ git log -S "",可用于找出添加或删除了某一特定函数的引用的提交。
    • 只显示某些文件或目录的提交:git log -- 文件或目录

4、撤销操作

(1)修补提交

修补提交就是用一个新的提交替换旧的提交,它可以改进最后的提交,而不会让忘记添加文件之类的小修改弄乱仓库历史。一般用git commit --amend命令修补提交。

例如,提交后发现忘记了暂存某些需要的文件或修改,就可以用下面的操作重新提交,最后的结果是保留第二次提交的文件和第一次提交的信息。

$ git commit -m "第一次提交的信息"
$ git add 忘记暂存的文件或修改
$ git commit --amend

(2)取消暂存的文件

有时候偷懒直接用git add *暂存了所有文件,但发现某个文件并不需要,这时可以通过以下命令来取消暂存。

$ git restore --staged 文件

这行命令之后,不需要的文件将重新转为unstaged状态。

(3)撤销对文件的修改

对于未加入暂存区的文件,修改了之后如何“回档”呢?一个一个找未免过于笨拙了,可以用以下命令来撤销所有修改。

$ git restore 文件

命令执行后,Git 将用最近提交的版本覆盖它。所以本地的所有修改将会完全丢失。

在 Git 中任何已提交的东西几乎都是可以恢复的,但是未提交的东西丢失后一般就很难再找回了,所以此命令需谨慎使用。

5、远程仓库的使用

远程仓库可以在本地主机上,也可以在任何能联网的服务器上,它通常是为了便于多人协作而创建的。

(1)查看远程仓库

  • 查看每一个远程服务器的简写:git remote,Git给克隆的仓库服务器的默认名字是 origin
  • 查看全部远程服务器的简写和对应的URL:git remote -v
  • 查看某个远程仓库的更详细信息如URL和跟踪分支的信息:git remote show <remote>,这个命令列出了当你在特定的分支上执行 git push 会自动地推送到哪一个远程分支。 它也同样地列出了哪些远程分支不在你的本地,哪些远程分支已经从服务器上移除了, 还有当你执行 git pull 时哪些本地分支可以与它跟踪的远程分支自动合并。

(2)添加远程仓库

使用git clone命令添加远程仓库时,会默认使用简写origin,想要自定义简写,用下面的命令:

$ git remote add 自定义简写 <url>

之后就可以在命令行中使用自定义简写来代替整个URL了。如git fetch 简写简写/master等。

(3)从远程仓库中抓取与拉取

抓取(fetch)拉取(pull)都是将克隆(或上一次抓取)后新推送的所有工作获取到本地仓库。

  • 必须手动合并:git fetch <remote>,注意此命令不会自动合并或修改当前的工作,。
  • 可以自动合并:git pull <remote>,如果当前分支设置了跟踪远程分支, 那么就可以用此命令来自动抓取后合并该远程分支到当前分支。

(4)推送到远程仓库

以克隆时自动生成的origin简写和master分支为例,以下命令就可以将所做的更改分享到服务器:

$ git push origin master

注意:只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。如果和别人同时推送,必须先抓取之前别人推送的工作并合并到本地工作后才能继续推送。

(5)重命名或移除远程仓库

  • 重命名:git remote rename 旧简写 新简写,这同样也会修改你所有远程跟踪的分支名字,“新简写/master”
  • 移除远程仓库及相关的远程跟踪分支:git remote rm 简写,所有和这个远程仓库相关的远程跟踪分支以及配置信息也会一起被删除。

6、打标签

刷版本号时可以用标签来标记发布结点,如v1.0、v2.0等。

(1)列出标签

如列出 git 1.8.5系列的版本标签,可以运行git tag -l "通配符",得到如下运行结果:

$ git tag -l "v1.8.5*"
v1.8.5
v1.8.5-rc0
v1.8.5-rc1
v1.8.5-rc2
v1.8.5-rc3

(2)查看标签信息及对应的提交信息

git show 标签名

$ git show v1.4-lw
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
  changed the version number

(2)打标签

  • 打附注标签: git tag -a 标签名 -m "存储在标签中的信息"
  • 打轻量标签: git tag 标签名
  • 给当前的提交打轻量标签:git tag 标签名
  • 给过去的提交打轻量标签:git tag -a 要补充的标签 提交的(部分)校验和
    [例子]

    # 提交历史
    $ git log --pretty=oneline
    15027957951b64cf874c3557a0f3547bd83b3ff6 第一次提交
    a6b4c97498bd301d84096da251c98a07c7723e65 第二次提交
    
    # 假设v1.1也就是第二次提交忘记打标签,就可以用以下命令补上
    $ git tag -a v1.1 a6b4c97

(3)推送标签

默认情况下,git push 命令并不会传送标签到远程仓库服务器上。推送到远程服务器需要以下命令:

  • 推送某个标签:git push origin 标签名
  • 推送全部标签:git push origin --tags

(4)删除标签

  • 删除本地标签:git tag -d 标签名
  • 删除远程标签:git push <remote> --delete 标签名
无标签