Git 学习笔记:
学习站点:廖雪峰-Git教程
1.创建版本库
首先,选择一个合适的地方,创建一个空目录:
$ mkdir learngit
$ cd learngit
$ pwd
-第二步,通过 ‘git init’ 命令把这个目录变成 Git 可以管理的仓库:
$ git init
2.把文件修改添加到暂存区:
$ git add README.md
3.把暂存区的所有内容提交到当前分支:
$ git commit -m "wrote a README file"
-解释:git commit 命令后面的 -m 选项指示本次提交的说明。
4.查看工作区状态:
$ git status
5.查看 difference :
$ git diff README.md
6.查看提交历史:
$ git log
$ git log --pretty=oneline
7.回退到上一版本:
$ git reset --hard HEAD^
-在 Git 中,用 'HEAD' 表示当前版本(提交的ID),上一个版本就是 'HEAD^' ,上上一个版本就是 'HEAD^^' ,往上100个版本可以写成 'HEAD~100' 。
8.查看命令历史:
$ git reflog
9.历史版本穿梭:
$ git reset --hard commit_id
-穿梭前,用 'git log' 可以查看提交历史,以便确定要回退到哪个版本。
-要重返未来,用 'git reflog' 查看命令历史,以便确定要回到未来的哪个版本。
10.查看工作区和版本库里面最新版本的区别:
$ git diff HEAD -- README.md
11.直接丢弃工作区的修改(还未使用 ‘git add’ 命令):
$ git checkout -- README.md
12.撤销暂存区的修改(使用了 ‘git add’ 但是还未使用 ‘git commit’命令):
$ git reset HEAD file
-然后就可以使用 'git checkout -- README.md' 丢弃工作区修改。
-已经提交了不合适的修改到版本库时,想要撤销本次提交,参考'版本穿梭',不过前提是没有推送到远程库
13.从版本库中删除文件:
-本地删除文件
$ git rm README.md
$ git commit -m "remove README file"
-如果是本地误删了文件
$ git checkout -- README.md
-'git checkout' 命令其实是用版本库里的版本替换工作区的版本,无论工作区是删除或是修改,都可以“一键还原”
14.远程仓库(GitHub)
-第一步,创建SSH Key
$ ssh-Keygen -t rsa -C "youremail@example.com"
-然后一路回车使用默认值即可,key的密码可以设置为空
-完成后,可在用户主目录下找到 '.ssh' 目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。
-第二步,登录 GitHub,在账户设置中添加SSH Key,把id_rsa.pub文件的内容粘贴进去。
15.添加远程仓库:
-首先,登录GitHub,然后创建一个新的仓库,仓库名称填写 'learngit' (和本地库一致),其他默认。
-根据GitHub提示关联远程库
$ git remote add origin git@github.com:1065585676/learngit.git
-下一步,把本地库的所有内容推送到远程库
$ git push -u origin master
-由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
-完成后,只要本地做了修改,就可以通过下面命令把本地master分支最新修改推送至GitHub:
$ git push origin master
-分布式版本库完成。
16.从远程库克隆:
-首先,登陆GitHub,创建一个新的仓库,名字叫gitskills,勾选Initialize this repository with a README,这样GitHub会自动为我们创建一个README.md文件。
-克隆一个本地库:
$ git clone git@github.com:1065585676/gitskills.git
-Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。
17.创建与合并分支:
-创建 dev 分支:
$ git checkout -b dev
-'git checkout'命令加上-b参数表示创建并切换,相当于以下两条命令:
$ git branch dev
$ git checkout dev
-查看分支
$ git branch
-会列出所有分支,当前分支前面会标一个*号
-合并分支到当前分支:
$ git checkout master
$ git merge dev
-删除分支:
$ git branch -d dev
18.解决冲突
-当不同分支各自都分别有新的提交,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。
-'Automatic merge failed; fix conflicts and then commit the result.'
-这时候也可以使用 'git status' 查看冲突的文件是哪个,打开冲突的文件,会看到冲突的内容
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
-Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,需要自己手动编辑修改,修改后再提交即可。
$ git add README
$ git commit -m "conflict fixed"
-用带参数的git log也可以看到分支的合并情况:
$ git log --graph --pretty=oneline --abbrev-commit
-最后,删除feature1分支:
$ git branch -d feature1
19.分支管理策略:
-通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
-如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
$ git merge --no-ff -m "merge with no-ff" dev
-合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。
20.Bug分支:
-Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:
$ git stash
-现在,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。
-查看存储的工作现场:
$ git stash list
-工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
-方式一:用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除
-方式二:用git stash pop,恢复的同时把stash内容也删了
-可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:
$ git stash apply stash@{0}
21.功能分支:
-开发一个新feature,最好新建一个分支
-如果要丢弃一个没有被合并过的分支,可以通过git branch -D <name>强行删除。
22.多人协作:
-查看远程库信息:
$ git remote
$ git remote -v
-推送分支:
$ git push origin master
$ git push origin dev
-抓取分支:
$ git clone git@github.com:1065585676/gitskills.git
-从远程库clone时,默认情况下,只能看到本地的master分支。用git branch命令看看:
$ git branch
-现在,要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是用这个命令创建本地dev分支:
$ git checkout -b dev origin/dev
-现在,就可以在dev上继续修改,然后,时不时地把dev分支push到远程:
$ git commit -m "add /usr/bin/env"
$ git push origin dev
-当合作开发者已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:
$ git add hello.py
$ git commit -m "add coding: utf-8"
$ git push origin dev
-推送失败,因为你的合作开发者的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:
$ git pull
-git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:
$ git branch --set-upstream dev origin/dev
-再pull:
$ git pull
-这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push:
$ git commit -m "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'。
-这就是多人协作的工作模式,一旦熟悉了,就非常简单。
23.标签管理:
-发布一个版本时,我们通常先在版本库中打一个标签(tag)
-Git有commit,为什么还要引入tag?
-“请把上周一的那个版本打包发布,commit号是6a5819e...”
-“一串乱七八糟的数字不好找!”
-如果换一个办法:
-“请把上周一的那个版本打包发布,版本号是v1.2”
-“好的,按照tag v1.2查找commit就行!”
-所以,tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
-创建标签:
-首先,切换到需要打标签的分支上:
$ git branch
$ git checkout master
-然后,敲命令git tag <name>就可以打一个新标签:
$ git tag v1.0
-可以用命令git tag查看所有标签:
$ git tag
-默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?
-方法是找到历史提交的commit id,然后打上就可以了:
$ git log --pretty=oneline --abbrev-commit
-比方说要对add merge这次提交打标签,它对应的commit id是6224937,敲入命令:
$ git tag v0.9 6224937
-注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show <tagname>查看标签信息:
$ git show v0.9
-还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:
$ git tag -a v0.1 -m "version 0.1 released" 3628164
$ git show v0.1
-还可以通过-s用私钥签名一个标签:
$ git tag -s v0.2 -m "signed version 0.2 released" fec145a
-签名采用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
$ git show v0.2
-用PGP签名的标签是不可伪造的,因为可以验证PGP签名。
-操作标签:
-如果标签打错了,也可以删除:
$ git tag -d v0.1
-因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
-如果要推送某个标签到远程,使用命令git push origin <tagname>:
$ git push origin v1.0
-或者,一次性推送全部尚未推送到远程的本地标签:
$ git push origin --tags
-如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
$ git tag -d v0.9
-然后,从远程删除。删除命令也是push,但是格式如下:
$ git push origin :refs/tags/v0.9
-要看看是否真的从远程库删除了标签,可以登陆GitHub查看。
24.自定义Git
-有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等
-在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。
-不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。
-所有配置文件可以直接在线浏览:https://github.com/github/gitignore
-忽略文件的原则是:
-忽略操作系统自动生成的文件,比如缩略图等;
-忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
-忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
-检验.gitignore的标准是git status命令是不是说working directory clean。
-有些时候,你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore忽略了:
$ git add App.class
-如果你确实想添加该文件,可以用-f强制添加到Git:
$ git add -f App.class
-或者你发现,可能是.gitignore写得有问题,需要找出来到底哪个规则写错了,可以用git check-ignore命令检查:
$ git check-ignore -v App.class
25.配置别名:
-我们只需要敲一行命令,告诉Git,以后st就表示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 last,让其显示最后一次提交信息:
$ git config --global alias.last 'log -1'
-这样,用git last就能显示最近一次的提交:
$ git last
-甚至还有人丧心病狂地把lg配置成了:
$ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
$ git lg
-配置文件:
-配置Git的时候,加上--global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
-配置文件放哪了?每个仓库的Git配置文件都放在.git/config文件中:
$ cat .git/config
-别名就在[alias]后面,要删除别名,直接把对应的行删掉即可。
-而当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中:
$ cat .gitconfig
-配置别名也可以直接修改这个文件,如果改错了,可以删掉文件重新通过命令配置。
26.搭建Git服务器
-搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简单的apt命令就可以完成安装。
-第一步,安装git:
$ sudo apt-get install git
-第二步,创建一个git用户并且设定密码,用来运行git服务:
$ sudo adduser git
$ sudo passwd git
-第三步,创建证书登录:
-收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个,注意修改authorized_keys文件权限为git。
-第四步,初始化Git仓库:
-先选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令:
$ sudo git init --bare sample.git
-Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为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-shell-commands的问题
-将目录/usr/share/doc/git/contrib/git-shell-commands拷贝到git用户下/home/git/ 这里/usr/share/doc/git的git可能带版本号,要自行修改
$ sudo cp -R /usr/share/doc/git/contrib/git-shell-commands /home/git/
-递归修改目录所有者
$ sudo chown -R git:git git-shell-commands
-确保命令文件执行权限
$ sudo chmod +x /home/git/git-shell-commands/*
-第六步,克隆远程仓库:
-现在,可以通过git clone命令克隆远程仓库了,在各自的电脑上运行:
$ git clone git@server:/srv/sample.git
-管理公钥(Gitosis)
-如果团队很小,把每个人的公钥收集起来放到服务器的/home/git/.ssh/authorized_keys文件里就是可行的。如果团队有几百号人,就没法这么玩了,这时,可以用Gitosis来管理公钥。
-管理权限(Gitolite)
-有很多不但视源代码如生命,而且视员工为窃贼的公司,会在版本控制系统里设置一套完善的权限控制,每个人是否有读写权限会精确到每个分支甚至每个目录下。因为Git是为Linux源代码托管而开发的,所以Git也继承了开源社区的精神,不支持权限控制。不过,因为Git支持钩子(hook),所以,可以在服务器端编写一系列脚本来控制提交等操作,达到权限控制的目的。Gitolite就是这个工具。