likes
comments
collection
share

Git 基础

作者站长头像
站长
· 阅读数 4

Git相关文档

Two years of squash merge

Git特点

  • 直接记录快照,而非差异的比较(与其他版本控制系统的主要差异?)
  • 保证完整性。Git用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)(SHA-1原理?)。这是一个由 40 个十六进制字符(0-9 和 a-f)组成的字符串,基于 Git 中文件的内容或目录结构计算出来。例如
// git log 命令的字符串也类似
24b9da6552252987aa493b52f8696cd6d3b00373
  • git一般只往数据库中添加数据(本地数据库的实现方式?)
  • 所有操作都是本地执行的
  • 任何已提交的操作总是可以恢复的,未提交的操作是无法恢复的

三种状态

  • modified 修改
  • staged 暂存。对已修改文件的当前版本做了标记,使之包含在下次提交的快照中
  • commited 存储到本地的数据库中

git配置

查询git配置

  • /etc/gitconfig。包含系统上每一个用户及他们仓库的通用配置。--system
  • ~/.gitconfig ~/.config/.gitconfig 只针对当前用户。--global
  • 当前目录的config文件.--local
  • git config --list --show-origin 展示当前所有的配置和来源

检查配置信息

  • git config --list 检查所有信息
  • git config user.name 检查对应的信息

设置用户信息

  • git config --global user.name 'xxx'
  • git config --global user.email 'xxx@xxx.xxx'

获取帮助

  • git help config git conifg --help man-git-<verb>获取详细帮助
  • git config -h 获取简明帮助

git仓库

  • git add *.c
  • git add LICENSE
  • git commit -m 'initial project version'

克隆远程仓库到本地

  • git clone 三种协议 https:// git:// SSH

文件状态

  • 已跟踪
  • 未跟踪 git add -> 未跟踪 -> 已跟踪 ???
  • 暂存 git add 已跟踪并修改的文件 -> 暂存 ???
  • git status 可以查询文件状态以及当前所在分支
  • git status -s 输出更为紧凑的文件状态
    • ?? 新添加的未跟踪文件
    • M 已修改的文件
    • M 已修改已暂存
    • MM 有已修改已暂存的内容,也有已修改的内容
    • A 新添加到暂存区的文件
  • git add
    • 可以用它开始跟踪新文件
    • 把已跟踪的文件放到暂存区
    • 合并时把有冲突的文件标记为已解决状态
  • .gitignore 忽略文件,一个项目可以有多个ignore文件存在
  • git diff 比较尚未暂存的文件的改动
  • git diff --staged(--cached) 比较已暂存文件的改动
  • git commit 提交
    • git commit -m 'xxx' 提交并输入提交信息
    • 提交时记录的是放在暂存区域的快照,以后可以回到这个状态 git log
    • git commit -a 把已跟踪的文件暂存起来并一并提交
    • git commit -a -m 'xxx' git commit -am ''
  • git rm
    • 手动删除文件时,从暂存区移除某个文件,这样就不用git add -> git commit ,可以直接git commit
    • git rm -f 删除之前修改过或已经放到暂存区
    • git rm --cache 把文件从git仓库中移除,并保留在当前工作区中
  • git mv 移动文件,对文件改名git mv fileA fileB
    • mv fileA fileB
    • git rm fileA
    • git add fileB

git查询历史

  • git log
  • git log -p 显示每次提交所引入的差异
  • git log -2 显示最近两条记录
  • git log --stat 看到每次提交的简略统计信息,如变更行数,变更文件等
  • git log --pretty=oneline 格式化log日志输出为一行
  • git log --pretty=format:'%h %s' --author='' --since='' --before='' --no-merge
  • git log --no-merges <branch>..<branch> 要求 Git 只显示所有在后面分支但不在前面分支的提交的列表。 -git log branch --not master 查询没有在master上的提交
  • git shortlog --no-merges master --not v1.0.1
  • git log --abbrev-commit 为 SHA-1 值生成出简短且唯一的缩写
  • git log -1 --name-only --pretty=format:'' <SHA-1>找出一次提交中修改文件的文件名

撤销提交

  • git commit --amend 提交时忘记add某个文件,可通过git add 添加后再执行该命令,这样只会存在一个提交
  • git reset HEAD filename 取消暂存一个文件。本质上先移动到HEAD,然后让索引看起来像HEAD。所以它本质上只是将 file.txt 从 HEAD 复制到索引中。
    • HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。
    • git reset --soft HEAD 将HEAD移动到指定快照,指定快照之后的索引和工作目录都不改变,此时可以通过git commit 来完成 git commit --amend 所做的事。主要用于压缩提交,即合并多个本地commit为一个commit
    • git reset --mixed HEAD (get reset HEAD)将HEAD移动到指定快照, 但是会取消暂存所有指定快照之前的提交内容
    • git reset --hard HEAD 将HEAD移动到指定快照, 并清空所有指定快照之前的提交内容
    • git reset HEAD filename 将指定文件还原为HEAD版本
    • resetcheckout的区别
      • git checkout [branch]相对于git reset --hard [branch]是安全的。在切换分支的过程中,checkout在没有分支冲突的情况下,会把工作区及暂存区的代码同步到另一条分支上
      • reset [branch] 会移动当前HEAD的指向,即改变当前分支的提交分支,而chekcout移动的是HEAD自身来指向另一个分支
      • git checkout 文件路径 类似于git reset --hard [branch] file,直接检出对应的文件覆盖工作区的文件
  • git checkout -- filename 还原一个文件,直接还原为数据库版本

恢复

  • git reflog 查询已经丢失的提交。每当head变化,Git 就会将这个信息存储到引用日志这个历史记录里。引用日志只存在于本地仓库。
  • git update-ref 更新引用日志(reflog)
  • git log -g 以标准格式输出引用日志
  • git branch branchname sha-1 新建名为branchname的分支指向引用为sha-1的提交
  • 引用日志存放在 ./git/logs/ 中
  • git fsck --full 用于恢复没有引用日志的情况
  • git clone 会下载整个项目的历史,包括每一个文件的每一个版本

远程仓库信息

  • git remote -v 查看远程仓库地址及读写信息
  • git remote add shortname url 添加新的远程仓库,并给一个shortname别名
  • git fetch 拉取远程仓库中你没有的信息。只会拉取远程数据到本地,并不会主动合并或修改当前的工作
  • git pull 抓取并合并远程分支到当前分支
  • git clone 会自动添加远程仓库并默认以origin为简写
  • git fetch origin 会自动拉取上一次抓取或clone后新推送的所有工作
  • git push remote branch 推送到远程remote仓库的branch分支
  • git remote show remote 查看remote远程仓库的信息
  • git remote rename prename curname 重命名某个远程仓库
  • git remote remove/rm 移除某个远程仓库

打标签

  • git tag
  • 轻量标签 git tag v1.2
  • 附注标签 git tag -a v1.2 -m 'xx' 会保留打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息,并且可以使用 GNU Privacy Guard (GPG)签名并验证
  • 推送到远程分支:git push tag v1.4 推送指定分支 git push origin --tags 推送远程仓库没有的本地所有分支
  • 删除tag git tag -d tagname 本地删除 推送到远程 git push origin --delete tagname
  • 检出并创建分支 git checkout -b branchname tagname 切换到对应的tag git checkout tag

别名

  • 内部命令设置别名:git config --global alias.customCommand 'gitCommand'
  • 外部命令设置别名:git config --global alias.customCommand '!outsideCommand'

分支

  • 创建分支:git branch branchName
  • 切换分支:git checkout branchName
  • 创建新分支并切换:git checkout -b branchName。为以上两条命令的简写。
  • 创建分支并关联到远程分支:git checkout -b <branch> <origin/branch>
  • 以远程分支名命名本地分支:git checkout -b --track <origin>/<branch> 。简写:git checkout <branch>
  • 更改本地分支绑定的远程分支:git branch -u <origin>/<branch> git branch --set-upstream-to <origin>/<branch>
  • 删除分支: git branch -d branchName 强制删除 git branch -D branchName
  • 删除远程分支:git push origin --delete <branch>。只删除指针,git服务器通常会保留一段时间知道gc执行,所以数据很容易恢复
  • 分支合并:git merge <branch> 切换到对应分支并执行该命令。三方合并?合并结果生成一个新的提交快照
  • 该命令仅会显示自当前主题分支与 master 分支的共同祖先起,该分支中的工作:git diff master...branch
  • 变基:git rebase 与merge相比,能够使分支更加整洁。先找到两个分支的共同祖先,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件, 然后将当前分支指向目标基底,最后以此将之前另存为临时文件的修改依序应用。变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。
  • git rebase --onto master a b b分支基于a分支创建,a分支基于master创建。此命令用于合并b分支上,不存在a分支的提交,并合并到master
  • git rebase master a 直接在master分支上rebase a分支
  • git rebase -i将工作压缩成一个单独的提交
  • 变基准则:如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。
  • git pull --rebase 等同于? git fetch``git rebase <origin>/<master> 。使用pull 默认使用选项--rebase,git config --global pull.rebase true
  • 总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史, 从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。
  • 查看当前本地分支:git branch
  • 过滤已经合并/未合并到当前分支:git branch --merge/--no-merge
  • 本地分支命名<branch>和远程分支命名 <remote>/<branch>
  • 查看远程分支:git ls-remote 获得远程分支更多的信息git remote show <remote>
  • 同步给定仓库的远程分支数据:git fetch <origin> 抓取所有仓库的数据:git fetch --al
  • 推送:git push <remote> <branch>
  • 避免https仓库每次提交输入密码:git config --global credential.helper cache
  • 拉取:git pull 相当于 git fetch git merge
  • git show <branch> | <SHA-1>

git分支切换的原理

  • 只记录提交的快照,变动文件的blob的对象(每个文件对应的快照),一个树对象(记录目录结构和blob对象索引),一个提交对象(包含着指向前述树对象的指针和所有提交信息)
  • 具体原理,其他版本管理系统一般是对文件进行复制操作,git的分支实质上仅是包含所指对象校验和的对象
  • 拣选对应的提交:git cherry-pick ecf34
  • rerere:首先配置 git config --global rerere.enabled true

其他

提交贡献

  • git clone 对应的仓库

  • git checkout <branch> 切换分支进行开发

  • git remote add origin <url>

  • git push -u origin <branch> 将commit推送到对应仓库的分支。不需要合并到master分支,这样做的好处是当你的建议未被采纳时,你不用回滚master上的代码。

  • 告知维护者自己的提交信息:git pull-request <origin>/<branch> <url> 获取自己的提交信息并发送给维护者

  • 仓库维护者无法干净的合并时,可以通过变基操作,重新提交:git checkout <branch> git rebase origin/master git push -f origin branch

  • 另一种方式 git checkout -b <branch> <origin>/<branch> 生成一个新分支;在新分支上合并git merge --squash <branch> git commit git push origin <branch>

  • 邮件方式。假如现在有多个commit,可以通过git format-patch -M origin/master将每一个提交转换为一封电子邮件

  • 邮件发送。需要在 ~/.gitconfig 文件中设置 imap 区块。然后通过git config --global imap.xxx xxx 设置

    [imap]
      folder = "[Gmail]/Drafts"
      host = imaps://imap.gmail.com
      user = user@gmail.com
      pass = YX]8g76G_2^sFbd
      port = 993
      sslverify = false
    
  • 执行cat *.patch |git imap-send将补丁序列放在特定 IMAP 服务器的 Drafts 文件夹中

  • 也可以通过smtp服务发送邮件,进行如下配置

    [sendemail]
      smtpencryption = tls
      smtpserver = smtp.gmail.com
      smtpuser = user@gmail.com
      smtpserverport = 587
    
  • 之后就可以执行 git send-email *.patch进行补丁发送

应用补丁

  • git apply 应用前检查补丁:git apply --check *.patch
  • git am a apply m mail 包含冲突时,解决冲突,执行git add,之后执行 git am --reslove
  • git am -3 *.patch 以一种智能方式提交。使 Git 尝试进行三方合并。创建补丁的提交并不在你的版本库内的话,这样做是没有用处的。

好用的命令

查看

  • 当前提交:git show 查看当前head的commit记录
  • 上一次提交:git show HEAD^ 查看上一次commit记录 等价于 git show HEAD~
  • 前两次提交:git show HEAD~2 git show HEAD~~ ``
  • 提交区间: 存在于dev分支上不存在于master上。git log master..dev
  • 查看即将推送到远程分支的内容: git log origin/master..HEAD 等同于 git log origin/master..
  • 双点语法无法显示多分支的差异,可以使用:git log refa refb --not refc 或者 git log refa refb ^refc
  • 三点:git log master...dev 如果你想看 master 或者 dev 中包含的但不是两者共有的提交。可以使用git log --left-right master...dev 可以方便的查看那个提交属于哪个分支

交互式缓存

  • 能够帮助你对commit进行拆分合并
  • git add -i 类似于git status 但展示更简明扼要一些
  • 能够暂存部分提交文件及文件中的某一部分git add -p git add --patch也可以实现该功能。git reset --patch 部分重置文件 git checkout --patch部分检出文件 git stash save --patch 部分暂存文件

贮藏与清理

  • 保存当前工作目录并切换到其他分支 git stashgit stash push
  • 查询储藏记录:git stash list
  • 应用储藏记录:git stash apply 应用指定的储藏 git stash apply stash@{2}
  • 移除暂存: git stash drop stash@{0}
  • 更新暂存: git stash apply --index
  • 暂存并保留在索引中:git stash --keep-index
  • 默认情况下,git stash 只会暂存已跟踪文件。若要暂存未跟踪文件,git stash -u。但并不会包含忽略文件。git stash -a,包含忽略文件。
  • 从储藏创建一个分支:git stash branch <new branchname>
  • 清空目录:git clean git clean -f -d 移除工作目录中为追踪的文件及空的子目录。这个命令是不安全的,更安全的命令是git stash --all
  • 清空目录预演:git clean -d -n
  • 清空忽略文件:git clean -n -d -x
  • 交互式清空: git clean -x -i

签署工作

搜索

  • git grep -n gmtime_r -n 输出 Git 找到的匹配行的行号
  • 日志搜索:git log -S ZLIB_BUF_MAX --oneline -S 查询ZLIB_BUF_MAX是什么时候引入的

重写历史

  • git commit --amend 会改变上次提交的SHA-1 校验和,可用于修改信息及修改提交内容。如果修改了提交内容并需要反映到提交信息上,可以用这个命令。但如果只是遗漏了文件,可以使用git commit --amend --no-edit
  • git rebase -i 交互式修改历史提交。注意点,commit 校验和和log显示的是相反的。修改pick改为edit。删除,直接删除对应的记录。合并,pick改为squash。拆分,修改pick为edit,执行git reset HEAD^,然后对工作区文件多次commit后执行,git rebase --continue。会改动所有在列表中的提交的 SHA-1 校验和。
  • 核武器级选项:git-filter-repo
  • 如果想要移除所有偶然提交的编辑器备份文件,执行:git filter-branch --tree-filter 'rm -f *~' HEAD
  • 全局修改邮箱地址:
    git filter-branch --commit-filter '
            if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
            then
                    GIT_AUTHOR_NAME="Scott Chacon";
                    GIT_AUTHOR_EMAIL="schacon@example.com";
                    git commit-tree "$@";
            else
                    git commit-tree "$@";
            fi' HEAD
    

高级合并

  • 不想继续合并:git merge --abort。在工作目录中有未储藏、未提交的修改时它不能完美处理,除此之外它都工作地很好。
  • 出于某些原因想要重来一次,执行:git reset --hard HEAD。回到上一次提交的状态。同样会丢失为提交的文件
  • 忽略空白行修改;git merge -Xignore-space-change whitespace-Xignore-space-change将一个空白符与多个连续的空白字符视作等价的。-Xignore-all-space完全忽略空白符修改
  • 传递给--confilict diff3 merge 选项 可以展示包含ours,thiers,base三个版本的代码。git checkout --conflict=diff3 hello.rb。可以通过设置,git config --global merge.conflictstyle diff3,来让diff3作为合并冲突的默认选项
  • 获取合并日志:git log --oneline --left-right HEAD...MERGE_HEAD
  • 撤销合并:撤销本地合并git reset --hard HEAD-。如果其他人已经有你要重写的提交,这种方法是无效的。
  • 另一种方式:git revert -m 1 HEAD 。revert后,如果想继续合并,需要再revert一次git revert HEAD
  • 单方合并,只是用我们的代码或只使用他们的代码:git merge Xours <branch> git merge Xtheirs <branch>
  • 把一个项目作为另一个项目的子目录:git read-tree --prefix=rack/ -u rack_branch。更新代码,先切回队形仓库的分支执行git pull命令。之后切回原分支,执行git merge --squash -s recursive -Xsubtree=rack rack_branch。与原分支做比较,git diff-tree -p <branch>

Rerere

  • 它允许你让 Git 记住解决一个块冲突的方法, 这样在下一次看到相同冲突时,Git 可以为你自动地解决它。reuse recorded resolution
  • 开启功能:git config --global rerere.enabled true
  • 如果文件冲突之前解决过。在回滚操作之后,如果开启了rerere,则git会帮我们用上次解决方式解决冲突。git diff 查看冲突是如何解决的。恢复文件为冲突状态:git checkout --conflict=merge hello.rb。执行git rerere
  • 如果需要经常变基,打开 rerere 功能可以帮助你的生活变得更美好 ###使用 Git 调试
  • 查询代码每一行的提交记录git blame
  • 使用二分法定位出错的commit:git bisect start git bitect bad git bitect good <good_commit> 在检出的commit上测试,然后执行git bisect goodgit bisect bad 定位到错误后,执行git bisect reset

子模块

打包

替换

凭证存储

  • http访问不需要输入账号密码
  • git config --global credential.helper cache cache会将凭证存放在内存中一段时间
  • git config --global credential.helper 'store --file ~/.my-credentials' store 永久明文保存

自定义 Git

配置git

  • 获取支持的配置列表:man git config
  • commit.templete git config --global commit.template ~/.gitmessage.txt 设置默认的提交模版
  • core.excludesfile 配置全局生效的.gitignore文件
  • core.autocrlf 解决多系统协作问题。true ,换行被转换成回车+换行。input ,回车+换行转换成换行。false,window开发者保留回车+换行
  • core.whitespace 。Git 预先设置了一些选项来探测和修正多余空白字符问题。
  • 服务器端配置。receive.fsckObjects 娇艳推送过来文件的SHA-1。receive.denyNonFastForwards变基已推送的提交,然后推送或推送一个提交,当前远程分支当前所指向的提交不再该推送的提交中。receive.denyDeletes,禁止通过推送删除分支和标签。

Git 钩子

提交工作流钩子:

  • pre-commit。键入提交信息前运行。用于做提交前检查,通过git commit --no-verify可以跳过。
  • prepare-commit-msg 钩子在启动提交信息编辑器之前,默认信息被创建之后运行。对那些会自动产生默认信息的提交,如提交信息模板、合并提交、压缩提交和修订提交等非常实用。 你可以结合提交模板来使用它,动态地插入信息。
  • commit-msg钩子接收一个参数,此参数即上文提到的,存有当前提交信息的临时文件的路径。 。可以用来在提交通过前验证项目状态或提交信息。 -post-commit。钩子在整个提交过程完成后运行。该钩子一般用于通知之类的事情。

Git内部原理

新初始化的 .git 目录的典型结构如下:

$ ls -F1
config        包含项目特有的配置选项
description   仅供 GitWeb 程序使用,我们无需关心
HEAD          指向目前被检出的分支
(尚待创建的)index 文件    保存暂存区信息
hooks/        钩子
info/         包含一个全局性排除(global exclude)文件, 用以放置那些不希望被记录在.gitignore 文件中的忽略模式(ignored patterns
objects/      存储所有数据内容
refs/         存储指向数据(分支、远程仓库和标签等)的提交对象的指针

Git 对象

  • 一个文件对应一个内容,以该内容加上特定头部信息一起的 SHA-1 校验和为文件命名。校验和的前两个字符用于命名子目录,余下的 38 个字符则用作文件名。
  • 头部信息构成:对象的类型 + 空格 + 数据内容的字节数 + 空字节
  • 创建一个文件git对象 git hash-object -w <filePath>
  • 通过对象获取对应的文件:git cat-file -p <SHA-1>
  • 获取文件存储的数据类型:git cat-file -t <SHA-1>
  • 树对象,一个树对象可以包含多个对象或其他树对象。git使用树对象来解决保存文件名,也允许我们将多个文件组织到一起。git cat-file -p master^{tree} 获取某项目当前的树对象
  • 创建暂存区:git update-index。文件模式,100644 普通文件 ,100755 可执行文件,12000表示一个符号链接。可以通过 git write-tree 命令将暂存区内容写入一个树对象。
  • 创建提交对象:可以通过调用 commit-tree 命令创建一个提交对象,为此需要指定一个树对象的 SHA-1 值,以及该提交的父提交对象(如果有的话)。
  • 标签对象,类似于一个提交对象。但是提交对象一般指向树对象,标签对象指向一个提交对象。它包含一个标签创建者信息、一个日期、一段注释信息,以及一个指针。 它永远指向一个提交对象,只不过给这个提交对象加上一个更友好的名字罢了。轻量标签指向固定引用,附注标签会生一个标签对象,并用一个引用只想当前这个标签对象。

Git 引用

  • 对仓库的一个提交感兴趣,我们需要知道对应提交的SHA-1校验和。但也可以通过refs来进行快捷访问。
  • git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
  • 当运行类似于 git branch 这样的命令时,Git 实际上会运行 update-ref 命令, 取得当前所在分支最新提交对应的 SHA-1 值,并将其加入你想要创建的任何新引用中。
  • HEAD引用。HEAD 文件通常是一个符号引用(symbolic reference),指向目前所在的分支。 所谓符号引用,表示它是一个指向其他引用的指针。
  • 查看HEAD引用值:git symbolic-ref HEAD;设置git symbolic-ref HEAD refs/heads/test
  • 标签引用。不光可以作为标签对象的引用,也可以作为其他对象的引用。
  • 远程引用。指向一个包含远程仓库地址和名字对象的引用,即remotes

包文件

  • Git 最初向磁盘中存储对象时所使用的格式被称为“松散(loose)”对象格式。但是,Git 会时不时地将多个这些对象打包成一个称为“包文件(packfile)”的二进制文件,以节省空间和提高效率。可以手动执行git gc命令后再查看.git/obejcts文件
  • 打包后。索引文件包含了包文件的偏移信息,我们通过索引文件就可以快速定位任意一个指定对象。
  • 查看打包对象的内容:git verify-pack .git/objects/pack/pack-978e03...
  • Git 时常会自动对仓库进行重新打包以节省空间。当然你也可以随时手动执行 git gc 命令来这么做。

引用规范

  • 引用规范的格式由一个可选的 + 号和紧随其后的 : 组成, 其中 是一个模式(pattern),代表远程版本库中的引用; 是本地跟踪的远程引用的位置。 + 号告诉 Git 即使在不能快进的情况下也要(强制)更新引用。
  • 定义push 或 fetch 推送/拉取规则

传输协议

维护与数据恢复

环境变量