Contents

Git 标签式文件系统

Git 的本质

Git 的本质是一个可追踪历史的标签式文件系统. 它的基本元素包括对象和引用两种.

Git 对象

Git 的对象由键值内容对组成. 通过

      find .git/objects -type f

可以查看所有对象的键值, 键值的前两个字符为目录, 剩余字符为文件.

Git 对象包括四种类型.

通过

      git cat-file -t

可以查看对象类型. blob 对象通常保存文件, 而 tree 对象则保存目录. 暂存区 index 就是一个 tree 对象. 当用户调用 git commit 时, 会产生一个 commit 对象. commit 对象的内容包括

当用户调用 git tag -a 时, 会产生一个 tag 对象. tag 对象的内容包括

通过

      git cat-file <key>

可查看普通对象内容. 通过

      git ls-tree <key>

可查看 tree 对象的内容. 通过

      git commit-tree <tree> [(-p <parent>) ...] -m "message"

可以手动提交一个 commit 对象. 其中, parent 依次列出该对象的每个 parent commit 对象. message 为提交的 log 信息.

Git 引用

Git 引用包括三种类型.

普通引用就是一个指向 Git 对象的指针. 而符号引用就是一个指向 Git 普通引用的指针. 所有引用保存于

      .git/refs

中. 分于三个目录中保存.

      .git/refs/heads/
      .git/refs/tags/
      .git/refs/remotes/

其中, heads 中包含了所有的 branch, 其中的引用就指向每个开发分支的最前沿, 每次调用 git commit, heads 中以相应分支命名的引用都会移动到最新提交的 commit 对象. tags 中包含了所有的 tag, 即通过 git tag 生成.

通过命令

      git update-ref refs/heads/test [cac0ca]

可以查看普通引用 refs/heads/test 或创建一个指向 cac0ca 对象的普通引用. HEADS 是个符号引用, 指向当前 branch 的最新提交的对应 heads 引用. 通过

      git symbolic-ref HEAD refs/heads/test

可以查看或创建符号引用. 当使用 git tag 时, 会在 tags/ 中创建一个普通引用, 指向用户指定的 commit 对象. 当使用 git tag -a 时, 会先创建一个以用户指定的 commit 对象为指针的 tag 对象 (同时还包含时间, message 等辅助信息), 然后再在 refs/tags/ 中创建一个指向上述新建 tag 对象的普通引用.

远程引用全部保存于 .git/refs/remotes/ 中. 其下一级目录为远程 git repository 的名称 (例如 origin). 每个远程引用代表远程 repository 中的一个 heads 或 tags 引用.

每个引用都可以使用后缀 ~n 语法指定第几辈祖先. 用 ^n 语法指定第几个 parent.

      ^ = ~1 
      ^^ = ~2

git 还会记录所有引用修改的历史. 通过

      git reflog --date=iso

可以查看该历史, 从而跟踪 git 提交的过程和时间.

命令备忘录

查看本地工作目录状态

      git status -uno -s

查看当前 branch 的相关历史

      git log --graph [--oneline] --author=...

--graph 用于以图的方式显示历史. --oneline 用于把每条历史显示在一行内. --author= 用于根据 author 过滤信息.

查看某个特定文件的修改历史

    git log -p <file_path>

切换 branch

      git checkout <branch_name>

新建 branch

      git branch <branch_name>

查看 branch 信息

      git branch -vv -a

-vv: 包含对远程 branch 的跟踪状态, -a: 包含远程 branch 本身.

建立远程引用

      git remote add <remote_name> <remote_url>

在本地新建一个跟踪到远程 branch 的 branch, 并切换到该 branch.

      git checkout -b [branch] [remotename]/[branch] 
      git checkout --track [remotename]/[branch] 

设置当前本地 branch 跟踪到远程 branch.

      git branch -u origin/serverfix

重命名一个 branch

      git branch -m <old_name> <new_name>

从远程 repository 中获取数据

      git fetch <remote> [refspec]

其中 remote 为远程 repository 名字. refspec 为如下格式

      [+]<src>:<dst>

src 为远程 branch, dst 为本地 ref 路径 (refs/..., 通常省略, 从而默认为 refs/remotes/<remote>/<branch>), 如果省略 refspec, 则 fetch 所有远程 branch (不推荐). + 表示强制取回 (不推荐). 从而通常形式为

      git fetch <remote> <branch>

合并某个 branch (本地或远程) 到当前 branch.

      git merge <branch|remote/branch>

仅合并某个 branch 到当前工作目录, 不提交.

      git merge --no-commit --no-ff <branch|remote/branch>

取消 merge 状态, 恢复为普通工作目录. (merge 途中无法执行 git commit --amend, 且存在未提交到 index 的修改)

    git update-ref -d MERGE_HEAD

中止 merge, 保持当前状态. (merge 途中无法执行 git commit --amend, 且存在未提交到 index 的修改)

    git merge --quit

此时不会动位于 index 里面的内容.

自动从跟踪的远程分支中合并代码

      git pull

相当于依次调用

      git fetch <remote> <branch>
      git merge remote/branch

其中, remote 和 branch 取自当前 branch 的跟踪信息. 如果 pull 中出现 fatal: refusing to merge unrelated histories 错误. 则加上选项 --allow-unrelated-histories 即可.

向远程跟踪分支中推送代码

      git push <remote> <local_branch>:<remote_branch>

向远程仓库添加本地建立的新分支

      git push -u <remote> <local_branch>:<remote_branch>

删除远程分支

      git push <remote> :<remote_branch> 

或

      git push <remote> --delete <remote_branch>

删除远程 tag

      git push <remote> --delete <remote_tag>

建立 git web 服务的工具: Gitweb

查看工作目录的修改

      git diff

或

      git difftool

提交修改

      git commit [-a] [-m "msg"]

当 git merge 出现冲突时, 可用如下命令修正冲突

      git mergetool

然后再用

      git commit

提交合并结果

恢复到 merge 前的状态

      git merge --abort

提交部分修改到 index

      git add <file> 

交互式选择提交每一处修改

      git add -i 

暂存当前的工作目录 (只保存针对当前 branch diff 的部分)

      git stash push

取回暂存的工作目录并应用到当前 branch 上.

      git stash pop

交换两个 parent 的顺序

      git reset --hard $(git commit-tree -p HEAD^2 -p HEAD^1 -m "New commit message" "HEAD^{tree}")

打包当前 git repository (用于在网络环境不好时远程交换)

      git bundle create <file>

设提交路径为 a1 -- a2 -- a3 -- a4 -- a5 -- a6, 若要删除提交 a2, a3, 则可用命令

git rebase --onto a1 a3 a6 或 git rebase --onto HEAD~5 HEAD~3 HEAD

常用配置


[user]
	name = Colin Teng
	email = tfcolin@224488.com
[diff]
	tool = vimdiff
[difftool]
      prompt = true
[merge]
	tool = vimdiff
[mergetool]
      prompt = false
[alias]      
      d = difftool
[difftool "vimdiff"]
      cmd = vimdiff "$LOCAL" "$REMOTE"
[core]
      editor = nvim
[log]
      decorate = full

本地配置文件位于: .git/config 全局配置文件位于: ~/.gitconfig

可以通过

      git config [--local|--global] aaa.bbb <value|default>

来设置选项. 通过

      git config [--local|--global] -l

来列出所有选项. (--local 表示针对当前 repository 的选项. --global 为全局选项)

可以在 git repository 目录中编辑 .gitignore 文件, 每行一个要忽略的文件 pattern, 相应文件就会在 git add 时被忽略.