# Git

# 参考文档

# 必玩小游戏

# 安装

  • 在 Linux 上安装

    如果你想在 Linux 上用二进制安装程序来安装基本的 Git 工具,可以使用发行版包含的基础软件包管理工具来安装。

    • 以 Fedora 为例,如果你在使用它(或与之紧密相关的基于 RPM 的发行版,如 RHEL 或 CentOS),你可以使用 dnf:
      • $ sudo dnf install git-all
    • 如果你在基于 Debian 的发行版上,如 Ubuntu,请使用 apt:
      • $ sudo apt install git-all

# git 的三种状态/ 阶段

# 三种状态

  • 已修改 - 修改了文件,还没有保存到数据库。
  • 已暂存 - 表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
  • 已提交 - 数据已经安全地保存在本地数据库中。

# 三种阶段

以上的三种状态对应Git 项目拥有三个阶段:工作区、暂存区以及 Git 目录。

  • 工作区:对项目的某个版本独立提取出来的内容。
    • 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
  • 暂存区:一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。
    • 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。
  • Git 仓库目录:Git 用来保存项目的元数据和对象数据库的地方。
    • 这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。

# 工作流程

  1. 在工作区中修改文件。
  2. 将你想要下次提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区。
  3. 提交更新,找到暂存区的文件,将快照永久性存储到 Git 目录。

# 帮助

  • Git 命令的综合手册(manpage):
    • git help,bash 下有效
    • git --help
    • man git
  • git config 命令的手册,执行
    • git help config

# Git 基础

# 获取一个仓库

  • 新建,将尚未进行版本控制的本地目录转换为 Git 仓库。
    • 切换到工程的根目录下,再执行 git init
  • 从其它服务器克隆一个已存在的 Git 仓库。
    • git clone <url>

# 记录每次更新到仓库

  • 检查当前文件状态
    • git status 详细展示
    • git -s 或者 git --short 状态简览。
      • 新添加的未跟踪文件前面有 ?? 标记。
      • 新添加到暂存区中的文件前面有 A 标记。
      • 修改过的文件前面有 M 标记。
  • git add 开始跟踪一个文件,如果后面跟的是一个目录,那么将会跟踪该目录下的所有文件。
  • Changes not staged for commit 这行内容下面的文件,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。
  • 要暂存这次更新,需要运行 git add 命令。
  • 忽略文件
    • 创建一个名为 .gitignore 的文件
    • 忽略文件规范
      • 所有空行或者以 # 开头的行都会被 Git 忽略。
      • 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
        • glob 模式是指 shell 所使用的简化了的正则表达式。
          • 星号 * 匹配零个或多个任意字符;
          • [abc] 匹配任何一个列在方括号中的字符
            • 这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c;
          • 问号 ? 只匹配一个任意字符;
          • 如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配
            • 比如 [0-9] 表示匹配所有 0 到 9 的数字. 使用两个星号 * 表示匹配任意中间目录,比如 a//z 可以匹配 a/z 、 a/b/z 或 a/b/c/z 等。
    • 匹配模式可以以 / 开头防止递归。
      • /usr 的意思是只对这一层目录有效。
    • 匹配模式可以以 / 结尾指定目录。
      • 例如: usr/ 的意思就是指定只是 usr 这个文件夹。
    • 要忽略指定模式以外的文件或目录,可以在模式前加上叹号 ! 取反。
  • 查看已经暂存和未暂存的修改
    • git diff,这里以补丁的形式进行展示。
    • git diff 展示的是未暂存的修改(已暂存的不显示)
    • git diff --staged 或者 --cached 参数,展示的是已暂存的修改。
  • 提交文件
    • git commit -m 'information'进行提交,提交时记录的是放在暂存区域的快照。
      • 任何还未暂存文件的仍然保持已修改状态,可以在下次提交时纳入版本管理。
      • 每一次运行提交操作,都是对你项目做一次快照,以后可以回到这个状态,或者进行比较。
    • git commit -a,跳过 add,但是如果是新建的文件还是需要 add。
  • 跳过使用暂存区
    • git commit -a ,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤。
  • 移除文件
    • Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。
    • git rm,移除文件, 删除工作区(本地)文件,并且将这次删除放入暂存区。
      • 注意: 要删除的文件是没有修改过的,并且没加到暂存区的,就是说和当前版本库文件的内容相同。
    • git rm --cached filename,删除暂存区文件,但保留工作区的文件,并且将这次删除放入暂存区。本地文件取消跟踪。
    • git rm -f,删除工作区和暂存区文件,并且将这次删除放入暂存区。
      • 就是说,要删除的文件只要修改过,git rm 就删除不了。删除后再提交,删除版本库。
    • 实例
      • 一个文件 test,使用 rm test 命令在本地删除以后,并没有从暂存区删除。
      • 如果想要从暂存区删除,那么需要 git add test ,将这个文件的变动记录到暂存区,然后 git commit -m 'delete test',到这里,这个文件在暂存区和版本库中也被删除了。
  • 移动文件/重命名
    • git mv file_from file_to
    • 实例
      • git mv README.md README
      • 运行 git mv 就相当于运行了下面三条命令:
        • mv README.md README
        • git rm README.md
        • git add README

# 查看提交历史

  • git log -number,时间最近的在上面,数字限制展示几条。

    • -p 或者 --patch,将每次的改动详情也展示出来,按补丁格式显示每个提交引入的差异。

    • --stat,简要统计信息,显示每次提交的文件修改统计信息。

    • --pretty,选择格式展示,使用其他格式显示历史提交信息。

      可用的选项包括:

      • oneline
      • short
      • full
      • fuller
      • format(用来定义自己的格式)

      例如:

      • git -log --pretty=oneline
        • online 会将每个提交放在一行显示,在浏览大量的提交时非常有用。
        • 另外还有 short,full 和 fuller 。
      • format,定制显示样式
        • 比如:
          • git log --pretty=format:"%h - %an, %ar : %s"
            • %H - 提交的完整哈希值
            • %h - 提交的简写哈希值
            • %T - 树的完整哈希值
            • %t - 树的简写哈希值
            • %P - 父提交的完整哈希值
            • %p - 父提交的简写哈希值
            • %an - 作者名字
            • %ae - 作者的电子邮件地址
            • %ad - 作者修订日期(可以用 --date=选项 来定制格式)
            • %ar - 作者修订日期,按多久以前的方式显示
            • %cn - 提交者的名字
            • %ce - 提交者的电子邮件地址
            • %cd - 提交日期
            • %cr - 提交日期(距今多长时间)
            • %s - 提交说明
    • --shortstat,只显示 --stat 中最后的行数修改添加移除统计。

    • --name-only,仅在提交信息后显示已修改的文件清单。

    • --name-status,显示新增、修改、删除的文件清单。

    • --abbrev-commit,仅显示 SHA-1 校验和所有 40 个字符中的前几个字符。

    • --relative-date,使用较短的相对时间而不是完整格式显示日期(比如“2 weeks ago”)。

    • --graph,在日志旁以 ASCII 图形显示分支与合并历史。

    • --oneline,是 --pretty=oneline --abbrev-commit 合用的简写。

  • 限制输出长度

    • --since, --after,仅显示指定时间之后的提交。

      例如:

      • git log --since=2.weeks,最近两周的。
      • 该命令可用的格式十分丰富。
        • 可以是类似 "2008-01-15" 的具体的某一天
        • 也可以是类似 "2 years 1 day 3 minutes ago" 的相对日期。
    • --until,--before,仅显示指定时间之前的提交。

    • --author,仅显示作者匹配指定字符串的提交。

    • --committer,仅显示提交者匹配指定字符串的提交。

    • --grep,仅显示提交说明中包含指定字符串的提交。

    • -S,仅显示添加或删除内容匹配指定字符串的提交。

    例子

    查看 Junio Hamano 在 2008 年 10 月其间, 除了合并提交之外的哪一个提交修改了测试文件,可以使用下面的命令:

    • git log --pretty="%h - %s" --author='Junio C Hamano' --since="2008-10-01" \ --before="2008-11-01" --no-merges -- t/

# 撤销操作

  • 重新提交
    • git commit --amend,修改上一次提交。
    • 例如,提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
      • git commit -m 'initial commit'
      • git add forgotten_file
      • git commit --amend
    • 最终你只会有一个提交,第二次提交将代替第一次提交的结果。
  • 取消暂存文件
    • git reset,git reset HEAD <file>...
  • 撤销对文件的修改
    • git checkout -- file...,本地文件的任何修改都会消失,Git 会用最近提交的版本覆盖掉它。
    • git reset <重置到哪里>
      • 清空暂存区,撤销 add,本地修改还在
      • 参数可以使用相对 HEAD~1,就是说回退上一位置。修改 HEAD 的位置,即将 HEAD 指向的位置改变为之前存在的某个版本。
      • 回退以后会提示没有任何需要提交的,这个时候强制 push,将会清空远程已有的提交。
      • --hard
        • 清空工作目录和暂存区的改动,本地和暂存区全都撤回到那一刻。
      • --soft
        • 保留工作目录的内容,并把因为保留工作目录内容所带来的新的文件差异放进暂存区。
        • 移动 HEAD 到指定的 commit 节点,但保留 工作区和暂存区的内容,简单来说就是你的代码还在只是变成了未提交状态或未添加状态
    • git revert <需要反做的目标>
      • 撤销 commit,在我们要撤销的提交记录后面居然多了一个新提交!这是因为新提交记录 C2' 引入了更改。这些更改刚好是用来撤销 C2 这个提交的。也就是说 C2' 的状态与 C1 是相同的。(顺序:C0 - C1 - C2 - C2')

# 远程仓库的使用

  • 操作远程仓库
    • origin,Git 给克隆的仓库(远程)服务器的默认名字。
    • git remote,不带参数,列出已经存在的远程分支。
    • git remote -v | --verbose,列出详细信息,在每一个名字后面列出其远程 url,此时,-v 选项(译注:此为 –verbose 的简写,取首字母),显示对应的克隆地址。
    • git remote add <shortname> url,添加一个新的远程 Git 仓库,同时指定一个方便使用的简写。
    • git remote show <remote>,查看某个仓库。
    • git remote rename <修改前> <修改后name>,远程仓库的重命名。
    • git remote rm 或者是 git remote remove,移除一个远程仓库。一旦你使用这种方式删除了一个远程仓库,那么所有和这个远程仓库相关的远程跟踪分支以及配置信息也会一起被删除。
    • git remote set-url origin xxxxx.git 直接修改 url。
    • git remote rm origingit remote add origin xxxxx.git, 实现修改 url。
  • 从远程仓库抓取和拉取
    • git fetch <remote>,这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
      • git fetch 只会将数据下载到你的本地仓库,它并不会自动合并或修改你当前的工作。
      • 当准备好时你必须手动将其合并入你的工作。git fetch 更新你的远程跟踪分支
    • git fetch <remote> <remotebranch>,拉取指定分支。
    • git fetch <remote> <remotebranch>:<localbranch>,拉取到本地,指定分支。
    • git pull,自动抓取后合并该远程分支到当前分支。
      • git pull = git fetch + git merge
      • 默认情况下,git clone 命令会自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支(或其它名字的默认分支)。
      • 运行 git pull 通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。
  • 推送到远程仓库
    • git push <远程主机名> <本地分支名> <远程分支名>
    • git push origin master:refs/for/master
      • 将本地的 master 分支推送到远程主机 origin 上的对应 master 分支。
        • origin 是远程主机名
        • 第一个 master 是本地分支名
        • 第二个 master 是远程分支名
    • git push origin master
      • 如果远程分支被省略,如上则表示将本地分支推送到与之存在追踪关系的远程分支(通常两者同名)
      • 如果该远程分支不存在,则会被新建。
    • git push origin :refs/for/master
      • 如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支
      • 等同于 git push origin --delete master
    • git push origin
      • 如果当前分支与远程分支存在追踪关系,则本地分支和远程分支都可以省略
      • 将当前分支推送到 origin 主机的对应分支
    • git push
      • 如果当前分支只有一个远程分支,那么主机名都可以省略
      • 形如 git push,可以使用 git branch -r ,查看远程的分支名。

# git 标签

  • git tag,查看(列出)标签。
    • 参数 -l 或者 --list
    • 可以使用 git tag -l 'string' 筛选内容。
  • 打标签
    • 标签分为:
      • 附注标签
      • 轻量标签
    • 默认标签是打在最新提交的commit上的。
    • 轻量标签
      • git tag <tagname>
    • 附注标签
      • git tag -a <tagname> -m "description"
      • 带有说明的标签:
        • -a 指定标签名
        • -m 指定说明文字
        • 如果没有为附注标签指定一条信息,Git 会启动编辑器要求你输入信息。
  • git show <tagname>,查看具体标签信息,命令可以看到标签信息和与之对应的提交信息。
  • git tag <tagname> <commit id>
    • 给历史提交打标签。
  • git push <远程主机名> <tagname>
    • 推送某个标签
  • git push origin --tags
    • 推送所有标签到远程。
  • 删除标签
    • git tag -d <tagname>,这只会删除本地的标签。
    • 如果要删除远程的标签的话
      • git push origin --delete <tagname>
  • 检出标签
    • git checkout <tagname>

# 设置 git 别名

  • git config --global alias.co checkout
  • git config --global alias.br branch
  • git config --global alias.ci commit
  • git config --global alias.st status

# 分支

  • 查看分支列表
    • git branch 看每一个分支的最后一次提交
      • git branch -v 查看已经/没有合并到当前分支的分支列表
      • --merged
      • --no-merged
      • 可以指定当前分支的分支名
        • git branch --merged <currentbranch>
  • 创建分支
    • git branch branchname
  • 切换分支
    • git checkout branch
  • 重命名分支
    • git branch -m oldName newName
  • 创建同时切换到新分支
    • git checkout -b <localnewbranchname> <remotebranchname>
    • git checkout --track origin/dev 等同于 git checkout -b dev origin/dev
  • 删除分支
    • git branch -d branchname
    • -D 强制删除。
  • 删除远程分支
    • git push origin --delete branchname
    • 基本上这个命令做的只是从服务器上移除这个指针。Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的。
  • 合并时遇到冲突,解决冲突文件以后,一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决。
  • 查看远程分支
    • 注意与 git remote 查看远程仓库不同
    • git ls-remote <远程主机>,简单列表
    • git remote show <远程主机名>,更多信息。
  • 跟踪分支
    • 查看所有跟踪分支

      • git branch -vv
        • “ahead” 是 2,意味着本地有两个提交还没有推送到服务器上。
    • 修改正在跟踪的远程分支

      • git branch -u <远程分支> <本地分支>
      • -u 等同于 --set-upstream-to
    • 设置好跟踪分支后,可以通过简写 @{upstream}@{u} 来引用它的上游分支。

      比如:

      • 在 master 分支时并且它正在跟踪 origin/master 时,如果愿意的话可以使用 git merge @{u} 来取代 git merge origin/master
      • 需要重点注意的一点是这些数字的值来自于你从每个服务器上最后一次抓取的数据。 这个命令并没有连接服务器,它只会告诉你关于本地缓存的服务器数据。
      • 如果想要统计最新的领先与落后数字,需要在运行此命令前抓取所有的远程仓库。
        • git fetch --all; git branch -vv
  • 变基
    • 只对尚未推送或分享给别人的本地修改执行变基操作清理历史。
    • 不要对已推送至别处的提交执行变基操作。

# 协议

  • 本地协议(Local),通常使用普通路径,因为这样更快。
    • 指定路径
      • git clone /srv/git/project.git
      • Git 会尝试使用硬链接(hard link)或直接复制所需要的文件。
    • 指定 file://
      • git clone file:///srv/git/project.git
      • Git 会触发平时用于网路传输资料的进程,那样传输效率会更低。
      • 指定 file:// 的主要目的是取得一个没有外部参考(extraneous references) 或对象(object)的干净版本库副本——通常是在从其他版本控制系统导入后或一些类似情况需要这么做。。
  • HTTP 协议
    • 需要用户名密码。
  • SSH 协议
    • 需要配置 ssh key,配置以后就不需要再输入用户名密码了。
    • 不支持匿名访问。
  • Git 协议
    • Git 协议是 Git 使用的网络传输协议里最快的。

# SSH 公钥

  • 生成
    • ssh-keygen
    • 默认情况下,用户的 SSH 密钥存储在其 ~/.ssh 目录下。
    • 一对以 id_dsaid_rsa 命名的文件,其中一个带有 .pub 扩展名的,将公钥添加到 git 服务器以实现 SSH 方式登录。
  • 使用上
    • 要做到每次提交都有具体的主题,比如每次提交都解决了一个问题,不要将处理的几个问题在一次提交中,提交上去。

    • 提交信息

      • 少于 25 汉字,或者 50 字符的简要概括,然后空一行,再接着继续更详细的解释。
      1edee6b..fbff5bc  master -> master
      <oldref>..<newref> fromref -> toref
      
      1
      2
      • oldref 的含义是推送前所指向的引用
      • newref 的含义是推送后所指向的引用
      • fromref 是将要被推送的本地引用的名字
      • toref 是将要被更新的远程引用的名字

# .gitkeep 文件

  • git 默认是不允许提交一个空的目录到版本库上的, 可以在空的文件夹里面建立一个 .gitkeep 文件,然后提交去即可。
  • 其实在 git 中 .gitkeep 就是一个占位符。

# 配置

  • 配置文件优先级:.git/config > ~/.gitconfig > /etc/gitconfig
  • 检查配置信息
    • 如果想要检查你的配置,可以使用 git config --list 命令来列出所有 Git 当时能找到的配置。
  • 安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址。
    • 这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:
      • git config --global user.name "John Doe"
      • git config --global user.email johndoe@example.com
  • 再次强调,如果使用了 --global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 --global 选项的命令来配置。很多 GUI 工具都会在第一次运行时帮助你配置这些信息。
  • 常用配置项:
    • git config --global user.name "John Doe" # 换成自己的名字
    • git config --global user.email johndoe@example.com # 换成自己的邮箱
    • git config --global core.editor vim
    • git config --global core.quotepath false
    • git config --global push.default simple
    • git config --global rerere.enabled true
    • git config --global alias.c checkout
    • git config --global alias.d diff
    • git config --global alias.dc "diff --cached"
    • git config --global alias.dw "diff --word-diff"
    • git config --global alias.l "log --all --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
    • git config --global alias.s status -sb
    • git config --global alias.reset-permission '!git diff -p -R --no-color | grep -E "^(diff|(old|new) mode)" --color=never | git apply'

# 讨论区

由于评论过多会影响页面最下方的导航,故将评论区做默认折叠处理。

点击查看评论区内容,渴望您的宝贵建议~
Last Updated: 6/10/2022, 3:54:48 PM