记录Git的常用命令及使用技巧。

Contents

常用命令

详细教程参考

初始化

1. 用户初始化
# system: `/etc/gitconfig`@linux, `path-to-install-git/gitconfig`@windows
git config --system user.name "yirami"
git config --system user.email "i@yirami.xyz"
# (✓) global: `~/.gitconfig`
git config --global user.name "yirami"
git config --global user.email "i@yirami.xyz"
# project: `path-to-project/.gitconfig`
cd `path-to-project`
git config user.name "yirami"
git config user.email "i@yirami.xyz"

# don't check file mode change
git config --add core.filemode false
2. 密钥初始化
# 针对不同用途创建不同的密钥
ssh-keygen -t rsa -f ~/.ssh/id_rsa -C "i@yirami.xyz"
ssh-keygen -t rsa -f ~/.ssh/id_rsa_yirami -C "i@yirami.xyz"
ssh-keygen -t rsa -f ~/.ssh/id_rsa_singulato -C "tangzhiyong@singulato.com"

# 多密钥管理方式:
#   1) (×)ssh-agent:该方法为一次性的,重启shell需重新添加
#   2) (✓)所有平台使用同一个密钥,哈哈
#   3) 通过配置文件管理:

vim ~/.ssh/config

Host github
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_yirami

Host gitlab
    HostName 10.2.2.11
    User git
    Port 8000
    IdentityFile ~/.ssh/id_rsa_singulato

ssh -T git@github.com  # testing
ssh -T git@10.2.2.11
3. 仓库初始化
mkdir project_name
cd project_name
git init
git init --bare  # 裸仓库,仅存储历史

本地操作

  1. git stash | 暂存改动到草稿
git stash list  # 查看当前的草稿栈
git stash push -m "desc"
git stash pop  # 弹出第一个,及stash@{0}
git stash pop stash@{num}  # 弹出第num+1个
  1. git add | 将工作区文件添加到暂存区(stage)
git add .  # 添加所有改动到暂存区
git add file_name  # 添加某改动文件到暂存区
  1. git commit | 提交暂存区改动
git commit -m "..."
  1. git diff | 比较差异
git diff [file]  # 比较工作区与暂存区的差异
git diff --staged [file]  # 比较暂存区与HEAD的差异
git diff [first-branch]...[second-branch]  # 比较两次提交的差异
  1. git show | 查看提交的改动
git show commit_hash  # 查看commit_hash有什么改动
git show file  # 查看当前提交中file有什么改动
  1. git branch | 编辑分支
git branch  # 显示仓库本地所有分支
git branch name  # 创建name分支
git branch -d name  # 删除name分支
git branch -m old new  # 重命名分支
  1. git checkout | 切换或创建分支
git checkout branch_name  # 切换到分支branch_name
git checkout -b branch_name  # 创建branch_name分支并切换过去
git checkout --orphan branch_name  # 创建空分支
git checkout -- file_name  # 撤销对file_name的修改(注意`--`与切换分支区分)
  1. git reset | 版本回退
git reset --hard HEAD  # 强行恢复到HEAD提交(所有文件均改动)
git reset --hard HEAD^^  # 强行恢复到HEAD之前2个提交(所有文件均改动)
git reset --hard HEAD~2  # 强行恢复到HEAD之前2个提交(所有文件均改动)
git reset --hard commit_hash  # 强行恢复到commit_hash提交(所有文件均改动)
git reset --soft commit_hash  # HEAD指向commit_hash提交(但文件不变)
  1. git merge | 合并分支

  2. git rebase | 变基(避免通过分支合并的方式合并修改)

git rebase other_branch  # 将当前分支从other_branch上分叉出的提交节点改变到最新提交,看起来就像把当前分支中新的提交强行挪到了other_branch最新提交之后
git rebase -i  # 交互式提交编辑,通过修改为`s`进行提交合并
  1. git status | 查看上次提交(commit)后工作区的文件改动

  2. git log | 查看仓库提交(commit)历史

git log
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%Cblue%cn%Cgreen, %cr)%Creset' --abbrev-commit --date=relative
  1. git reflog | 查看参考历史(常用于恢复)

  2. git clean | 删除未跟踪文件

# 删除未跟踪文件
git clean -f
# 删除未跟踪文件(含目录)
git clean -fd
# 删除未跟踪文件(含目录及被ignore排除的部分)
git clean -xfd
# 删除未跟踪文件(预先展示将删除哪些文件)
git clean -nf

与远程端互动

  1. git clone | 从远程仓库克隆
git clone [-b master] <repo> [dir]  # 从仓库克隆master分支到本地dir目录
  1. git remote | 连接远程仓库
git remote -v  # 显示本地仓库连接的所有远程仓库
git remote add repo_name repo_url  # 新增远程仓库连接
git remote set-url --add repo_name repo_url  # 远程仓库新增多个地址,push时都推送,pull时仅第一个有效
git remote remove repo_name  # 删除远程仓库连接
git remote prune repo_name --dry-run  # 删除本地失效的远程仓库repo_name的跟踪
  1. git fetch | 拉取远程分支到本地分支(一般不是当前分支)
git fetch repo_name remote_branch:local_branch  # 从repo_name拉取分支remote_branch到local_branch
  1. git push | 推送本地分支到远程分支
git push repo_name local_branch:remote_branch
git push -f repo_name local_branch:remote_branch  # 强制推送(远程仓库需有对应权限)
  1. git pull | 拉取远程分支到本地分支(一般为当前分支)
git pull repo_name remote_branch:local_branch  # 从repo_name拉取分支remote_branch到local_branch
git pull -f repo_name remote_branch:local_branch  # 强制拉取
  1. git tag | 操作标签
git tag --list  # 列出标签
git ls-remote --tags repo_name  # 列出repo_name的标签
git tag tag_name  # 创建标签 
git tag -a tag_name -m 'tag_description'  # 创建带注释的标签 
git show tag_name  # 查看标签信息
git tag -d tag_name  # 删除标签
git push repo_name tag_name  # 推送标签到远程
git push -d repo_name tag_name  # 删除(远程)标签
git push repo_name --tags  # 推送所有标签到repo_name
git fetch repo_name 'refs/tags/*:refs/tags/*'  # 从repo_name拉取所有tag

高级操作

.gitconfig

git submodule

在一个项目中包含另一个项目,可以使用子模块

# 添加子模块(可指定存放路径)
git submodule add https://github.com/pybind/pybind11 [third_party]
# 检查仓库变化
git status
git diff --cached --submodule
# 克隆包含子模块的项目
git clone xxx
git submodule init
git submodule update
# 克隆包含子模块的项目(简单)
git clone --recurse-submodules xxx

git hook

git hook 类似于回调函数,可以在发生某些事件时触发自定义脚本。自定义脚本需没有扩展名,且具备可执行权限。

脚本存储在.git/hooks/(git init)或hooks(git init --bare)下。

Hook脚本不会被管理同步

1. 本地端
  1. pre-commit | 在Git请求你输入提交日志或者生成提交对象前触发

没有参数传递给pre-commit脚本,并且非零状态时候退出会暂停整个提交

  1. prepare-commit-msg | 在pre-commit后操作输入提交信息的文本编辑器后触发

这是为压缩(squashed)或者合并提交时修改自动生成提交信息的最佳时机

  1. commit-msg | 在用户输入提交日志时触发

可以提示提交的日志不符合标准

  1. post-commit | 在commit-msg后触发,不能改变git commit操作的结果

可以用来发送通知

  1. post-checkout | 切换分支后触发

可以用于清理工作目录生成的文件

  1. pre-rebase | 在git rebase调用前触发

  2. pre-push | 发起git push前触发

2. 服务端
  1. pre-receive | 接受客户端git push前触发

  2. update | 在pre-receive后但真正更新之前触发

  3. post-update |

  4. post-receive | 接受客户端git push成功后触发