Git 使用指南
git init
初始化
git init
git clone
克隆
git clone [远端仓库] [目标文件夹名称(默认:远端仓库的名字)]
git log
查看历史提交
- 当前分支
- 按时间先后顺序显示到
[校验和]
为止
git log [校验和(默认:最新提交的校验和)]
选项:
--oneline
:精简至单行版本--stat
:增加文件修改信息-p
:忽略空格引起的不同-w
:忽略空格引起的不同--all --graph
:用分支图显示所有历史提交--author="[作者]"
:显示作者包含[文本]
的历史提交--grep="[文本]"
:显示内容包含[文本]
的历史提交
希望选项--all --graph
成为命令git log
的默认参数?
在文件.bashrc
中加入:
git() { if [ "$1" = "log" ] then command git log --all --graph "${@:2}"; else command git "$@"; fi; }
Enabling git log parameters by default
git shortlog
查看历史提交:按作者分类显示
git shortlog
git show
查看[校验和]
提交的所有信息
git show [校验和(默认:最新提交的校验和)]
查看附注标签(Annotated Tag)的内容
git tag [标签名]
选项同命令git log
。
git status
查看当前状态
git status
git diff
查看尚未加入暂存区的改动
git diff
git stash
Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is thegit stash
command.经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作。问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。解决这个问题的办法就是
git stash
命令。
保存工作区的更改到暂存,恢复工作区至上次提交。
git stash
查看所有暂存
git stash list
恢复暂存到工作区
git stash apply [暂存记录(默认:最新暂存记录)]
恢复暂存到工作区,同时删除暂存记录。
git stash pop [暂存记录(默认:最新暂存记录)]
git add
将文件从工作区添加到暂存区
git add [文件名1 文件名2 文件名3 ...]
将所有文件(文件.gitignore
指示的除外)从工作区添加到暂存区
git add .
git rm
将文件从暂存区移回到工作区
git rm --cached [文件名1 文件名2 文件名3 ...]
git commit
将暂存区提交到本地仓库
- 会进入一个 vim 窗口,在此输入提交信息。
- 所有以
#
开头的行都会被忽略。 - 对于空行的问题无需纠结,因为 git 会忽略所有不必要的空行。
git commit
键入提交信息并提交
git commit -m [提交信息]
更新最近的提交(内容和信息)
git commit --amend
如修改了提交内容,建议先执行:
git add .
提交的本质是区块链的节点,每个节点存储了上一个提交(父提交)的校验和。父提交可能是1个(通常),也可能是2个(分支的合并提交)。
提交信息的书写规范(Udacity)
The goal is that each commit has a single focus. Each commit should record a single-unit change. Now this can be a bit subjective (which is totally fine), but each commit should make a change to just one aspect of the project.
Conversely, a commit shouldn't include unrelated changes - changes to the sidebar and rewording content in the footer. These two aren't related to each other and shouldn't be included in the same commit. Work on one change first, commit that, and then change the second one. That way, if it turns out that one change had a bug and you have to undo it, you don't have to undo the other change too.
The best way that I've found to think about what should be in a commit is to think, "What if all changes introduced in this commit were erased?". If a commit were erased, it should only remove one thing.
DO:
- do keep the message short (less than 60-ish characters)
- do explain what the commit does (not how or why!)
DO NOT:
- do not explain why the changes are made (more on this below)
- do not explain how the changes are made (that's what
git log -p
is for!)do not use the word "and"
- if you have to use "and", your commit message is probably doing too many changes - break the changes into separate commits
- e.g. "make the background color pink and increase the size of the sidebar"
完整版文档:Udacity Git Commit Message Style Guide
git tag
查看所有标签
git tag
添加轻便标签(Lightweight Tag)
git tag [标签名] [校验和(默认:最新提交的校验和)]
添加附注标签(Annotated Tag)
- 会进入一个 vim 窗口,在此输入标签内容。
- 所有以
#
开头的行都会被忽略。对于空行的问题无需纠结, - 因为 git 会忽略所有不必要的空行。
git tag -a [标签名] [校验和(默认:最新提交的校验和)]
键入附注标签的内容并提交
git tag -a [标签名] [校验和(默认:最新提交的校验和)] -m [附注标签的内容]
删除本地标签
git tag -d [标签名1 标签名2 标签名3 ...]
删除指定远端标签
git push [远端名] -d refs/tags/[标签名] git push [远端名] :refs/tags/[标签名]
推送所有本地标签到远端
git push [远端名] --tag
从远端拉取所有标签到本地
git fetch [远端名] --tag
在远端唯一的情况下,上述[远端名]
可以省略。
标签的本质是引用(ref),指向一个提交或一个分支。
git checkout
切换
git checkout [校验和 / 标签名 / 分支名]
创建新分支并切换
git checkout -b [分支名]
恢复工作区中的某个文件到上次提交的状态
git checkout -- [文件名]
恢复工作区中的全部文件到上次提交的状态
git checkout -- .
git branch
查看所有本地分支
git branch
查看所有远端分支
git branch -r
新建分支
git branch [分支名] [校验和(默认:最新提交的校验和)]
删除分支
git branch -d [分支名1 分支名2 分支名3 ...]
删除分支的前提条件是:
- 该分支不是当前分支。
- 该分支上的最新提交已经合并到另一分支上(如需忽略本条件,请使用
-D
选项。警告:尚未合并的所有提交将会全部丢失!)。
分支的本质是引用,指向一个提交。Git 将分支理解为从首个提交到指向提交的序列。
git merge
将[源分支]
合并到当前分支,通常有两种情况:
[源分支]
是当前分支的更新:直接移动当前分支的 ref。- 创建
[源分支]
后,又当前分支上新建了提交:创建一个合并提交,将[源分支]
和当前分支的 ref 指向这个合并提交。
git merge [源分支]
合并操作操作会因为冲突(conflict)而中止,条件是:参与合并的两个分支上,各存在一个提交,它们修改了同一个行范围。
如需撤销合并操作,请使用命令git merge --abort
。
冲突的处理方法(Udacity)
Merge Conflict Output Explained
The output that shows in the Terminal is:
$ git merge heading-update Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result.Notice that right after the
git merge heading-update
command, it tries merging the file that was changed on both branches (index.html
), but that there was a conflict. Also, notice that it tells you what happened - "Automatic merge failed; fix conflicts and then commit the result".Remember our good friend
git status
? Well he'll come in really handy when working with merge conflicts.$ git status On branch master You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Unmerged paths: (use "git add <file> ..." to mark resolution) both modified: index.htmlThe git status output tells us to that the merge conflict is inside index.html. So check out that file in your code editor!
Merge Conflict Indicators Explanation
<<<<<<< HEAD
everything below this line (until the next indicator) shows you what's on the current branch||||||| merged common ancestors
everything below this line (until the next indicator) shows you what the original lines were> > > > > > > heading-update
is the ending indicator of what's on the branch that's being merged in (in this case, theheading-update
branch)Resolving A Merge Conflict
Git is using the merge conflict indicators to show you what lines caused the merge conflict on the two different branches as well as what the original line used to have. So to resolve a merge conflict, you need to:
- choose which line(s) to keep
- remove all lines with indicators
For some reason, I'm not happy with the word "Crusade" right now, but "Quest" isn't all that exciting either. How about "Adventurous Quest" as a heading?!?
Commit Merge Conflict
Once you've removed all lines with merge conflict indicators and have selected what heading you want to use, just save the file, add it to the staging index, and commit it! Just like with a regular merge, this will pop open your code editor for you to supply a commit message. Just like before, it's common to use the provided merge commit message, so after the editor opens, just close it to use the provided commit message.
And that's it! Merge conflicts really aren't all that challenging once you understand what the merge conflict indicators are showing you.
If a merge conflict occurs in a file and you edit the file, save it, stage it, and commit it but forget to remove the merge conflict indicators, Git will commit the lines with the merge conflict indicators, because they're just regular characters.
git revert
git revert [校验和]
设[校验和]
提交的前一个提交为PREVIOUS
,修改的行范围是range
。
该命令的作用是回滚range
范围内的修改,将会产生一个新的提交。
回滚操作操作会因为冲突而中止,条件是:如果range
范围内的某一部分在[校验和]
提交的某个后续提交中被修改。
如需撤销回归操作,请使用命令git revert --abort
。
冲突的处理方法与命令git merge
相同。
git reset
git reset [校验和]
该命令的作用是回到[校验和]
提交的状态,即撤销[校验和]
提交的后续提交的所有修改modification。
选项:
--mixed
:将modification移入工作区(默认)--soft
:将modification移入暂存区--hard
:删除modification
执行命令git reset
之后,[校验和]
提交的后续提交未被删除。
可以通过下列命令查看这些不属于任何分支的提交:
git relog
git fsck --unreachable --no-reflog
(具体使用方法详见后文。)
可以通过切换到提交、新建分支、合并到现有分支的方法恢复这些提交。
相对引用(Udacity)
Relative Commit References
You already know that you can reference commits by their SHA, by tags, branches, and the special
HEAD
pointer. Sometimes that's not enough, though. There will be times when you'll want to reference a commit relative to another commit. For example, there will be times where you'll want to tell Git about the commit that's one before the current commit...or two before the current commit. There are special characters called "Ancestry References" that we can use to tell Git about these relative references. Those characters are:
^
– indicates the parent commit~
– indicates the first parent commitHere's how we can refer to previous commits:
the parent commit – the following indicate the parent commit of the current commit
HEAD^
HEAD~
HEAD~1
the grandparent commit – the following indicate the grandparent commit of the current commit
HEAD^^
HEAD~2
the great-grandparent commit – the following indicate the great-grandparent commit of the current commit
HEAD^^^
HEAD~3
The main difference between the
^
and the~
is when a commit is created from a merge. A merge commit has two parents. With a merge commit, the^
reference is used to indicate the first parent of the commit while^2
indicates the second parent.
- The first parent is the branch you were on when you ran git merge
- The second parent is the branch that was merged in.
git remote
查看所有远端仓库的 URL
git remote -v
关联到远端仓库:[远端仓库的别名]
通常被设为origin
git remote add [远端仓库的别名] [远端仓库的URL]
取消与远端仓库的关联
git remote remove [远端仓库的别名]
重命名远端仓库
git remote rename [旧名称] [新名称]
git push
将本地分支推送到远端
git push [远端名] [本地分支] [远端分支]
省略[远端分支]
的情形:将[本地分支]
推送到同名的远程分支,如果该远程分支不存在,则会被新建。
git push [远端名] [本地分支]
可以为该命令添加-u
参数,表示在推送的同时,设定远端名
中的远端分支
为[本地分支]
的 upstream branch。同样,同名的远端分支可以省略。
git push -u [远端名] [本地分支] [远端分支] git push -u [远端名] [本地分支]
在执行上述命令后,就可以切换到相应本地分支(命令git checkout
)并直接使用命令git push
了。
git push
如果出现各种原因导致命令git push
不能成功执行(例如:删除并重建本地分支、修改本地分支上的历史提交),可以使用选项-f
进行覆盖(强行)推送。不要在多人合作的远端分支中使用!!!原因参见:
- 团队开发里频繁使用 git rebase 来保持树的整洁好吗?
- git 如何撤销一次remote的master commit?
git push -f
删除远端分支
git push [远端名] -d [远端分支] git push [远端名] :[远端分支]
git pull
将远端分支拉取到本地:取回远程主机某个分支的更新,再与本地的指定分支合并。
git pull [远端名] [远端分支]:[本地分支]
在满足下列条件的前提下,可以直接使用命令git pull
:
- 已使用命令
git checkout
将该对应的本地分支切换为当前分支。 该分支的 upstream branch 已设置。有两种方法:
- 执行命令
git branch --set-upstream-to=[远端名]/[远端分支] [本地分支]
。 - 借助带选项
-u
的命令git push
。
- 执行命令
git pull
git fetch
将远端分支拉取到本地:取回远程主机某个分支的更新,但是不与本地分支合并。
git fetch [远端名] [远端分支]:[本地分支]
在满足下列条件的前提下,可以直接使用命令git fetch
:
- 已使用
git checkout
将该对应的本地分支切换为当前分支。 该分支的 upstream branch 已设置。有两种方法:
- 命令
git branch --set-upstream-to=[远端名]/[远端分支] [本地分支]
- 借助
git push
命令的选项-u
- 命令
git fetch
区别于命令git pull
:
- 命令
git fetch
让用户决定怎样合并远端分支和本地分支。 - 命令
git pull
自动进行分支合并,在某些情况下可能无法成功执行。
problem
本地分支和远端分支被同时更新。
更新前:
master | remote a -- 3 -- d -- f -- e -- 7 ============================================================ origin/master | local a -- 3 -- d -- f -- e -- 7 | master
更新后:
master | remote a -- 3 -- d -- f -- e -- 7 -- 8 ============================================================ origin/master | local a -- 3 -- d -- f -- e -- 7 -- b | master
Solution
首先先使用命令git fetch
,获取远端分支更新。
master | remote a -- 3 -- d -- f -- e -- 7 -- 8 ============================================================ origin/master | 8 / local a -- 3 -- d -- f -- e -- 7 -- b | master
再使用命令git merge origin/master
,将远端分支合并到本地分支。
master | remote a -- 3 -- d -- f -- e -- 7 -- 8 ============================================================ origin/master | 8 / \ local a -- 3 -- d -- f -- e -- 7 -- b -- 4 | master
最后使用命令git push
,推送本地分支到远端分支。
master | 8 | / \ | remote a -- 3 -- d -- f -- e -- 7 -- 8 -- 4 ============================================================ origin/master | 8 | / \ | local a -- 3 -- d -- f -- e -- 7 -- b -- 4 | master
git rebase
将当前分支的修改应用到目标分支
,步骤是:
- 找出同时位于两个分支且最新的提交最新提交(最新公共提交)。
- 将当前分支上最新公共提交后的所有提交,移动到
目标分支
后。 - 将当前分支指向最新提交。
git rebase [目标分支]
参考资料:
对[校验和]
后的提交进行交互式修改(vim)。
git rebase -i [校验和]
参考资料:
中止上述操作
git rebase --abort
Do not rebase commits that exist outside your repository.If you follow that guideline, you’ll be fine. If you don’t, people will hate you, and you’ll be scorned by friends and family.
不要对在你的仓库外有副本的分支执行变基。
如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。
其他
参考资料:https://www.worldhello.net/go...
操作记录
查看90天内的操作记录
git reflog
立即清除所有操作记录(不推荐)
git reflog expire --expire=now --all
查看无法显示的对象
查看无法显示(unreachable)的对象,包括但不限于:
- 暂存区操作时引入的临时对象(以
unreachable blob
开头) - 不在分支上的提交(以
unreachable commit
开头)
git fsck --unreachable --no-reflog
清除对象
清除对象,通常是暂存区操作时引入的临时对象。操作记录中的对象不在清理范围。
git prune
优化仓库
优化仓库
git gc