
在开始前,让我们先来讲几个概念,这将更好的让我们理解接下来的内容:
工作区
我们正在编辑的内容就是在工作区,上图中的 workspace。
暂存区
我们将工作区的内容通过git add命令就提交到了暂存区。 上图中的 index
本地仓库
我们将暂存区的内容通过git commit就提交到了本地仓库。内容的修改从工作区 ==> 暂存区 ==> 本地仓库是顺序执行的。上图中的 repository
远程仓库
Git 是一个分布式管理工具,所以当我们将修改的内容提交到本地仓库后,就可以通过git push命令推送到远程仓库与他人协作完成项目。
分支
分支可以理解为像树枝一样来管理我们的代码,分支很好的解决了多人协作的问题。当我们需要开发一个新的功能时,从稳定分支(通常是 master 或者 release)新建分支开发,当开发完成并测试通过后,再通过git merge合并入稳定分支发布。
FAQ
工作区
怎样将工作区中的内容提交到暂存区?
$ git add [./filename]
我在工作区修改了一些文件用于调试,完成后想丢弃所有修改
$ git checkout .
我想丢弃某个文件的修改
$ git checkout --[filename]
我想暂存这些调试的代码
$ git stash
我想看看有哪些暂存的内容
$ git stash list
我想将某个暂存的内容复制到工作区
$ git stash apply stash@{n}
我想将某个暂存的内容剪切到工作区
$ git stash pop stash@{n}
我想删除某个暂存
$ git stash drop stash@{n}
我想删除所有暂存内容
$ git stash clear
我想将 git 中的某个文件取消跟踪,并保留本地文件
$ git rm --cached [filename]
已跟踪的文件也要每次 add 后 commit 吗?
不需要,可以使用 git commit -am 'msg' 合并两个命令
我回退到了某个提交,如何再重新回到最新的提交?
git reflog命令记录了你的操作记录,在这里你可以找到最新的提交,使用git reset [commit-id]即可回到未来。
暂存区
我想将暂存区的文件还原到工作区
$ git reset ./filename
在
git reset中,有--mixed(默认),--soft,--hard三个可选参数,它们间有什么区别呢?其实是他们的修改作用域不同,具体可看下表。需要注意的是,请谨慎使用--hard,这会使你工作区的内容被覆盖!
| 工作区 | 暂存区 | 本地仓库 | |
|---|---|---|---|
--soft |
❌ | ❌ | ✅ |
--mixed |
❌ | ✅ | ✅ |
--hard |
✅ | ✅ | ✅ |
我想对比工作区域暂存区的某个文件差异
$ git diff [filename]
我想对比暂存区与仓库某个文件的差异
$ git diff --cached [filename]
我想比较工作区与本地仓库的差异
$ git diff HEAD
我想比较当前提价与某个历史提交的差异
$ git diff [commit-id]
本地仓库
怎么将远程仓库克隆到本地,并命名为proj?
$ git clone [repo_url] [proj]
怎么更新本地仓库?
$ git fetch && git merge
$ git pull
变基
变基的命令:git rebase -i [要修改 commit 的上一个 commit]
请注意!变基是一把利剑,合理的使用会让你的分支更加简洁和灵活,滥用则会导致分支的混乱。请谨慎使用 rebase,尤其是团队合作项目,因为它会修改历史信息,导致信息冲突 在危险操作前可以先在副本分支进行操作,确认结果是预期后再行操作。
变基的概念可能难以理解,你可以到这里学习一下。
当我们使用git rebase -i 5f2452b2 8f33126c调出了变基窗口时,会看到以下内容,请注意其中的 Commands 部分,每个命令后都有说明,我们只需要按照说明操作即可。如果不明白其实际动作和结果,请创建一个测试仓库演练一下。
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 分支上(移花接木)
$ git rebase -i A B
我更换了邮箱,想修改历史提交的邮箱
$ git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
then
GIT_AUTHOR_NAME="Scott Chacon";
GIT_AUTHOR_EMAIL="schacon@example.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD
我想在所有分支删除某个文件,如 password.txt
git filter-branch --all --tree-filter 'rm -f passwords.txt' HEAD
变基冲突解决后我该做什么?
$ git rebase --continue
变基过程中我想放弃变基
$ git rebase --abort
分支
怎么切换分支?
$ git checkout [branch name]
怎么切换到上一个分支?
$ git checkout -
我当前在test分支,并想将ver1.0合并到test分支
$ git mrege ver1.0
我想取消合并
$ git merge --abort
怎么查看已经合并的分支?
$ git branch --merged
怎么查看还未合并的分支?
$ git branch --no-merged
我想新建一个分支
$ git branch [branch name]
我想新建并切换到新分支
$ git branch -b [branch name]
我想删除已合并的分支
$ git branch -d [branch name]
我想强制删除未合并的分支
$ git branch -D [brnach name]
我想将另一个分支的某次提交应用到当前分支
$ git cherry-pick [commit-id]
怎么修改分支名?
$ git branch -m [old name] [new name]
怎么将本地分支与远程分支建立跟踪关系?
$ git branch -t [branch name] [remote-branch]
怎么查看远程分支?
$ git branch -r
我本地没有别人新推送的远程分支
请使用git fetch命令更新一下
远程仓库名为origin,怎么将本地的 A 分支推送到远程并建立同名分支?
$ git push -u origin A:B
远程仓库名为origin,我想删除origin的分支B
$ git push -u origin :B
我将 5 个提交推送到了远程仓库,但发现第3个提交需要撤回,我该如何操作?
此时,你应该使用git revert [commit-id],无论这个提交在log的哪个位置,都不会影响上下的提交,而且它会将撤销的作为一个新的提交,因此revert命令主要用于推送到远程仓库的修改。
如果你使用了git reset会有什么不同呢?
如果一个提交在log的中间位置,那么reset这个提交 之后的提交有可能都会消失掉!尤其在多人协作中,你将后两个提交删除,并推送到远程,而别人本地还是 5 个提交,这将导致分支的混乱,请清楚你的每一步操作与后果,谨慎使用每个命令。
远程仓库
怎么查看所有关联的远程仓库名称
$ git remote
我想查看远程仓库的地址
$ git remote -v
我想将本地已有仓库与远程仓库关联
$ git remote add 本地远程仓库名 url|path
我想取消远程仓库的关联
$ git remote rm 本地远程仓库名
我想重命名远程仓库
$ git remote rename [old name] [new name]
检索
我想在项目中查找某个关键词,并输出所在行号
$ git grep [-n] [keyword]
我想查看搜索结果所属方法或函数
$ 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 |
仅显示添加或移除了某个关键字的提交 |
我想查看某次提交信息
$ git show [commit-id]
我想查看某次提交修改的文件列表
$ git show --pretty="" --name-only [commit-id]
查看某次提交的变更内容
$ git show --pretty="" [commit-id]
我想查看某个文件的所有提交信息
$ git log -p [filename]
我想查看最近两次的提交信息
$ git log -p -2
我想查看某个文件的所有变动及统计信息
$ git whatchanged --stat [filename]
我想修改最近一次提交信息
$ git commit --amend
某行代码有问题,我想查看谁最后修改了这里
$ git blame [-L start-line, end-line] filename
我想查看某个用户在某个时间段内的所有提交,并仅输出简短hash与提交说明
$ git log --pretty="%h - %s" --author=Rock --since="2008-10-01" --before="2008-11-01"
我想用图形的方式查看分支的变化
$ git log --oneline --graph
.gitignore
**/*.go忽略所有目录中的扩展名为go的文件*/*/temp*忽略二级目录下以temp开头的所有文件temp?忽略以temp开头的文件名,如tempa和tempb!README.md感叹号(!)表示不忽略该文件
版本控制最佳实践
鼓励频繁提交
频繁提交,而不要等到代码没有问题了再一次性提交。对于可能损坏主干原则的代码,不要直接提交到主干,而是创建一个分支,在分支中频繁提交。
定义主干原则,并且坚守它
“主干的代码必须是可以发布并且不会产生bug的”,如果不能保证新增或修改的代码符合这一原则,就在分支提交代码。
不要把逻辑的修改和代码格式化操作混在一起
如果你做了一些代码格式化的操作,就单独提交这次修改,然后再做一些逻辑的修改后提交,这样可以在出现问题时容易追溯。
不相干的代码分开提交
也就是说不要在一次提交里修复两个bug
说明
- 本文受启发于项目git-flight-rules
- 推荐 Git 学习资料 Pro Git