Git 常用命令 FAQ

git图解

在开始前,让我们先来讲几个概念,这将更好的让我们理解接下来的内容:

工作区

我们正在编辑的内容就是在工作区,上图中的 workspace。

暂存区

我们将工作区的内容通过git add命令就提交到了暂存区。 上图中的 index

本地仓库

我们将暂存区的内容通过git commit就提交到了本地仓库。内容的修改从工作区 ==> 暂存区 ==> 本地仓库是顺序执行的。上图中的 repository

远程仓库

Git 是一个分布式管理工具,所以当我们将修改的内容提交到本地仓库后,就可以通过git push命令推送到远程仓库与他人协作完成项目。

分支

分支可以理解为像树枝一样来管理我们的代码,分支很好的解决了多人协作的问题。当我们需要开发一个新的功能时,从稳定分支(通常是 master 或者 release)新建分支开发,当开发完成并测试通过后,再通过git merge合并入稳定分支发布。

FAQ

Git 配置

我们可以通过git config命令来配置 Git,配置文件可以存放在以下三个不同的地方:

  • /etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 --system 选项,读写的就是这个文件。
  • ~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global 选项,读写的就是这个文件。
  • 当前仓库的 Git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前仓库有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。

在 Windows 系统上,Git 会找寻用户主目录下的 .gitconfig 文件。主目录即 $HOME 变量指定的目录,一般都是 C:\Documents and Settings\$USER。此外,Git 还会尝试找寻 /etc/gitconfig 文件,只不过看当初 Git 装在什么目录,就以此作为根目录来定位。

用户信息配置

1
2
$ git config [--global] user.name "John Doe"
$ git config [--global] user.email [email protected]

文本编辑器配置

1
$ git config --global core.editor emacs/vim

查看配置信息

1
2
3
4
5
6
7
$ git config --list
user.name=Scott Chacon
user.email[email protected]
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto

有时候会看到重复的变量名,那就说明它们来自不同的配置文件(比如 /etc/gitconfig 和~/.gitconfig),不过最终 Git 实际采用的是最后一个。

也可以通过git config user.name方式直接查阅某个环境变量的设定。

工作区

怎样将工作区中的内容提交到暂存区?

1
$ git add [./filename]

我在工作区修改了一些文件用于调试,完成后想丢弃所有修改

1
$ git checkout .

我想丢弃某个文件的修改

1
$ git checkout --[filename]

我想暂存这些调试的代码

1
$ git stash

我想看看有哪些暂存的内容

1
$ git stash list

我想将某个暂存的内容复制到工作区

1
$ git stash apply stash@{n}

我想将某个暂存的内容剪切到工作区

1
$ git stash pop stash@{n}

我想删除某个暂存

1
$ git stash drop stash@{n}

我想删除所有暂存内容

1
$ git stash clear

我想将 git 中的某个文件取消跟踪,并保留本地文件

1
$ git rm --cached [filename]

已跟踪的文件也要每次 add 后 commit 吗?

不需要,可以使用 git commit -am 'msg' 合并两个命令

我回退到了某个提交,如何再重新回到最新的提交?

git reflog命令记录了你的操作记录,在这里你可以找到最新的提交,使用git reset [commit-id]即可回到未来。

暂存区

我想将暂存区的文件还原到工作区

1
$ git reset ./filename

git reset中,有--mixed(默认), --soft--hard三个可选参数,它们间有什么区别呢?其实是他们的修改作用域不同,具体可看下表。需要注意的是,请谨慎使用--hard,这会使你工作区的内容被覆盖!

工作区 暂存区 本地仓库
--soft
--mixed
--hard

我想对比工作区域暂存区的某个文件差异

1
$ git diff [filename]

我想对比暂存区与仓库某个文件的差异

1
$ git diff --cached [filename]

我想比较工作区与本地仓库的差异

1
$ git diff HEAD

我想比较当前提价与某个历史提交的差异

1
$ git diff [commit-id]

本地仓库

怎么将远程仓库克隆到本地,并命名为proj?

1
$ git clone [repo_url] [proj]

怎么更新本地仓库?

1
$ git fetch && git merge
1
$ git pull

子模块功能怎么用?

添加子模块

1
$ git submodule add url repo_name

初始化子模块

1
$ git submodule init

更新子模块

1
$ git submodule update

标签

我想查看所有的标签

1
$ git tag

我想列出1.8.5系列的标签

1
$ git tag -l 'v1.8.5*'

我要查看某个标签信息

1
$ git show [tagname]

怎么创建一个标签?

1
$ git tag -a [tagname] -m "message"

该形式是附注标签,包含了创建标签者姓名、email、时间以及标签信息。另外有一种轻量标签git tag [tagname],该标签类型由于包含了较少的信息,所以不被推荐。

怎么推送标签到远程仓库?

默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。

推送单个标签:

1
$ git push origin [tagname]

将所有不在远程仓库的标签全部推送:

1
$ git push origin --tags

怎么删除标签?

1
$ git tag -d [tagname]

注意:该命令并不会从任何远程仓库中移除这个标签,你必须使用 git push [remote] :refs/tags/[tagname] 来更新你的远程仓库。

变基

变基的命令:git rebase -i [要修改 commit 的上一个 commit]

请注意!变基是一把利剑,合理的使用会让你的分支更加简洁和灵活,滥用则会导致分支的混乱。请谨慎使用 rebase,尤其是团队合作项目,因为它会修改历史信息,导致信息冲突
在危险操作前可以先在副本分支进行操作,确认结果是预期后再行操作。

变基的概念可能难以理解,你可以到这里学习一下。

当我们使用git rebase -i 5f2452b2 8f33126c调出了变基窗口时,会看到以下内容,请注意其中的 Commands 部分,每个命令后都有说明,我们只需要按照说明操作即可。如果不明白其实际动作和结果,请创建一个测试仓库演练一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pick cacc52da add: qrcode
pick f072ef48 update: indexeddb hack
pick 4e84901a feat: add indexedDB floder
pick 8f33126c feat: add test2.js

# Rebase 5f2452b2..8f33126c onto 5f2452b2 (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

我想修改某次提交信息

r

我想将多个提交合并

s

我想删除某次提交

d

我想将 A 分支变基到 B 分支上(移花接木)

1
$ git rebase -i A B

我想从每个commit移除一个文件

这经常发生。 有人粗心地通过 git add . 提交了一个巨大的二进制文件,你想要从所有地方删除它。 可能偶然地提交了一个包括一个密码的文件,然而你想要开源项目。 filter-branch 是一个可能会用来擦洗整个提交历史的工具。 为了从整个提交历史中移除一个叫做 passwords.txt 的文件,可以使用 --tree-filter 选项给 filter-branch

1
$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD

--tree-filter 选项在检出仓库的每一个提交后运行指定的命令然后重新提交结果。在本例中,你从每一个快照中移除了一个叫作 passwords.txt 的文件,无论它是否存在。通常一个好的想法是在一个测试分支中做这件事,然后当你决定最终结果是真正想要的,可以硬重置 master 分支。 为了让 filter-branch 在所有分支上运行,可以给命令传递 --all 选项。

我更换了邮箱,想修改历史提交的邮箱

1
2
3
4
5
6
7
8
9
$ git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
then
GIT_AUTHOR_NAME="Scott Chacon";
GIT_AUTHOR_EMAIL="[email protected]";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD

我想在所有分支删除某个文件,如 password.txt

1
git filter-branch --all --tree-filter 'rm -f passwords.txt' HEAD

变基冲突解决后我该做什么?

1
$ git rebase --continue

变基过程中我想放弃变基

1
$ git rebase --abort

分支

怎么切换分支?

1
$ git checkout [branch name]

怎么切换到上一个分支?

1
$ git checkout -

我当前在test分支,并想将ver1.0合并到test分支

1
$ git mrege ver1.0

我想取消合并

1
$ git merge --abort

怎么查看已经合并的分支?

1
$ git branch --merged

怎么查看还未合并的分支?

1
$ git branch --no-merged

我想新建一个分支

1
$ git branch [branch name]

我想新建并切换到新分支

1
$ git branch -b [branch name]

我想删除已合并的分支

1
$ git branch -d [branch name]

我想强制删除未合并的分支

1
$ git branch -D [brnach name]

我想将另一个分支的某次提交应用到当前分支

1
$ git cherry-pick [commit-id]

怎么修改分支名?

1
$ git branch -m [old name] [new name]

怎么将本地分支与远程分支建立跟踪关系?

1
$ git branch -t [branch name] [remote-branch]

怎么查看远程分支?

1
$ git branch -r

我本地没有别人新推送的远程分支

请使用git fetch命令更新一下

远程仓库名为origin,怎么将本地的 A 分支推送到远程并建立同名分支?

1
$ git push -u origin A:B

远程仓库名为origin,我想删除origin的分支B

1
$ git push -u origin :B

我将 5 个提交推送到了远程仓库,但发现第3个提交需要撤回,我该如何操作?

此时,你应该使用git revert [commit-id],无论这个提交在log的哪个位置,都不会影响上下的提交,而且它会将撤销的作为一个新的提交,因此revert命令主要用于推送到远程仓库的修改。

如果你使用了git reset会有什么不同呢?

如果一个提交在log的中间位置,那么reset这个提交之后的提交有可能都会消失掉!尤其在多人协作中,你将后两个提交删除,并推送到远程,而别人本地还是 5 个提交,这将导致分支的混乱,请清楚你的每一步操作与后果,谨慎使用每个命令。

远程仓库

怎么查看所有关联的远程仓库名称

1
$ git remote

我想查看远程仓库的地址

1
$ git remote -v

我想将本地已有仓库与远程仓库关联

1
$ git remote add 本地远程仓库名 url|path

我想取消远程仓库的关联

1
$ git remote rm 本地远程仓库名

我想重命名远程仓库

1
$ git remote rename [old name] [new name]

检索

我想在项目中查找某个关键词,并输出所在行号

1
$ git grep [-n] [keyword]

我想查看搜索结果所属方法或函数

1
$ git grep -p [keyword]

日志

git log中有很多参数能够帮助我们增强筛选,常用的选项如下表

选项 说明
-p 按补丁格式显示每个更新之间的差异。
--stat 显示每次更新的文件修改统计信息。
--shortstat 只显示 –stat 中最后的行数修改添加移除统计。
--name-only 仅在提交信息后显示已修改的文件清单。
--name-status 显示新增、修改、删除的文件清单。
--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。
--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)。
--graph 显示 ASCII 图形表示的分支合并历史。
--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。

git log --pretty=format 常用的选项如下表:

选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 –date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明

限制 git log 输出的选项

选项 说明
-(n) 仅显示最近的 n 条提交
--since, --after 仅显示指定时间之后的提交。
--until, --before 仅显示指定时间之前的提交。
--author 仅显示指定作者相关的提交。
--committer 仅显示指定提交者相关的提交。
--grep 仅显示含指定关键字的提交
-S 仅显示添加或移除了某个关键字的提交

我想查看某次提交信息

1
$ git show [commit-id]

我想查看某次提交修改的文件列表

1
$ git show --pretty="" --name-only [commit-id]

查看某次提交的变更内容

1
$ git show --pretty="" [commit-id]

我想查看某个文件的所有提交信息

1
$ git log -p [filename]

我想查看最近两次的提交信息

1
$ git log -p -2

我想查看某个文件的所有变动及统计信息

1
$ git whatchanged --stat [filename]

我想修改最近一次提交信息

1
$ git commit --amend

某行代码有问题,我想查看谁最后修改了这里

1
$ git blame [-L start-line, end-line] filename

我想查看某个用户在某个时间段内的所有提交,并仅输出简短hash与提交说明

1
$ git log --pretty="%h - %s" --author=Rock --since="2008-10-01" --before="2008-11-01"

我想用图形的方式查看分支的变化

1
$ git log --oneline --graph

.gitignore

  • **/*.go 忽略所有目录中的扩展名为go的文件
  • */*/temp*忽略二级目录下以temp开头的所有文件
  • temp?忽略以temp开头的文件名,如tempatempb
  • !README.md感叹号(!)表示不忽略该文件

版本控制最佳实践

鼓励频繁提交

频繁提交,而不要等到代码没有问题了再一次性提交。对于可能损坏主干原则的代码,不要直接提交到主干,而是创建一个分支,在分支中频繁提交。

定义主干原则,并且坚守它

“主干的代码必须是可以发布并且不会产生bug的”,如果不能保证新增或修改的代码符合这一原则,就在分支提交代码。

不要把逻辑的修改和代码格式化操作混在一起

如果你做了一些代码格式化的操作,就单独提交这次修改,然后再做一些逻辑的修改后提交,这样可以在出现问题时容易追溯。

不相干的代码分开提交

也就是说不要在一次提交里修复两个bug


相关阅读

  1. Git 常用命令总结
因为热爱,所以执着。