概述
git 使用
初次运行 Git 前的配置
查看、修改 gitconfig
git config --list --show-origin
git config --global user.name "John Doe"
git config --global user.email johndoe@example.com
记录每次更新到仓库
查看当前文件状态
git status
git status -s
# 状态简览
跟踪新文件或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等
git add README
查看未暂存或未提交差异
git diff
# 未暂存差异
git diff --cached (git diff --staged)
# 未提交差异
提交
git commit -m "add README.m"
移除文件
git rm README.m
从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中,比如忘记添加 .gitignore
文件
git rm --cached README
git rm --cached log/*.log
# 支持正则, 注意* 之前的反斜杠 ,因为 Git 有它自己的文件模式扩展匹配方式
移动文件
git mv file_from file_to
## 相当于
mv file_from file_to
git rm fire_from
git add file_to
查看提交历史
最近两次提交差异
git log -p -2
每次提交的简略的统计信息
git log --stat
不同于默认格式的方式展示提交历史,查看方式oneline
, short
,full
和 fuller
可以用
git log --pretty=oneline
定制显示记录格式 常用选项,(作者指的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人)
git log --pretty=format:"%h - %an, %ar : %s"
当 oneline
或 format
与另一个 log
选项 --graph
结合使用时尤其有用,形象地展示你的分支、合并历史
git log --pretty=format:"%h %s" --graph
git log
的 常用选项
限制输出长度,限制输出的选项
git log --since=2.weeks
# 最近两周的所有提交
git log -S function_name
# 找出添加或删除了对某一个特定函数的引用的提交
git log 1.txt 2.txt
# 某些文件或者目录的历史提交
git log -S function_name -- 1.txt
# 两个短划线隔开之前的选项和后面限定的路径名
git log --pretty="%h - %s" --author='Junio C Hamano' --since="2008-10-01"
--before="2008-11-01" --no-merges -- t/
# 查看 Junio Hamano 在 2008 年 10 月其间, 除了合并提交之外的哪一个提交修改了测试文件
撤消操作
重新提交
git commit --amend
取消暂存的文件
git restore --staged README.m
git reset HEAD README.m (老版本)
撤消对文件的修改,危险命令,你对那个文件在本地的任何修改都会消失——Git 会用最近提交的版本覆盖掉它
git restore README.m
git checkout -- README.m (老版本)
远程仓库的使用
查看远程仓库
git remote
git remote -v
# 显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL
添加远程仓库,
git remote add <shortname> <url>
git remote add origin https://github.com/paulboone/ticgit
从远程仓库中抓取与拉取
git fetch <remote>
#必须注意 git fetch 命令只会将数据下载到你的本地仓库——它并不会自动合并或修改你当前的工作
git pull <remote>
#如果你的当前分支设置了跟踪远程分支,自动抓取后合并该远程分支到当前分支
推送到远程仓库
git push <remote> <branch>
git push origin master
# 克隆时通常会自动帮你设置好那两个名字
查看某个远程仓库
git remote show <remote>
远程仓库的重命名与移除
git remote rename pb paul
# 重命名
git remote remove paul
# 移除
打标签
列出标签
git tag
git tag -l "v1.8.5*"
# 标签过滤
创建标签
轻量标签很像一个不会改变的分支——它只是某个特定提交的引用
而附注标签是存储在 Git 数据库中的一个完整对象, 它们是可以被校验的,其中包含打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息,并且可以使用 GNU Privacy Guard (GPG)签名并验证。
git tag -a v1.4 -m "my version 1.4"
# 附注标签
git show v1.4
# 查看标签信息和与之对应的提交信息
git tag v1.4-lw
# 轻量标签
git tag -a v1.2 9fceb02
# 在某个提交上打标签
共享标签
git push origin v1.5
git push origin --tags
# 一次性推送很多标签
删除标签
git tag -d v1.4-lw
# 不会从任何远程仓库中移除这个标签
git push <remote> :refs/tags/<tagname>
# 来更新你的远程仓库
git push origin :refs/tags/v1.4-lw
# 方式一
git push origin --delete <tagname>
# 方式二
检出标签,查看某个标签所指向的文件版本
git checkout 2.0.0
# 会使你的仓库处于“分离头指针(detached HEAD)”的状态
git checkout -b version2 v2.0.0
# 如果你需要进行更改,比如你要修复旧版本中的错误,那么通常需要创建一个新分支
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 config --global alias.unstage 'reset HEAD --'
# 取消暂存文件
git config --global alias.last 'log -1 HEAD'
# 最后一次提交
git config --global alias.visual '!gitk'
# 执行外部命令,而不是一个Git子命令,命令前面加入!符号
分支新建与合并
分支创建
git branch testing
git log --oneline --decorate
# 查看各个分支当前所指的对象
分支切换
git checkout testing
git chekkout -b testing
# 创建一个新分支后立即切换过去,相当于 git branch,git checkout
查看分叉历史
git log --oneline --decorate --graph --all
删除分支
git branch -d hotfix
合并分支
git checkout master
# 检出到你想合并入的分支
git merge iss53
# 合并 iss53 分支到 master 分支
遇到冲突时的分支合并
<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html
这表示 HEAD
所指示的版本(也就是你的 master
分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(=======
的上半部分),而 iss53
分支所指示的版本在 =======
的下半部分。
git mergetool
# 启动一个合适的可视化合并工具
分支管理
查看分支
git branch
# 分支的一个列表
git branch -v
# 查看每一个分支的最后一次提交
git branch --merged
# 哪些分支已经合并到当前分支, 这个列表中分支名字前没有*号的分支通常可以使用 git branch -d 删除掉
git branch --no-merged # 尚未合并到当前分支
git branch --no-merged master
# 不必检出到 master, 查看未合并到 master 分支的有哪些
分支开发工作流
远程分支
查看远程分支
git ls-remote <remote>
# 获得远程引用的完整列表
git remote show <remote>
# 获得远程分支的更多信息
远程跟踪分支是远程分支状态的引用, 请将它们看做书签,它们以 <remote>/<branch>
的形式命名,例如 origin/master
分支
同步数据
git fetch <remote>
# 从中抓取本地没有的数据,并且更新本地数据库,移动 origin/master 指针到更新之后的位置
git fetch origin
推送
git push <remote> <branch>
git push origin serverfix
# Git 自动将 serverfix 分支名字展开为 refs/heads/serverfix:refs/heads/serverfix
git push origin serverfix:serverfix
# 也可以运行, 推送本地的 serverfix 分支,将其作为远程仓库的 serverfix 分支
git push origin serverfix:awesomebranch # 本地的 serverfix 分支推送到远程仓库上的 awesomebranch 分支
可以运行 git merge origin/serverfix
将这些工作合并到当前所在的分支。 如果想要在自己的 serverfix
分支上工作,可以将其建立在远程跟踪分支之上
git checkout -b serverfix origin/serverfix
跟踪分支
git checkout -b <branch> <remote>/<branch>
git checkout --track origin/serverfix
# Git 提供了 --track 快捷方式
git checkout serverfix
# 如果你尝试检出的分支 (a) 不存在且 (b) 刚好只有一个名字与之匹配的远程分支,那么 Git 就会为你创建一个跟踪分支
git checkout -b sf origin/serverfix
# 如果想要将本地分支与远程分支设置为不同的名字
git branch -u origin/serverfix
# 设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支, 你可以在任意时间使用 -u 或 --set-upstream-to 选项运行 git branch 来显式地设置
# git merge @{u} 来取代 git merge origin/master
查看设置的所有跟踪分支
git branch -vv
git fetch --all; git branch -vv
# 统计最新的领先与落后数字,需要在运行此命令前抓取所有的远程仓库
拉取
git fetch # 从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容
git pull
#
在大多数情况下它的含义是一个 git fetch 紧接着一个 git merge 命令
删除远程分支
git push origin --delete serverfix
# 只是从服务器上移除这个指针, Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的。
变基
提取在 C4
中引入的补丁和修改,然后在 C3
的基础上应用一次,变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。
git checkout experiment
# 检出 experiment 分支, 将它变基到 master 分支上
git rebase master
git checkout master
# 回到 master 分支,进行一次快进合并
git merge experiment
在对两个分支进行变基时,所生成的“重放”并不一定要在目标分支上应用,你也可以指定另外的一个分支进行应用
将 client 中的修改合并到主分支并发布,但暂时并不想合并 server 中的修改
git rebase --onto master server client
#
取出 client 分支,找出它从 server 分支分歧之后的补丁, 然后把这些补丁在 master 分支上重放一遍,让 client 看起来像直接基于 master 修改一样
直接将主题分支 (即本例中的 server
)变基到目标分支(即 master
)上。 这样做能省去你先切换到 server
分支,再对其执行变基命令的多个步骤
git rebase <basebranch> <topicbranch>
git rebase master server
变基的风险
如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。
变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。
解决方法用变基解决变基
另一种简单的方法是使用 git pull --rebase
命令而不是直接 git pull
。 又或者你可以自己手动完成这个过程,先 git fetch
,再 git rebase teamone/master
如果你习惯使用 git pull
,同时又希望默认使用选项 --rebase
,你可以执行这条语句 git config --global pull.rebase true
来更改 pull.rebase
的默认配置
只对尚未推送或分享给别人的本地修改执行变基操作清理历史, 从不对已推送至别处的提交执行变基操作。
向一个项目贡献
git diff --check
# 找到可能的空白错误并将它们为你列出来
git add --patch
# 部分暂存文件
git format-patch -M origin/master
# 创建补丁, -M 开关告诉 Git 查找重命名
维护项目
git apply --check 0001-seeing-if-this-helps-the-gem.patch
# 检查补丁是否可以顺利应用
git apply /tmp/patch-ruby-client.patch
# 应用一个使用 git diff 生成补丁
## 建议使用 git format-patch 打补丁
git am 0001-limit-log-function.patch
# 应用一个由 format-patch 命令生成的补丁
git am --resolved
# 解决冲突后继续应用下一个补丁
git am -3 0001-seeing-if-this-helps-the-gem.patch
#
-3 选项来使 Git 尝试进行三方合并
git am -3 -i mbox
# 交互模式下运行 am 命令
git log contrib --not master
# 查看每次提交所引入的具体修改
git diff master...contrib
# 该分支与 master 分支的共同祖先进行比较
git cherry-pick e43a6
# 将提交 e43a6 拉取到 master 分支
准备一次发布
git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
# 最新的快照归档
git archive master --prefix='project/' --format=zip > `git describe master`.zip # 创建一个 zip 压缩包
提交简报
git shortlog --no-merges master --not v1.0.1
#自 v1.0.1 以来的所有提交
选择修订版本
简短的 SHA-1
git log --abbrev-commit --pretty=oneline
分支引用
git show topic1
# 假设 topic1 分支指向提交 ca82a6d...
git rev-parse topic1
# 某个分支指向哪个特定的 SHA-1
引用日志, 引用日志只存在于本地仓库
git reflog
git show HEAD@{5}
# HEAD 在五次前的所指向的提交
git show master@{yesterday}
# master 分支在昨天的时候指向了哪个提交
git log -g master
# git log 输出格式的引用日志信息
祖先引用
git show HEAD^
# 该引用的上一个提交
git show HEAD^^
# HEAD的父提交的父提交
git show HEAD^2
# HEAD的第二父提交, 这个语法只适用于合并的提交,因为合并提交会有多个父提交
# 另一种指明祖先提交的方法是 ~
git show HEAD~2
# 代表“第一父提交的第一父提交”
# 组合使用
git show HEAD~3^2
# 来取得之前引用的第二父提交(假设它是一个合并提交)
提交区间
## 双点
git log master..experiment
# 在 experiment 分支中而不在 master 分支中的提交
git log origin/master..HEAD
# 查看你即将推送到远端的内容
# 下列三个命令是等价
git log refA..refB
git log ^refA refB
git log refB --not refA
## 多点
git log refA refB ^refC
# 被 refA 或 refB 包含的但是不被 refC 包含的提交
git log refA refB --not refC
## 三点
git log master...experiment
# master 或者 experiment 中包含的但不是两者共有的提交
git log --left-right master...experiment
# 它会显示每个提交到底处于哪一侧的分支
交互式暂存
git add -i
# --interactive
暂存补丁
git add -p
# --patch, 通常情况下可以输入 y 或 n 来选择是否要暂存每一个区
更进一步地,可以使用 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 apply --index
# 尝试重新应用暂存的修改
git stash drop
# 移除的贮藏
git stash pop
# 应用贮藏然后立即从栈上扔掉它
贮藏的创意性使用
git stash --keep-index
# 不贮藏已暂存的内容
git stash -u
# --include-untracked, 藏任何未跟踪文件
git stash --patch
# 交互式地提示哪些改动想要贮藏
从贮藏创建一个分支
git stash branch testchanges
清理工作目录
git stash --all
# 更安全的移除每一样东西并存放在栈中
git clean
# 去除冗余文件或者清理工作目录
git clean -f -d
# 移除工作目录中所有未追踪的文件以及空的子目录
git clean -d -n
# 做一次演习然后告诉你 将要 移除什么
搜索
Git Grep 不仅仅可以可以搜索工作目录,还可以搜索任意的 Git 树
git grep -n gmtime_r
# -n 或 --line-number 选项数来输出 Git 找到的匹配行的行号
git grep --count gmtime_r
# -c 或 --count 仅包括那些包含匹配字符串的文件,以及每个文件中包含了多少个匹配
git grep -p gmtime_r *.c
#
-p 或 --show-function 选项来显示每一个匹配的字符串所在的方法或函数
git grep --break --heading
-n -e '#define' --and ( -e LINK -e BUF_MAX ) v1.8.0
# 查看在旧版本 1.8.0 的 Git 代码库中定义了常量名包含 “LINK” 或者 “BUF_MAX” 这两个字符串的行 (这里也用到了 --break 和 --heading 选项来使输出更加容易阅读)
Git 日志搜索
git log -S ZLIB_BUF_MAX --oneline
# 找到 ZLIB_BUF_MAX 常量是什么时候引入的
# 你可以使用 -G 选项来使用正则表达式搜索
行日志搜索
git log -L :git_deflate_bound:zlib.c
# 展示代码中一行或者一个函数的历史
git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c # 提供一个正则表达式
重写历史
修改最后一次提交,修正会改变提交的 SHA-1 校验和。 它类似于一个小的变基
git commit --amend
git commit --amend --no-edit
# 避免不必要的编辑器环节
修改多个提交信息
git rebase -i HEAD~3
# 从上到下的依次重演每一个提交引入的修改,每一个修改了提交信息的提交及其 所有后裔都会被重写
重新排序提交,压缩提交,拆分提交
git rebase -i HEAD~3
# 后修改顺序
# squash 压缩提交
# edit 然后 git reset HEAD^, 几个提交后,运行 git rebase --continue
核武器级选项:filter-branch
想要通过脚本的方式改写大量提交的话可以使用它——例如,全局修改你的邮箱地址或从每一个提交中移除一个文件,git filter-branch
有很多陷阱,不再推荐使用它来重写历史。 请考虑使用 git-filter-repo
,它是一个 Python 脚本,相比大多数使用 filter-branch
的应用来说,它做得要更好。它的文档和源码可访问 https://github.com/newren/git-filter-repo 获取。
git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
# 从整个提交历史中移除一个叫做 passwords.txt 的文件, 为了让 filter-branch 在所有分支上运行,可以给命令传递 --all 选项
使一个子目录做为新的根目录
git filter-branch --subdirectory-filter trunk HEAD
# 让 trunk 子目录作为每一个提交的新的项目根目录
全局修改邮箱地址
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 cat-file -p HEAD
# 底层命令
git ls-tree -r HEAD
git ls-files -s
重置的作用
git reset --soft HEAD~
# 把该分支移动回原来的位置,而不会改变索引和工作目录
git reset --mixed HEAD~
# 默认,它依然会撤销一上次 提交,但还会 取消暂存 所有的东西
git reset --hard HEAD~
# 让工作目录看起来像索引
通过路径来重置
git reset file.txt
# 其实是 git reset --mixed HEAD file.txt 的简写, 本质上只是将 file.txt 从 HEAD 复制到索引中,还有 取消暂存文件 的实际效果
git reset eb43bf file.txt
# 指定一个提交来拉取该文件的对应版本, 又可以运行 git add 添加它,也可以接受一个 --patch 选项来一块一块地取消暂存的内容。 这样你就可以根据选择来取消暂存或恢复内容了
压缩
git reset --soft HEAD~2
git commit
检出
不带路径
运行 git checkout [branch]
与运行 git reset --hard [branch]
非常相似,它会更新所有三棵树使其看起来像 [branch]
,不过有两点重要的区别
-
checkout
对工作目录是安全的 -
reset
会移动 HEAD 分支的指向,而checkout
只会移动 HEAD 自身来指向另一个分支。
带路径
运行 checkout
的另一种方式就是指定一个文件路径,这会像 reset
一样不会移动 HEAD。 它就像 git reset [branch] file
那样用该次提交中的那个文件来更新索引,但是它也会覆盖工作目录中对应的文件。它就像是 git reset --hard [branch] file
(如果 reset
允许你这样运行的话), 这样对工作目录并不安全,它也不会移动 HEAD
高级合并
合并冲突
git merge --abort
# 简单地退出合并
git reset --hard HEAD
# 回到上一次提交的状态
忽略空白
git merge -Xignore-space-change whitespace
# 这次使用 -Xignore-all-space 或 -Xignore-space-change 选项。 第一个选项在比较行时 完全忽略 空白修改,第二个选项将一个空白符与多个连续的空白字符视作等价的
手动文件再合并
git show :1:hello.rb > hello.common.rb
# Stage 1 是它们共同的祖先版本
git show :2:hello.rb > hello.ours.rb
# stage 2 是你的版本
git show :3:hello.rb > hello.theirs.rb
# stage 3 你将要合并入的版本(“theirs”)
git ls-files -u
# 使用 ls-files -u 底层命令来得到这些文件的 Git blob 对象的实际 SHA-1 值
# 手动修改 hello.theirs.rb 文件
git merge-file -p
hello.ours.rb hello.common.rb hello.theirs.rb > hello.rb
# 命令来重新合并
git diff --ours
# 看看合并引入了什么
git diff --theirs -b
# 合并的结果与他们那边有什么不同, -b 来去除空白
git diff --base
# 查看文件在两边是如何改动的
git clean -f
# 清理我们为手动合并而创建但不再有用的额外文件
检出冲
git checkout --conflict=diff3 hello.rb
# 重新检出文件并替换合并冲突标记
git checkout --ours 和 --theirs
# 选择留下一边, 当有二进制文件冲突时这可能会特别有用
合并日志
git log --oneline --left-right HEAD...MERGE_HEAD
# 得到此次合并中包含的每一个分支的所有独立提交的列表
git log --oneline --left-right --merge
# 显示任何一边接触了合并冲突文件的提交
git log --oneline --left-right -p
# 得到所有冲突文件的区别
组合式差异格式
git diff
git log --cc -p -1
# 查看冲突是如何解决的
撤消合并
# 方法1: 修复引用
git reset --hard HEAD~
# 方法的缺点是它会重写历史,应当避免使用 reset
# 方法2: 还原提交
git revert -m 1 HEAD
# -m 1 标记指出 “mainline” 需要被保留下来的父结点
git revert ^M
# 如果重新 git merge topic 了
其他类型的合并
git merge -Xours mundo
# 使用 ours 快速合并
git merge-file --ours
# 来合并单个文件
git merge -s ours mundo
# 本质上会做一次假的合并
git diff HEAD HEAD~
子树合并
Rerere
重用记录的解决方案
git config --global rerere.enabled true
# 你也可以通过在特定的仓库中创建 .git/rr-cache 目录来开启它
git rerere status
# 通过 git rerere status 告诉你它记录的合并前状态
git rerere diff
# 显示解决方案的当前状态——开始解决前与解决后的样子
git ls-files -u
# 查看冲突文件的之前、左边与右边版本
# 修改解决,然后 commit
git reset --hard HEAD^
# 来回滚分支,然后 checkout 另外一个分支
git rebase master
# 得到了相同的合并冲突, 我们看这个文件,会发现它已经被解决了
git checkout --conflict=merge hello.rb
# 重新恢复到冲突时候的文件状态
使用 Git 调试
文件标注
git blame -L 69,82 Makefile
# 第 69 行到第 82 行每一行分别来自哪个提交和提交者
git blame -C -L 141,153 GITPackUpload.m # 看到代码块的原始出处
二分查找
git bisect start
# 来启动
git bisect bad
# 告诉系统当前你所在的提交是有问题的
git bisect good v1.0
# 告诉 bisect 已知的最后一次正常状态是哪次提交
git bisect good
# 来告诉 Git测试结果是没有问题的, 然后继续寻找
git bisect bad
# 发现这个提交下是有问题的
git bisect reset
# 最后,重置你的 HEAD 指针到最开始的位置
git bisect start HEAD v1.0
# 参数来设定这两个提交,第一个参数是项目不正常的提交,第二个参数是项目正常的提交
git bisect run test-error.sh
# Git 会自动在每个被检出的提交里执行 test-error.sh, 脚本在项目是正常的情况下返回 0,在不正常的情况下返回非 0
子模块
开始使用子模块
git submodule add https://github.com/chaconinc/DbConnector # 后面加上想要跟踪的项目的相对或绝对 URL 来添加新的子模块
git diff --cached --submodule
# 查看另一个是项目文件夹记录
克隆含有子模块的项目
git submodule init
# 用来初始化本地配置文件
git submodule update
# 从该项目中抓取所有数据并检出父项目中列出的合适的提交
git clone --recurse-submodules https:// # 自动初始化并更新仓库中的每一个子模块
git submodule update --init --recursive # 还要初始化、抓取并检出任何嵌套的子模块
在包含子模块的项目上工作
git fetch
# 进入到子目录中
git merge
git diff --submodule # 进入主目录
git config --global diff.submodule log
# 不想每次输入 --submodle
git submodule update --remote DbConnector # 不想在子目录中手动抓取与合并
未完…
打包
git bundle create repo.bundle HEAD master
# 打包
git clone repo.bundle repo
# 接收者使用
git bundle create commits.bundle master ^9a466c5 # 打包区间
git bundle verify ../commits.bundle
# 检查这个文件是否是一个合法的 Git 包
git bundle list-heads ../commits.bundle
#
查看这边包里可以导入哪些分支
git fetch ../commits.bundle master:other-master # 可以使用 fetch 或者 pull 命令从包中导入提交
替换
凭证存储
配置 Git
git config --global commit.template ~/.gitmessage.txt
# 模版
格式化与多余的空白字符
默认被打开的三个选项是:
blank-at-eol
,查找行尾的空格;
blank-at-eof
,盯住文件底部的空行;
space-before-tab
,警惕行头 tab 前面的空格。
默认被关闭的三个选项是:
indent-with-non-tab
,揪出以空格而非 tab 开头的行(你可以用 tabwidth
选项控制它);
tab-in-indent
,监视在行头表示缩进的 tab;
cr-at-eol
,告诉 Git 忽略行尾的回车。
git config --global core.autocrlf true
# 提交时自动地把回车和换行转换成换行,而在检出代码时把换行转换成回车和换行
git config --global core.autocrlf input
# 提交时把回车和换行转换成换行,检出时不转换
git config --global core.whitespace
trailing-space,-space-before-tab,indent-with-non-tab,tab-in-indent,cr-at-eol # 不指定它或在它前面加个 -
git apply --whitespace=warn <patch>
# 探测到这些问题
git apply --whitespace=fix <patch>
# 自动修正此问题
服务器端配置
Git 属性
Git 钩子
底层命令与上层命令
Git 是一个内容寻址(content-addressable)文件系统。
config
# 包含项目特有的配置选项
description
# 仅供 GitWeb 程序使用,我们无需关心
HEAD
# 指向目前被检出的分支
(**重要**)
hooks/
# 包含客户端或服务端的钩子脚本(hook scripts
info/
# 包含一个全局性排除(global exclude)文件
objects/
# 存储所有数据内容
(**重要**)
refs/
# 目录存储指向数据(分支、远程仓库和标签等)的提交对象的指针
(**重要**)
#(尚待创建的)index 文件保存暂存区信息
(**重要**)
Git 对象
echo 'test content' | git hash-object -w --stdin
# 创建一个新的数据对象并将它手动存入你的新 Git 数据库中
git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4 # 取回数据
# 对一个文件进行简单的版本控制
echo 'version 1' > test.txt
git hash-object -w test.txt
echo 'version 2' > test.txt
# 写入新内容
git hash-object -w test.txt
git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt # 从对象数据库中取回它的第一个版本
# 记住文件的每一个版本所对应的 SHA-1 值并不现实, 文件名并没有被保存——我们仅保存了文件的内容
git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a # 告诉我们其内部存储的任何对象类型
树对象
git cat-file -p master^{tree}
# master 分支上最新的提交所指向的树对象
git update-index --add --cacheinfo 100644
83baae61804e65cc73a7201a7252750c76066a30 test.txt
# 通过暂存一些文件来创建一个暂存区
git write-tree
# 将暂存区内容写入一个树对象
git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579 # 把树对象读入暂存区, 可以通过对该命令指定 --prefix 选项,将一个已有的树对象作为子树读入暂存区
提交对象
echo 'first commit' | git commit-tree d8329f
# 创建一个提交对象, 它先指定一个顶层树对象,代表当前项目快照
git log --stat 1a410e
这就是每次我们运行 git add
和 git commit
命令时,Git 所做的工作实质就是将被改写的文件保存为数据对象, 更新暂存区,记录树对象,最后创建一个指明了顶层树对象和父提交的提交对象。
对象存储
Git 是如何存储其对象的
Git 引用
这基本就是 Git 分支的本质:一个指向某一系列提交之首的指针或引用
echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git/refs/heads/master # 创建一个新引用来帮助记忆最新提交所在的位置, 从技术上讲我们只需简单地做如下操作
git log --pretty=oneline master
git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9 # 更安全
git update-ref refs/heads/test cac0ca
# 第二个提交上创建一个分支
HEAD 引用
git symbolic-ref HEAD
refs/heads/test
# 可以手动编辑该文件, 可以借助此命令来查看 HEAD 引用对应的值
标签引用
git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d # 像这样创建一个轻量标签
git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag' # 若要创建一个附注标签,Git 会创建一个标签对象,并记录一个引用来指向该标签对象,而不是直接指向提交对象
远程引用
远程引用和分支(位于 refs/heads
目录下的引用)之间最主要的区别在于,远程引用是只读的
包文件
Git 会时不时地将多个这些对象打包成一个称为“包文件(packfile)”的二进制文件,以节省空间和提高效率。 当版本库中有太多的松散对象,或者你手动执行 git gc
命令,或者你向远程服务器执行推送时,Git 都会这样做。 要看到打包过程,你可以手动执行 git gc
命令让 Git 对对象进行打包
git gc
Git 是如何做到这点的? Git 打包对象时,会查找命名及大小相近的文件,并只保存文件不同版本之间的差异内容。 你可以查看包文件,观察它是如何节省空间的。 git verify-pack
这个底层命令可以让你查看已打包的内容:
git verify-pack -v .git/objects/pack/pack-978e03944f5c581011e6998cd0e9e30000905586.idx
引用规范
传输协议
维护与数据恢复
git reset --hard 1a410efbd13591db07 # 硬重置丢失数据
git reflog
# 引用日志
git branch recover-branch ab1afef # 创建一个新的分支指向这个提交来恢复它
rm -Rf .git/logs/ #
如果已经没有引用日志了
git fsck --full
# 检查数据库的完整性, 显示出所有没有被其他对象指向的对象
移除对象
git rm git.tgz
# 移除大文件
git count-objects -v
# 快速的查看占用空间大小, size-pack 的数值指的是你的包文件以 KB 为单位计算的大小
git verify-pack -v .git/objects/pack/pack-29…69.idx
| sort -k 3 -n | tail -3 # 如果你执行 git gc 命令,所有的对象将被放入一个包文件中,你可以通过运行 git verify-pack 命令, 然后对输出内容的第三列(即文件大小)进行排序,从而找出这个大文件。
git rev-list --objects --all | grep 82c99a3
# 找出具体是哪个文件
git log --oneline --branches -- git.tgz
# 查看哪些提交对这个文件产生改动
git filter-branch --index-filter 'git rm --ignore-unmatch --cached git.tgz' -- 7b30847^.. # 重写 7b30847 提交之后的所有提交来从 Git 历史中完全移除这个文件
rm -Rf .git/refs/original
# 通过 filter-branch 选项添加的新引用中还存有对这个文件的引用
rm -Rf .git/logs/
# 删除引用日志
git gc
# 重新打包
git count-objects -v # 可以从 size 的值看出,这个大文件还在你的松散对象中,并没有消失;但是它不会在推送或接下来的克隆中出现,这才是最重要的
git prune --expire now
# 完全地移除那个对象
环境变量
来自:git官网
最后
以上就是清爽世界为你收集整理的git教程 git笔记 git常用 git使用 git操作 git简明 git版本控制 git仓库git 使用的全部内容,希望文章能够帮你解决git教程 git笔记 git常用 git使用 git操作 git简明 git版本控制 git仓库git 使用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复