AnSwErYWJ's Blog

Git速查手册(第三版)

字数统计: 4.3k阅读时长: 94 min
2020/09/29

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

下载与安装

Git下载地址:https://git-scm.com/downloads,安装请参考页面说明。

建议使用版本v1.8及以上。

配置

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服务默认都支持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
    17
    18
    19
    $ 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"
    $ cp git1_id_rsa* ~/.ssh/
    $ cp git2_id_rsa* ~/.ssh/

    # 创建配置文件
    $ 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

配置完成后,将对应的id_rsa.pub添加到Git服务的SSH keys,再次检查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

协议切换

查看当前协议:

1
$ git remote -v

https切换至ssh

1
git remote set-url origin git@domain:username/ProjectName.git

ssh切换至https

1
git remote set-url origin https://domain/username/ProjectName.git

自定义配置

超时时间

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

保存用户凭证

1
$ git config --global credential.helper store

执行后,下次操作输入的用户名和密码会被保存,后续不必手动输入用户名和密码。若同时使用不同的Git服务,则不推荐使用。

多Git服务

若同时使用不同的Git服务,可以根据目录配置用户信息(需要使用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中配置的各个目录,不能是包含关系;

文本编辑器

Linux or MacOS

1
$ git config --global core.editor vim

Windows

1
> git config --global core.editor "'C:/Program Files/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin"

文本比较合并工具

查看支持的工具集合(推荐使用meld):

1
$ git difftool --tool-help

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

操作别名

示例,将checkout设置为别名co

1
$ git config --global alias 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 status

添加修改到暂存区:

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 add:把文件修改添加到暂存区;
  2. git commit:把暂存区的所有内容提交到当前分支,即版本库;

版本历史记录

查看当前仓库所有文件的版本历史记录:

1
$ git log

查看每个文件的版本历史记录:

1
$ git log <filename>

查看包含指定关键字的版本历史记录:

1
$ git log --grep="keywords"

查看指定时间段的版本历史记录,如下示例时间段为2020.9.23全天:

1
$ git log --after="2020-9-23 00:00:00" --before="2020-9-23 23:59:59"

暂存

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

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
$ 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 add),不删除工作空间代码:

1
$ git reset HEAD <filename> # 无filename则默认回退全部 

回退版本区(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 --help

仓库管理

推送本地修改到远程仓库

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

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

添加本地仓库到远程

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

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

获取远程仓库

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

查看远程仓库

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

同步远程仓库的更新

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替代submodule

submodule

添加子模块
1
$ git submodule add git@github.com:USERNAME/repo.git <submodule-path>

执行成功后,暂存区会有两个修改:.gitmodules和命令中<submodule-path>指定的路径。

提交更新:

1
2
$ git commit
$ git push
使用子模块

克隆使用了子模块的项目后,默认其子模块目录为空,需要在项目根目录执行如下命令单独下载:

1
2
3
4
5
6
$ git submodule update --init --recursive

or

$ git submodule init
$ git submodule update
更新子模块

子模块仓库更新后,使用子模块的项目必须手动更新才能同步最新的提交:

1
2
$ cd <submodule-path>
$ git pull

完成后返回项目根目录,可以看到子模块有待提交的更新,执行提交即可:

1
2
3
$ git add .
$ git commit
$ git push
删除子模块

删除子模块目录及源码:

1
$  rm -rf <submodule-path>

删除项目根目录下.gitmodules文件中待删除的子模块相关条目:

1
$ vi .gitmodules 

删除版本库下的子模块目录,每个子模块对应一个目录,只删除对应的子模块目录即可:

1
rm -rf .git/module/<submodule-path>

删除子模块缓存:

1
git rm --cached <submodule-path>

提交更新:

1
2
3
$ git add .
$ git commit
$ git push

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
2
3
$ git pull # 拉取更新
$ git remote prune origin --dry-run # 列出所有可以从本地仓库中删除的远程追踪分支
$ git remote prune origin # 清理本地无效的远程追踪分支

清理无用的分支和标签

1
2
$ git branch -d <branch-name>
$ git tag -d <tag-name>

清理大文件

  • 查看仓库占用空间:

    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
    $ git filter-branch --force --index-filter 'git rm -rf --cached --ignore-unmatch [FileName]' --prune-empty --tag-name-filter cat -- --all
  • 删除缓存下来的refgit操作记录:

    1
    2
    $ git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
    $ git reflog expire --expire=now --all
  • 清理.git目录并推送到远程:

    1
    2
    $ git gc --prune=now
    $ git push -f --all

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

  • 重新查看仓库占用空间,发现较清理前变小很多:

    1
    2
    $ git count-objects -v
    $ du -sh .git

清理大型二进制文件

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

分支管理

查看分支

查看所有分支:

1
$ git branch -a

*标记的是当前分支。

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

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

创建分支

在本地创建分支:

1
$ git branch <newbranch> # 创建

在本地创建分支并切换:

1
$ git checkout -b <newbranch> # 创建并切换

从标签创建分支:

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

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

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

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

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

创建空白分支

创建一个分支,该分支会包含父分支的所有文件,但不会指向任何历史提交:

1
$ git checkout --orphan <newbranch>

删除所有文件:

1
$ git rm -rf .

提交分支:

1
2
3
4
$ echo '# new branch' >> README.md
$ git add README.md
$ git commit
$ git push origin <remote-branch-name>

删除分支

删除本地分支:

1
$ git branch -d <branch>

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

清理无用的本地分支:

1
$ git remote prune origin

通常在remote上的分支被删除后,更新本地分支列表时使用。

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

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 branch --set-upstream <local-branch> origin/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

同步其它分支的部分更新,即同步某几次提交:

1
2
3
4
5
6
7
8
# 同步提交A
$ git cherry-pick <commit id A>
# 同步提交A和B
$ git cherry-pick <commit id A> <commit id B>
# 同步提交A到B的所有提交(不包括A),提交A必须早于提交B,否则命令将失败,但不会报错
$ git cherry-pick <commit id A>..<commit id B>
# 同步提交A到B的所有提交(包括A),提交A必须早于提交B,否则命令将失败,但不会报错
$ git cherry-pick <commit id A>^..<commit id B>

标签管理

查看标签

1
$ git tag

创建标签

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

若创建标签基于的commit被删除,标签不会被影响,依旧存在。

推送标签

推送标签到远程服务器:

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

删除标签

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

进阶技巧

忽略特殊文件

当你的仓库中有一些文件,类似密码或者数据库文件不需要提交但又必须放在仓库目录下,每次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
$ git commit --amend

修改提交记录中的用户信息

修改最近一次提交的用户信息:

1
$ git commit --amend --author="GIT_AUTHOR_NAME <GIT_AUTHOR_EMAIL>"

全局修改用户信息:

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

Reference

原文作者:AnSwErYWJ

原文链接:https://answerywj.com/2020/09/29/git-help-v3/

发表日期:2020/09/29 15:09

版权声明:本文采用Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License进行许可.
Creative Commons License

CATALOG
  1. 1. 下载与安装
  2. 2. 配置
    1. 2.1. 用户信息
    2. 2.2. 克隆协议
      1. 2.2.1. SSH
      2. 2.2.2. HTTPS
      3. 2.2.3. 协议切换
    3. 2.3. 自定义配置
      1. 2.3.1. 超时时间
      2. 2.3.2. 保存用户凭证
      3. 2.3.3. 多Git服务
      4. 2.3.4. 文本编辑器
      5. 2.3.5. 文本比较合并工具
      6. 2.3.6. 显示颜色
      7. 2.3.7. 操作别名
    4. 2.4. 查看所有配置
  3. 3. 基础操作
    1. 3.1. 工作流
    2. 3.2. 版本历史记录
    3. 3.3. 暂存
    4. 3.4. 撤销与回退
    5. 3.5. 使用帮助
  4. 4. 仓库管理
    1. 4.1. 推送本地修改到远程仓库
    2. 4.2. 添加本地仓库到远程
    3. 4.3. 获取远程仓库
    4. 4.4. 查看远程仓库
    5. 4.5. 关联远程仓库
    6. 4.6. 同步远程仓库的更新
    7. 4.7. 仓库引用(子仓库)
      1. 4.7.1. submodule
        1. 4.7.1.1. 添加子模块
        2. 4.7.1.2. 使用子模块
        3. 4.7.1.3. 更新子模块
        4. 4.7.1.4. 删除子模块
      2. 4.7.2. subtree
    8. 4.8. 清理仓库
      1. 4.8.1. 清理本地无效的远程追踪分支
      2. 4.8.2. 清理无用的分支和标签
      3. 4.8.3. 清理大文件
      4. 4.8.4. 清理大型二进制文件
  5. 5. 分支管理
    1. 5.1. 查看分支
    2. 5.2. 创建分支
    3. 5.3. 创建空白分支
    4. 5.4. 删除分支
    5. 5.5. 更新分支
  6. 6. 标签管理
    1. 6.1. 查看标签
    2. 6.2. 创建标签
    3. 6.3. 推送标签
    4. 6.4. 删除标签
  7. 7. 进阶技巧
    1. 7.1. 忽略特殊文件
      1. 7.1.1. 本地忽略
      2. 7.1.2. 全局忽略
    2. 7.2. 重写历史(慎用!)
      1. 7.2.1. 修改历史提交(变基)
      2. 7.2.2. 修改最近一次提交信息
      3. 7.2.3. 修改提交记录中的用户信息
  8. 8. Reference