Git 的总结 Blog 很多,一般的都参考或学习廖雪峰曾写的 Git 教程
和 经典的 Pro Git,小白在此整理了 Git 的常用命令,是方便自己的复习和查阅,也希望能方便和我一样有此需求的人,O(∩_∩)O~
git status
命令可以让我们时刻掌握仓库当前的状态。
要随时掌握工作区的状态,使用 git status
命令。
如果 git status
告诉你有文件被修改过,用 git diff
可以查看修改内容。
版本回退
git log
命令显示从最近到最远的提交日志。
在 Git 中,用 HEAD 表示当前版本,上一个版本就是 HEAD^,上上一个版本就是 HEAD^^,当然往上 100 个版本写 100个 ^ 比较容易数不过来,所以写成 HEAD~100。
现在,我们要把当前版本 “append GPL” 回退到上一个版本 “add distributed”,就可以使用 git reset
命令:
1 | $ git reset --hard HEAD^ |
重新恢复到新版本
只要右侧环境还在,就可以找到那个 append GPL 的 commit id 是 3628164…,于是就可以指定回到未来的某个版本:
1 | $ git reset --hard 3628164 |
版本号没必要写全,前几位就可以了,Git 会自动去找
- HEAD 指向的版本就是当前版本,因此,Git 允许我们在版本的历史之间穿梭,使用命令 git reset –hard commit_id。
- 穿梭前,用 git log 可以查看提交历史,以便确定要回退到哪个版本。
- 要重返未来,用 git reflog 查看命令历史,以便确定要回到未来的哪个版本。
撤销修改
1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令 git checkout -- file
。
2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步:
第一步用命令 git reset HEAD file
,就回到了 1;
第二步按 1 操作。
3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
删除文件
一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用 rm 命令删了:
$ rm test.txt
这个时候,Git 知道你删除了文件,因此,工作区和版本库就不一致了,git status
命令会立刻告诉你哪些文件被删除了。
现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令 git rm
删掉,并且 git commit
:
1 | $ git rm test.txt |
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git checkout -- test.txt
git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
命令 git rm
用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
远程仓库
关联远程仓库
$ git remote add origin git@github.com:onlyone/learngit.git
注意:要关联一个远程库,使用命令 git remote add origin git@server-name:path/repo-name.git。
本地内容推送到远程库
本地库的所有内容推送到远程库上:
$ git push -u origin master
加上了 -u 参数,Git 不但会把本地的 master 分支内容推送的远程新的 master 分支,还会把本地的 master 分支和远程的 master 分支关联起来,在以后的推送或者拉取时就可以简化命令
要关联一个远程库,使用命令 git remote add origin git@server-name:path/repo-name.git;
关联后,使用命令 git push -u origin master
第一次推送 master 分支的所有内容;此后,每次本地提交后,只要有必要,就可以使用命令 git push origin master
推送最新修改;
Git 支持多种协议,包括 https,但通过 ssh 支持的原生 git 协议速度最快
git 分支管理
首先,我们创建 dev 分支,然后切换到 dev 分支:
1 | $ git checkout -b dev |
git checkout
命令加上 -b 参数表示创建并切换,相当于以下两条命令:
1 | $ git branch dev |
然后,用 git branch
命令查看当前分支:
1 | $ git branch |
git merge
命令用于合并指定分支到当前分支
前面所讲知识:汇总下这些使用命令:
查看分支:git branch
创建分支:git branch
切换分支:git checkout
创建+切换分支:git checkout -b
合并某分支到当前分支:git merge
删除分支:git branch -d
冲突
在上节中 master 分支和 feature1 分支各自都分别有新的提交,变成了这样:
这种情况下,Git 无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,执行 git merge feature1,在看
readme.txt:
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
我们把冲突的内容修改为 Creating a new branch is quick and simple.
,提交:
1 | $ git add readme.txt |
现在,master 分支和 feature1 分支变成了下图所示:
用带参数的 git log 也可以看到分支的合并情况:
$ git log --graph --pretty=oneline --abbrev-commit
最后,删除 feature1 分支:
1 | $ git branch -d feature1 |
冲突解决,最后,删除 feature1 分支 git branch -d feature1
。当 Git 无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
用 git log --graph
命令可以看到分支合并图。
Bug 分支
如果你有一个 bug 任务,你想创建一个分支 issue-101 来修复它,但是你当前正在 dev 上进行的工作还没有完成而不能提交,bug 需要现在修复,所以现在你需要暂停 dev 上工作,Git 提供了一个 stash 功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:$ git stash
。
假定需要在 master 分支上修复,就从 master 创建临时分支:
$ git checkout master
Switched to branch 'master'
$ git checkout -b issue-101
Switched to a new branch 'issue-101'
现在修复 bug,需要把 “Git is free software …” 改为 “Git is a free software …”,然后提交:
$ git add readme.txt
$ git commit -m "fix bug 101"
修复完成后,切换到 master 分支,并完成合并,最后删除 issue-101 分支:
$ git checkout master
...
$ git merge --no-ff -m "merged bug fix 101" issue-101
...
$ git branch -d issue-101
Deleted branch issue-101 (...).
Git 把 stash 内容存在某个地方了,但是需要恢复一下,有两个办法:
- 一是用
git stash apply
恢复,但是恢复后,stash 内容并不删除,你需要用git stash drop
来删除; - 另一种方式是用
git stash pop
,恢复的同时把 stash 内容也删了:
Feature 分支
在软件开发中,总会添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以每添加一个新功能,最好新建一个 feature 分支,在上面开发,完成后,合并,最后,删除该 feature 分支。
现在新功能开发代号为 Vulcan:
$ git checkout -b feature-vulcan
Switched to a new branch 'feature-vulcan'
开发完毕,添加并提交:
$ git add vulcan.py
$ git commit -m "add feature vulcan"
切回 dev,准备合并:
一切顺利的话,feature 分支和 bug 分支是类似的,合并,然后删除。
由于种种原因,此功能又不需要了,现在这个分支需要就地销毁:
$ git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-vulcan'.
销毁失败。Git 友情提醒,feature-vulcan 分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用命令 git branch -D feature-vulcan
。
现在我们强行删除:
$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 756d4af).
注意:开发一个新 feature,最好新建一个分支;如果要丢弃一个没有被合并过的分支,可以通过 git branch -D <name>
强行删除。
推送分支
当你从远程仓库克隆时,实际上 Git 自动把本地的 master 分支和远程的 master 分支对应起来了,并且远程仓库的默认名称是 origin。
要查看远程库的信息,用 git remote
$ git remote
origin
或者,用 git remote -v 显示更详细的信息:
$ git remote -v
推送分支
推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git 就会把该分支推送到远程库对应的远程分支上:
git push origin master
如果要推送其他分支,比如dev,就改成
git push origin dev
- master 分支是主分支,因此要时刻与远程同步;
- dev 分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
- bug 分支只用于在本地修复 bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个 bug;
- feature 分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
多人协作
多人协作时,大家都会往 master 和 dev 分支上推送各自的修改。当你的同事也克隆一份此项目从远程库,默认情况下,只能看到本地的 master 分支。现在,你的同事要在 dev 分支上开发,就必须创建远程 origin 的 dev 分支到本地,于是他用这个命令创建本地 dev 分支:
$ git checkout -b dev origin/dev
现在,他就可以在dev上继续修改,然后,时不时地把 dev 分支 push 到远程,并且已经向 origin/dev 分支推送了他的提交,这时你也对同样的文件作了修改,并试图推送,推送失败,因为你的同事的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git 已经提示我们,先用 git pull 把最新的提交从 origin/dev 抓下来,然后,在本地合并,解决冲突,再推送,git pull 也失败了,原因是没有指定本地 dev 分支与远程 origin/dev 分支的链接,根据提示,设置 dev 和 origin/dev 的链接。
这回 git pull 成功,但是是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再 push:
$ git commit -m "merge & fix hello.py"
[dev adca45d] merge & fix hello.py
$ git push origin dev
因此,多人协作的工作模式通常是这样:
- 首先,可以试图用
git push origin branch-name
推送自己的修改; - 如果推送失败,则因为远程分支比你的本地更新,需要先用 git pull 试图合并;
- 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用 git push origin branch-name 推送就能成功!
如果 git pull 提示 “no tracking information”
,则说明本地分支和远程分支的链接关系没有创建,用命令 git branch --set-upstream branch-name origin/branch-name
。
标签
在 Git 中打标签非常简单,首先,切换到需要打标签的分支上:
$ git branch
* dev
master
$ git checkout master
Switched to branch 'master'
然后,敲命令 git tag <name>
就可以打一个新标签:
$ git tag v1.0
默认标签是打在最新提交的 commit 上的。还可以对历史提交打上标签,只要找到历史提交的 commit id,然后打上就可以了,例如要对 add merge 这次提交打标签,它对应的 commit id 是 6224937,输入命令:
$ git tag v0.9 6224937
还可以创建带有说明的标签,用 -a 指定标签名,-m 指定说明文字:
$ git tag -a v0.1 -m "version 0.1 released" 3628164
用命令 git show 可以看到说明文字:
$ git show v0.1
tag v0.1
Tagger: hubwiz <hubwiz@163.com>
Date: Mon Aug 26 07:28:11 2015 +0800
version 0.1 released
</hubwiz@163.com>
签名采用 PGP 签名,因此,必须首先安装 gpg(GnuPG),如果没有找到 gpg,或者没有 gpg 密钥对,就会报错:
gpg: signing failed: secret key not available
error: gpg failed to sign the data
error: unable to sign the tag
如果报错,请参考 GnuPG 帮助文档配置 Key。
- 命令
git tag <name>
用于新建一个标签,默认为 HEAD,也可以指定一个 commit id; git tag -a <tagname> -m "blablabla..."
可以指定标签信息;git tag -s <tagname> -m "blablabla..."
可以用 PGP 签名标签;
命令git tag
可以查看所有标签。
如果标签打错了,也可以删除:
$ git tag -d v0.1
Deleted tag 'v0.1' (was e078af9)
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
如果要推送某个标签到远程,使用命令 it push origin <tagname>:
$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
* [new tag] v1.0 -> v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
$ git push origin --tags
Counting objects: 1, done.
Writing objects: 100% (1/1), 554 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
* [new tag] v0.2 -> v0.2
* [new tag] v0.9 -> v0.9
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
$ git tag -d v0.9
Deleted tag 'v0.9' (was 6224937)
然后,从远程删除。删除命令也是 push,但是格式如下:
$ git push origin :refs/tags/v0.9
To git@github.com:michaelliao/learngit.git
- [deleted] v0.9
要看看是否真的从远程库删除了标签,可以登陆 GitHub 查看。
- git push origin
可以推送一个本地标签; - git push origin –tags 可以推送全部未推送过的本地标签;
- git tag -d
可以删除一个本地标签; - git push origin :refs/tags/
可以删除一个远程标签。
GitHub 不仅是免费的远程仓库,个人的开源项目,可以放到 GitHub 上,而且 GitHub 还是一个开源协作社区,通过 GitHub,既可以让别人参与你的开源项目,也可以参与别人的开源项目。
在 GitHub 上,利用 Git 极其强大的克隆和分支功能,人们可以自由参与各种开源项目。比如人气极高的 bootstrap 项目,这是一个非常强大的 CSS 框架,在它的项目主页,点 “Fork” 就在自己的账号下克隆了一个 bootstrap 仓库,然后,从自己的账号下 clone。一定要从自己的账号下 clone 仓库,这样你才能推送修改。如果从 bootstrap 的作者的仓库地址 git@github.com:twbs/bootstrap.git 克隆,因为没有权限,你将不能推送修改。
Bootstrap 的官方仓库 twbs/bootstrap、你在 GitHub 上克隆的仓库 my/bootstrap,以及你自己克隆到本地电脑的仓库,他们的关系就像下图显示的那样:
如果你希望 bootstrap 的官方库能接受你的修改,你就可以在 GitHub 上发起一个 pull request。
- 在 GitHub 上,可以任意 Fork 开源仓库;
- 自己拥有 Fork 后的仓库的读写权限;
- 可以推送 pull request 给官方仓库来贡献代码。
自定义 Git
实际上,Git 还有很多可配置项。
比如,让 Git 显示颜色,会让命令输出看起来更醒目:
$ git config --global color.ui true
这样,Git会适当地显示不同的颜色
忽略特殊文件
有些时候我们需要把一些文件例如:保存了数据库密码的配置文件放在 Git 目录下,但又不提交,那么需要我们在 Git 工作区的根目录下创建一个特殊的 .gitignore 文件,然后把要忽略的文件名填进去,Git 就会自动忽略这些文件。
不需要从头写 .gitignore 文件,GitHub 已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore
忽略文件的原则是:
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的 .class 文件;
- 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
配置别名
配置别名其实就是把命令重新设置简单些,方便输入,例如:如果输入 git st 就表示 git status。
$ git config --global alias.st status
现在都用 co 表示 checkout,ci 表示 commit,br 表示 branch:
$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.br branch
–global 参数是全局参数,也就是这些命令在这台电脑的所有 Git 仓库下都有用。
配置文件
配置 Git 的时候,加上 –global 是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
配置文件放哪了?每个仓库的 Git 配置文件都放在 .git/config 文件中:
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:michaelliao/learngit.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[alias]
last = log -1
别名就在 [alias] 后面,要删除别名,直接把对应的行删掉即可。
而当前用户的 Git 配置文件放在用户主目录下的一个隐藏文件 .gitconfig 中:
$ cat .gitconfig
[alias]
co = checkout
ci = commit
br = branch
st = status
[user]
name = Your Name
email = your@email.com
配置别名也可以直接修改这个文件,如果改错了,可以删掉文件重新通过命令配置。
搭建 Git 服务器
搭建 Git 服务器需要准备一台运行 Linux 的机器,强烈推荐用 Ubuntu 或 Debian,这样,通过几条简单的 apt 命令就可以完成安装。
假设你已经有 sudo 权限的用户账号,下面,正式开始安装。
第一步,安装 git:
$ sudo apt-get install git
第二步,创建一个 git 用户,用来运行 git 服务:
$ sudo adduser git
第三步,创建证书登录:
收集所有需要登录的用户的公钥,就是他们自己的 id_rsa.pub
文件,把所有公钥导入到 /home/git/.ssh/authorized_keys
文件里,一行一个。
第四步,初始化 Git 仓库:
先选定一个目录作为 Git 仓库,假定是 /srv/sample.git,在 /srv 目录下输入命令:
$ sudo git init --bare sample.git
Git 就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的 Git 仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的 Git 仓库通常都以 .git 结尾。然后,把 owne r改为 git:
$ sudo chown -R git:git sample.git
第五步,禁用 shell 登录:
出于安全考虑,第二步创建的 git 用户不允许登录 shell,这可以通过编辑 /etc/passwd 文件完成。找到类似下面的一行:
git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
这样,git用户可以正常通过 ssh 使用 git,但无法登录 shell,因为我们为 git 用户指定的 git-shell 每次一登录就自动退出。
第六步,克隆远程仓库:
现在,可以通过 git clone 命令克隆远程仓库了,在各自的电脑上运行:
$ git clone git@server:/srv/sample.git
Cloning into 'sample'...
warning: You appear to have cloned an empty repository.
管理公钥和管理权限
管理公钥
如果团队很小,把每个人的公钥收集起来放到服务器的 /home/git/.ssh/authorized_keys
文件里就是可行的。如果团队有几百号人,就没法这么玩了,这时,可以用 Gitosis 来管理公钥。
管理权限
有很多不但视源代码如生命,而且视员工为窃贼的公司,会在版本控制系统里设置一套完善的权限控制,每个人是否有读写权限会精确到每个分支甚至每个目录下。因为 Git 是为 Linux 源代码托管而开发的,所以 Git 也继承了开源社区的精神,不支持权限控制。不过,因为 Git 支持钩子(hook),所以,可以在服务器端编写一系列脚本来控制提交等操作,达到权限控制的目的。 Gitolite 就是这个工具。
这里我们也不介绍 Gitolite 了,不要把有限的生命浪费到权限斗争中。
主要两点:
- 要方便管理公钥,用 Gitosis;
- 要像SVN那样变态地控制权限,用 Gitolite。