Git速查手册(第三版)

本文是对Git速查手册(第二版)的更新,补充了一些近期使用或者收集的一些命令。


设置Git

版本

Git下载地址:https://git-scm.com/downloads,推荐使用v1.8及以上版本。

查看Git版本:

1
$ git --version

用户信息

Git配置分为三个级别:

  • --system:系统级,位于 /etc/gitconfig
  • --global:用户级,位于 ~/.gitconfig
  • --local:仓库级,位于 [repo]/.git/config,为默认级别且优先级最高

删除global用户信息,防止不同Git服务之间冲突:

1
2
$ git config --global --unset user.name
$ git config --global --unset user.email

配置用户名:

1
2
$ git config --local user.name "username"
$ git config --local user.email "email"

保存用户凭证,若同时使用不同的Git服务,不推荐使用:

1
$ git config --global credential.helper store

执行后,下次操作输入的用户名和密码会被保存,后续不必手动输入用户名和密码。

配置超时时间:

1
$ git config --global credential.helper 'cache --timeout=3600'

根据目录配置用户信息,需要使用v2.13.0及以上版本:

  • 首先修改用户目录下的 .gitconfig,通过 includeIf 配置不同目录的配置文件:

    1
    2
    3
    4
    5
    6
    7
    8
    - [user]
    - name = weijie.yuan
    - email = weijie.yuan@gitlab.com

    + [includeIf "gitdir:~/github/"]
    + path = .gitconfig-github
    + [includeIf "gitdir:~/gitlab/"]
    + path = .gitconfig-gitlab
  • 根据配置的 path,分别创建 .gitconfig-github 文件和 .gitconfig-gitlab 文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ vi .gitconfig-github
    [user]
    name = weijie.yuan
    email = weijie.yuan@github.com

    $ vi .gitconfig-gitlab
    [user]
    name = weijie.yuan
    email = weijie.yuan@gitlab.com

includeIf 配置有如下规则:

  • 家目录下的 .gitconfigincludeIf 后面的 path 最后需要 / 结尾;
  • 家目录下的 .gitconfig ,原有的 user 部分需要删除;
  • 家目录下的 .gitconfigincludeIf中配置的各个目录,不能是包含关系;

克隆协议

一般Git服务默认都支持SSHHTTPSSSH支持的原生Git协议速度最快,HTTPS除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令。

SSH

检查本机SSH公钥,若存在,则将id_rsa.pub添加到Git服务的SSH keys

1
$ ls ~/.ssh

若不存在,则生成:

  • 单个Git服务

    1
    $ ssh-keygen -t rsa -C "your_email@youremail.com"
  • 多个Git服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $ ssh-keygen -t rsa -C "your_email@youremail.com" -f "git1_id_rsa"
    $ ssh-keygen -t rsa -C "your_email@youremail.com" -f "git2_id_rsa

    # 创建配置文件
    $ vi ~/.ssh/config
    # git1
    Host git1.com
    HostName git1.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/git1_id_rsa

    # git2
    Host git2.com
    HostName git2.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/git2_id_rsa

配置完成后,再次检查ssh连接情况;若不生效,则重启后再尝试:

1
2
$ ssh -T git@github.com
Hi! You’ve successfully authenticated, but GitHub does not provide shell access.

若出现上述信息,则表示设置成功。

克隆:

1
$ git clone git@git.server:test.git

HTTPS

关闭ssl校验:

1
$ git config --global http.sslverify false

克隆:

1
$ git clone https://git.server/test.git

工具配置

配置diffmerge工具,可以通过git difftool --tool-help查看支持的工具集合,推荐使用meld

  • Linux or MacOS

    1
    2
    $ git config --global diff.tool meld
    $ git config --global merge.tool meld
  • Windows

1
2
3
4
5
> git config --global diff.tool meld
> git config --global merge.tool meld
> git config --global difftool.bc3.path 'C:\Program Files (x86)\Meld\Meld.exe'
> git config --global mergetool.meld.path 'C:\Program Files (x86)\Meld\Meld.exe'
> git config --global difftool.meld.path 'C:\Program Files (x86)\Meld\Meld.exe'

配置显示颜色:

1
$ git config --global color.ui.true

配置Git操作别名,示例:

1
$ git config --global alias checkout co

上面的命令将checkout设置为别名co

查看所有配置

1
2
3
$ git config --local --list
$ git config --global --list
$ git config --system --list

工作流

工作流

工作区就是你的本地仓库文件夹,不过其中的.git目录不属于工作区,而是版本库,里面存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

现在来解释一下前面的添加和提交操作:

  1. git add:把文件修改添加到暂存区;
  2. git commit:把暂存区的所有内容提交到当前分支,即版本区;

基本操作

获取远程仓库:

1
$ git clone git@github.com:USERNAME/repo.git

将本地的仓库添加到远程:

1
2
3
$ cd repo
$ git init
$ git remote add origin git@github.com:USERNAME/repo.git

origin就是一个名字,是git为你默认创建的指向这个远程代码库的标签。

添加修改到暂存区:

1
2
3
$ git add <filename> 
$ git add . # 添加当前目录所有修改过的文件
$ git add * # 递归地添加执行命令时所在的目录中的所有文件

提交修改到版本区:

1
2
$ git commit -m "commit message"
$ git commit -am "commit message" # am:将添加和提交合并为一步,但只对本来就存在的文件有效

commit message的填写可以参考写好 Git Commit 信息的 7 个建议

推送修改到远程服务器:

1
$ git push -u origin <feature-branch-name>

-u选项可以将本地分支与远程分支关联,下次git pull操作时可以不带参数.具体参见这里

查看远程仓库:

1
2
3
$ git remote -v
origin git@github.com:USERNAME/repo.git (push)
origin git@github.com:USERNAME/repo.git (fetch)

添加上游仓库:

1
$ git remote add upstream git@github.com:USERNAME/repo.git

fork后同步上游仓库的更新:

1
2
3
4
5
6
7
8
9
10
$ git remote -v
origin git@github.com:USERNAME/repo.git (push)
origin git@github.com:USERNAME/repo.git (fetch)
upstream git@github.com:USERNAME/repo.git (push)
upstream git@github.com:USERNAME/repo.git (fetch)

$ git fetch upstream
$ git difftool <branch-name> upstream/master
$ git merge upstream/master
$ git mergetool

代码引用在Git上有submodulesubtree两种方式,推荐使用subtree方式:

1
2
3
4
5
6
7
8
9
# 第一次初始化
$ git remote add -f <remote-subtree-repository-name> <remote-subtree-repository-url>
$ git subtree add --prefix=<local-subtree-directory> <remote-subtree-repository> <remote-subtree-branch-name> --squash

# 同步subtree的更新
$ git subtree pull --prefix=<local-subtree-directory> <remote-subtree-repository> <remote-subtree-branch-name> --squash

# 推送到远程subtree库
$ git subtree push --prefix=<local-subtree-directory> <remote-subtree-repository> <remote-subtree-branch-name>

分支

查看所有分支,有*标记的是当前分支:

1
$ git branch -a

创建本地分支:

1
$ git branch <newbranch>

创建并切换本地分支:

1
$ git checkout -b <newbranch>

从标签创建分支:

1
2
$ git branch <branch> <tagname>
$ git checkout <branch> # 切换到新建分支

推送新建本地分支到远程:

1
2
3
$ git push -u origin <remote-branch-name>
or
$ git push --set-upstream origin <remote-branch-name>

删除本地分支:

1
$ git branch -d <branch>

若当前分支因为有修改未提交或其它情况不能删除,请使用-D选项强制删除。

删除远程分支(三种方法):

1
2
3
$ git push origin --delete <remote-branch-name>
$ git push origin -d <remote-branch-name>
$ git push origin :<remote-branch-name>

清除无用的分支:

1
$ git remote prune origin

说明:remote上的一个分支被其他人删除后,需要更新本地的分支列表。

获取远程分支到本地已有分支:

1
$ git branch --set-upstream <local-branch> origin/branch

获取远程分支到本地并新建本地分支:

1
$ git checkout -b <local-branch> <remote-branch>

同步当前分支的更新,使用git pull并不保险:

1
2
3
4
5
6
7
8
# 下载最新的代码到远程跟踪分支, 即origin/<branch-name>
$ git fetch origin <branch-name>
# 查看更新内容
$ git difftool <branch-name> origin/<branch-name>
# 尝试合并远程跟踪分支的代码到本地分支
$ git merge origin/<branch-name>
# 借助mergetool解决冲突
$ git mergetool

同步其它分支的更新,本例拉取master分支更新:

1
2
3
4
$ git fetch origin master
$ git difftool <branch-name> origin/master
$ git merge origin/master
$ git mergetool

查看某个<commit id>属于哪个分支:

1
$ git branch -a --contains <commit id>

标签

查看标签:

1
$ git tag

创建标签:

1
2
$ git tag -a <tagname> -m "tag message" # 创建标签在当前最新提交的commit上
$ git tag -a <tagname> -m "tag message" <commit id> # 创建标签在指定的commit上

推送标签到远程服务器:

1
2
$ git push origin <tagname> # 推送一个本地标签
$ git push origin --tags # 推送全部未推送过的本地标签

删除标签:

1
2
$ git tag -d <tagname> # 删除一个本地标签
$ git push origin :refs/tags/<tagname> # 删除一个远程标签

撤销与回退

查看当前仓库状态:

1
$ git status

查看文件更改:

1
2
$ git difftool <filename>
$ git mergetool <filename>

查看提交历史:

1
2
$ git log
$ git log --pretty=oneline #只保留commit id 和 commit message

撤销工作区Tracked files的修改:

1
$ git checkout -- <filename>

撤销工作区Untracked files的修改:

  • n:查看将会删除的文件,防止误删;
  • f:Untracked的文件;
  • d:Untracked的目录;
  • x:包含gitignoreUntracked文件和目录一并删掉,慎用!;
1
git clean -nfd

回退版本区(git commit)和暂存区(git add),不删除工作空间代码:

1
2
$ git reset --mixed HEAD^ # --mixed为默认参数
$ git reset HEAD^

回退版本区(git commit),暂存区(git add)不回退,不删除工作空间代码:

1
$ git reset --soft HEAD^

回退版本区(git commit)和暂存区(git add),并删除工作空间代码(不包括Untracked files),执行后直接恢复到指定<commit-id>状态:

1
$ git reset --hard <commit-id>

HEAD表示当前版本,HEAD^表示上个版本,HEAD^^表示上上个版本,上100个版本可以表示为HEAD~100以此类推。

回退版本后,若需要返回原来的版本,会发现找不到未来的commit id,则需要查看操作命令历史进行查找:

1
$ git reflog

从版本库删除文件:

1
$ git rm <filename>

若你的代码已经push到线上,则推荐使用下面这个命令回滚:

1
$ git revert <commit-id>

revert是用一次新的commit来回滚之前的commit,更安全;reset则是直接删除指定的commit,若直接push会导致冲突。

暂存

当你需要切换分支时,若当前工作区还有些修改没有完成、又不适合提交的,操作切换分支是会提示出错的,这时就需要将这些修改暂存起来:

1
$ git stash save "message"

查看:

1
$ git stash list

恢复:

1
2
3
$ git stash pop [--index] [stash@{num}] 
or
$ git stash apply [--index] [stash@{num}] # 不删除已恢复的进度.

--index表示不仅恢复工作区,还会恢复暂存区;num是你要恢复的操作的序列号,默认恢复最新进度。

删除进度:

1
2
$ git stash drop [stash@{num}] # 删除指定进度
$ git stash clear # 删除所有

清理仓库

清理无用的分支和标签

1
2
3
4
$ git branch -d <branch-name>
$ git tag -d <tag-name>
$ git remote prune origin
$ git pull

清理大文件

  • 查看git相关文件占用空间:

    1
    2
    $ git count-objects -v
    $ du -sh .git
  • 寻找大文件ID

    1
    $ git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10

输出的第一列是文件ID,第二列表示文件(blob)或目录(tree),第三列是文件大小,此处筛选了最大的10条。

  • 获取文件名与ID映射:

    1
    $ git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}')"
  • 从所有提交中删除文件:

    1
    2
    $ git filter-branch --tree-filter 'rm -rf xxx' HEAD --all
    $ git pull
  • 清理.git目录:

    1
    $ git gc --prune=now

在执行push操作时,git会自动执行一次gc操作,不过只有loose object达到一定数量后才会真正调用,建议手动执行。

处理大型二进制文件

由于Git在存储二进制文件时效率不高,所以需要借助第三方组件

忽略特殊文件

当你的仓库中有一些文件,类似密码或者数据库文件不需要提交但又必须放在仓库目录下,每次git status都会提示Untracked,看着让人很不爽,提供两种方法解决这个问题

本地

在代码仓库目录创建一个.gitignore文件,编写规则如下:

1
2
3
tmp/  # 忽略tmp文件夹下所有内容
*.ini # 忽略所有ini文件
!data/ #忽略除了data文件夹的所有内容

.gitignore模版

全局

在用户目录创建一个.gitignore_global文件,编写规则同.gitignore,并修改~/.gitconfig

1
2
[core]
excludesfile = ~/.gitignore_global

如果添加的忽略对象已经Tracked,纳入了版本管理中,则需要在代码仓库中先把本地缓存删除,改变成Untracked状态:

1
$ git rm -r --cached .

奇技淫巧

重写历史(慎用!)

1
2
$ git rebase -i [git-hash| head~n]
$ git push -f # 不强制 push 会多一条 merge 提交信息

其中git-hash是你要开始进行rebasecommithash,而head~n则是从HEAD向前推ncommit

全局更换电子邮件

1
2
3
4
5
6
7
8
9
git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "xxx@localhost" ];
then
GIT_AUTHOR_NAME="xxx";
GIT_AUTHOR_EMAIL="xxx@example.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD --all

帮助

查看帮助:

1
$ git --help

Reference

  1. 廖雪峰老师的git教程
  2. 常用Git命令清单
  3. Git-Book
  4. Git-Reference
  5. Git push与pull的默认行为
  6. git stash 详解

forthebadge

微信公众号同步更新,微信搜索"AnSwEr不是答案"或者扫描二维码,即可订阅。
AnSwEr(Weijie Yuan) wechat