Git 基础
Git相关文档
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为一个commitgit reset --mixed HEAD
(get reset HEAD
)将HEAD移动到指定快照, 但是会取消暂存所有指定快照之前的提交内容git reset --hard HEAD
将HEAD移动到指定快照, 并清空所有指定快照之前的提交内容git reset HEAD filename
将指定文件还原为HEAD版本reset
和checkout
的区别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
切换到对应的taggit 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分支的提交,并合并到mastergit 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 --reslovegit 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 stash
或git 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 good
或git 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 推送/拉取规则
传输协议
维护与数据恢复
环境变量
转载自:https://juejin.cn/post/6956128847230140429