====== GIT ======
===== 一、为什么要使用代码版本控制系统 =====
* 历史版本的说明、存储与备份
* 便于团队协同合作
* 便于查看代码差异、变动历史
===== 二、GIT 与 SVN 的区别 =====
* GIT 为分布式的,GIT 工程每个副本都是一个备份;而 SVN 是非分布式的,依赖一个中心服务器;
* SVN 只有一个中心服务器,GIT 可以有0个或多个远端备份;svn是提交,git是同步;
* SVN 得连接中心服务器才能查看历史log; 而git不用;
* GIT 以元数据方式存储(于.git隐藏目录);SVN 以文件方式存储;
* SVN 的分支是一个完整文件目录;而 git 只保持一份本地文件目录,切换分支直接改变该目录文件;
* GIT 没有全局唯一版本号;SVN 有;
* git 可以打 tag 来弥补;但是 tag 只与 commit id对应,而 commit id 不能保证代码的唯一性(因为不同分支的同一 commit id 之前的提交历史可能不一样),所以要保证 tag 唯一性,可额外人为规定只有主分支打的tag是有效的。
===== 三、GIT 资料 =====
==== Installation ====
* 查看官网[[http://git-scm.com]]来下载安装
* CentOS yum 默认只有1.7.1 版本, 但像github.com等网站需要高于此版本. 此时需要源码安装升级.
* 如果源码安装,[[http://github.com/git/git|github repo]], ''make'', ''make install prefix=/usr'', 如果不添加''prefix=/usr'', 默认会安装在当前home目录.
==== Tools ====
* 显示仓库信息[[https://github.com/o2sh/onefetch|onefetch]], 这个不错
==== Usage====
* [[http://rogerdudler.github.io/git-guide/index.zh.html| git 简明指南]]
* [[http://marklodato.github.io/visual-git-guide/index-zh-cn.html?no-svg| 图解 Git]]
* [[https://git-scm.com/book/zh/v2| git scm 官方文档]]
* [[http://git-scm.com/book/zh/v1| Pro Git]]
* [[http://yonghaowu.github.io/2017/06/18/TheGitYouShouldKnow/|The Git You Should Know]]
* [[http://wp.joak.org/?p=62 | Git 博文备查]]
* [[https://github.com/commitizen/cz-cli | Commitizen:Git 提交信息优化]]
* [[http://pcottle.github.io/learnGitBranching/|Learning git branching]]
* [[https://nvie.com/posts/a-successful-git-branching-model/|git flow]],[[https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow|gitflow-workflow]]
* [[https://jvns.ca/blog/2024/01/26/inside-git/|Inside .git]], 介绍 .git 文件夹内容
==== Ignore ====
* [[http://git-scm.com/docs/gitignore|gitignore]]
==== Submodule ====
* 参考[[http://josephjiang.com/entry.php?id=342|Git Submodule 的認識與正確使用!]]
* 简单概括:**父项目只记录子模块的commit id**,任何和“子模块”常识违背的额外操作,想想这个就理解了但不能接受
* 新增Submodule:
git submodule add <欲放置的位置> # 增加一個新的 submodule
git add .gitmodules <放置的位置>
git commit -m "Add submodule into version control" # 提交修改
git submodule init # 初始化.git/config
* 更新已安裝的Submodule,**需额外手动更新子模块**:
cd your-submodule-folder # 进入子模块所在文件夹
git pull origin master # 更新子模块,这里不会更新父项目
cd ..//回父项目目录
git add your-submodule-folder
git commit -m "static/platform submodule updated"
# 这里实际只提交了子模块commit id
* 团队人员使用 Submodule:
git submodule init
git submodule update
# 或一条命令循环初始化及更新子模块
git submodule update --init --recursive
* 修改Submodule的内容:
# 直接进入子模块文件夹,进行常规git操作
cd your-submodule-folder
git add something
git commit -m "modify something"
cd .. # 还要回父项目根目录进行更新
git add your-submodule-folder
git commit -m 'Submodule updated'
git push
* 移除Submodule:
# 先砍掉目录
git rm --cached <欲移除的目录>
rm -rf <欲移除的目录>
# 修改 .gitmodules,将相关内容移除
vim .gitmodules
# 再修改 .git/config,将相关内容移除
vim .git/config
# 提交修改
$ git add .gitmodules
$ git commit -m "Remove a submodule"
# 安全起见再做个 sync:
$ git submodule sync
==== Git补丁 ====
* 一般打git补丁步骤
git diff > ../sync.patch # 生成补丁
git apply ../sync.patch # 打补丁
git apply --check ../sync.patch # 测试补丁能否成功
* [[https://git-scm.com/docs/git-format-patch|git format-patch]]
git format-patch -1
git am
==== Tips ====
* 查看暂存区(git add 的内容)的修改:''git diff --staged'' 或 ''git diff --cached''
* 查看每次修改的文件统计(不显示具体代码修改):git whatchanged --stat
# 或者
git log --name-status
* 设置git走本地代理
#只对github.com
git config --global http.https://github.com.proxy socks5://127.0.0.1:1080
#取消代理
git config --global --unset http.https://github.com.proxy)
* 增加远程仓库git remote add [repo_name] git@remote_repo
* 选择远程仓库push git push [repo_name] [branch]
* 删除远程仓库分支:git push origin --delete branchname
* 删除本地的远程分支追踪(比如在远程分支删除后本地''git branch -a''还有遗留remote分支)git branch -r -d origin/your_branch_name
* 同步本地的远程分支追踪git remote prune origin
* 打tag最好用-a -m ,多一个obj,方便管理 git tag -a tagname -m "message" [commit]
* 删除远程仓库tag
git push origin :refs/tags/目的标签名
解释:
# 事实上Git 的推送和删除远程标签命令是相同的,删除操作实际上就是推送空的源标签refs:
git push origin 标签名
# 相当于
git push origin refs/tags/源标签名:refs/tags/目的标签名
# git push 文档中有解释:
# tag <> means the same as refs/tags/:refs/tags/.
# Pushing an empty allows you to delete the ref from the remote repository.
* merge 分支后, 如果隐藏了分支的 commit 信息, 具体查看可以使用git log --graph
来列出合并分支的 commit , 再一一查看.
* 撤销 ''git add FILE'' : git rm --cached FILE # 撤销文件FILE
git rm -r --cached FOLDER # 撤销文件夹FOLDER
* 加个比较爽的输出
git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
* 合并特定的单个commit,可使用指令 ''git cherry-pick''
* 凭证存储: [[https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%87%AD%E8%AF%81%E5%AD%98%E5%82%A8|credential.helper]],
* 如果设为store模式,将在本地明文存储用户密码,节省下次输入:git config credential.helper store
:!:这样会明文保存密码到 '' ~/.git-credentials''
* 打印更多日志:GIT_TRACE=2 GIT_CURL_VERBOSE=1 git clone
详见[[https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables#Debugging|git 环境变量]]
* 提交空commit: git commit --allow-empty -m "Empty"
* 修改上一次commit的message: git commit --amend -m "new msg"
* 设置 vim 为默认编辑器:git config --global core.editor "vim"
* 删除某文件的所有历史记录,[[https://blog.csdn.net/yxpandjay/article/details/111275665|参考]]:
* 查找大文件:'' git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')" ''
* 从历史记录删除:'' git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch "your/big.file"' HEAD --all ''
* 显示差异时忽略行尾空格差异: git diff --ignore-space-at-eol
==== server: gitolite ====
* [[http://gitolite.com/gitolite/index.html|gitolite docs]]
* :!: ''conf/gitolite.conf''里,权限项单独的 ''C ''才表示创建 repo 的权限, 而 ''RWC''的 ''C''是表示禁止创建ref. 具体看[[http://gitolite.com/gitolite/conf.html#write-types|文档]].
==== Git 原理 ====
搞懂原理后发现, git 是一个典型的, 以方便实现而设计出的工具. 所以其并没有充分考虑使用的体验. 虽然它强大, 效率高,
但对使用者来说, 命令的正交性不强这一点, 就造成了使用者需要记住大量单独的命令.
考虑找个或弄个简化版的本地版本控制系统, 或者做个git的命令包装.
这篇文章不错:https://www.highflux.io/blog/what-makes-git-hard-to-use
* 通过git管理的文件版本信息全部存放在项目根目录''.git''下
* Git 的三个区域:
* **working directory**,也就是你所操作的那些文件
* **history**,你所提交的所有记录,文件历史内容等等。git是个分布式版本管理系统,在你本地有项目的所有历史提交记录;文件历史记录;提交日志等等。
* **stage(index)**,暂存区域,本质上是个文件,存在于''.git/index''
* Git 的存储对象,保存在''.git/objects''目录,一个对象一个文件, 使用 zlib 压缩:
* **blob**,用于表示一个文件
* **tree**,用于表示一个目录,索引到若干文件或子目录
* **commit**,用于表示一次提交(commit)
* **tag** , 标签对象, annotated tag, 含注释的标签
* 可使用 ''git cat-file''子命令来查看对象.
* Git 的refs 引用, 保存在''.git/refs''目录下:
* heads, ''.git/refs/heads/'' 存分支引用.
* HEAD, ''.git/refs/HEAD'', 指向你当前所在分支的引用.
* tags, ''.git/refs/tags/'', tag引用
* remotes, ''.git/refs/remotes/'', Git 会把你最后一次推送到远端的每个分支的值都记录在该目录下
* 子命令''git update-ref''可处理大部分 refs 相关的操作
* 区域和对象如何交互的可以用下图描述:{{ :public:it:git-objects.png?direct |}}