安装后初始化配置
配置全局的用户名(–global此电脑所有git仓库都会使用该配置)
$ git config --global user.name "Your Name"
配置全局的邮箱地址(–global此电脑所有git仓库都会使用该配置)
$ git config --global user.email "email@example.com"
查看配置信息
$ git config --system --list ## 查看系统配置
$ git config --global --list ## 查看当前用户配置
$ git config --local --list ## 查看当前仓库配置
创建版本库
创建一个空目录作为版本库目录
$ mkdir learngit # 创建目录learngit(即为版本库)
$ cd learngit # 进入该目录
$ pwd # 查看路径
/Users/michael/learngit
初始化该目录,使其可以被git管理,此后该目录会出现一个.git目录(如果没有可能被隐藏了,可以用:ls -ah查看)
$ git init # 初始化该目录之后会出现以下提示
Initialized empty Git repository in /Users/michael/learngit/.git/
$ ls -ah # 查看当前目录文件,包括隐藏文件
在目录下面新建一个文件,用于做测试,内容随便
$ ls
readme.php
$ cat readme.php
Git is a version control system.
Git is free software.
将文件放入到仓库只需要两步
1). 告诉Git将要将要添加该文件到仓库(add后面可以跟多个文件用空格隔开,也可以跟目录,并且可以将文件多次分别add,只要在commit之前都算一次提交)
$ git add readme.txt
2). 将文件提交到仓库
$ git commit -m "wrote a readme file"
[master (root-commit) 74256b9] wrote a readme file
2 files changed, 6 insertions(+)
create mode 100644 reademe.php
create mode 100644 readme.php
==注意:每次修改,如果不用git add到暂存区,那就不会加入到commit中==
对文件进行修改,添加一个单词,然后通过命令查看状态
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
告诉我们文件被修改过了,但是还没有准备提交的修改
查看具体修改了哪些内容
$ git diff readme.php # 会显示出修改的内容信息
版本回退
查看所有的版本日志
$ git log # 如果需要精简到一行的信息加上参数 --pretty=oneline 即可
在Git中,用HEAD表示当前版本,也就是最新的提交1094adb...
(注意我的提交ID和你的肯定不一样),
上一个版本就是HEAD^,
上上一个版本就是HEAD^^,
当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100
如下,回退到上一个版本,执行命令后用cat查看发现内容已经变了
$ git reset --hard HEAD^
如果我回到过去的版本了,随后又想恢复到原来的版本怎么办,只要命令框没有关闭,还可以找到原来的版本号,取前几位就可以,但是也不能太少,用以下命令
$ git reset --hard 3642b # 版本号写前几位就可以,但是也不能太短,能识别就行
但是如果手贱关闭了命令框,关闭了电脑,怎么办,回退版本后发现错了,还想回来怎么办
$ git reflog # 这个命令可以查看你所有操作命令的日志,当然里面是有版本号的了
工作区概念:就是你在本地电脑里能看到的目录
版本库概念:工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。
撤销更改
情况一:准备提交之前如果发现了错误(==add之前==)
- 一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
- 一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
- 总之,就是让这个文件回到最近一次git commit或git add时的状态。
$ git checkout -- readme.php
情况二:已经提交到暂存区了(==add之后commit之前==)
1). git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。
$ git reset HEAD readme.php
2). 此时就变为了情况一,暂存区没有了,但是工作区还有
$ git checkout -- readme.php
情况三:已经提交到了分支(==commit之后==)
可以采用版本回退的方法
情况四:不仅提交到了分支,还推送到了远程版本库
那么恭喜你,本次撤销修改是不可能的了
删除文件
当你从本地删除了一个文件,可以有两种选择:
1). 确定要删除了,然后将分支中的文件也删除掉
$ git rm test.php
$ git commit -m "remove test.php"
2). 发现删错了,想要将其从分支恢复回来
$ git checkout -- test.php
远程仓库操作
添加远程仓库
[1]. 第一步:==创建SSH Key==。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:
$ ssh-keygen -t rsa -C "youremail@example.com"
注意:需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,密码没必要设置,如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对, ==id_rsa是私钥==,不能泄露出去,==id_rsa.pub是公钥==,可以放心地告诉任何人
[2]. 第二步:登陆GitHub,打开“Account settings”,“SSH Keys”页面:
然后,点“==Add SSH Key==”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容
==为什么GitHub需要SSH Key呢?==
因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
最后友情提示,在GitHub上免费托管的Git仓库,任何人都可以看到喔(但只有你自己才能改)。所以,不要把敏感信息放进去。
[3]. ==在github上新建空的版本库==
$ git remote add origin git@github.com:KeepFang/learngit.git
注意:要将KeepFang换成自己的github用户名,learngit换成自己的远程库名字
添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
[4]. 将本地内容推送到远程库
$ git push -u origin master # 将本地库内容推送到远程库
把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送到远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
从现在起,只要本地作了提交,就可以通过命令:
$ git push origin master
==SSH警告==
当你第一次使用Git的clone或者push命令连接GitHub时,会得到一个警告
The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?
这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes回车即可。
Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了:
Warning: Permanently added 'github.com' (RSA) to the list of known hosts.
==克隆远程库文件到本地==
git clone git@github.com:用户名/版本库名.git
你也许还注意到,GitHub给出的地址不止一个,还可以用
https://github.com/michaellia…。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。
分支
分支在实际中怎么用呢?
团队合作开发一个项目,你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
1). 创建分支
$ git checkout -b dev # 创建分支dev并切换到该分支
等价于
$ git branch dev # 创建分支
$ git checkout dev # 切换分支
$ git branch # 列出所有分支,当前分支前面会标一个 * 号
2). 合并分支
$ git checkout master # 切换到主分支
$ git merge dev # 将dev分支与当前分支合并
$ git branch -d dev # 合并完成之后就可以删除dev分支了
$ git branch # 删除之后再次查看分支,发现只剩下master分支了
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
$ git merge --no-ff -m "merge with no-ff" dev # 禁用fast forward快速合并模式,
## 合并分支时,加上--no-ff参数就可以用普通模式合并,
## 合并后的历史有分支,能看出来曾经做过合并,
## 而fast forward合并就看不出来曾经做过合并。
$ git log --graph --pretty=oneline --abbrev-commit # 然后再查看分支历史
3). 解决冲突
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。
合并之后,删除没用的分支
$ git log --graph --pretty=oneline --abbrev-commit # graph参数是查看分支合并图
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
bug分支
当你正在开发一个新功能的时候,突然有人反馈原先的项目存在一个bug,很自然想到新建一个分支来进行修复然后合并,但是现在你的工作还没完成,又不能提交,这时候就需要用到 ==保存现场== 功能
$ git stash # 将当前工作的现场储存起来
$ git status # 这时候再看发现工作区是干净的可以放心创建分支
1). 确定在哪个分支修复bug,就从哪个分支创建临时分支,假设是master
$ git checkout master # 切换到master分支
$ git checkout -b issue-101 # 创建并切换到bug分支
$ git add readme.txt
$ git commit -m "fix bug 101" # 模拟修复bug
$ git checkout master # 切换到主分支
$ $ git merge --no-ff -m "merged bug fix 101" issue-101 # 将bug分支与主分支合并
2). 修复bug后继续dev分支开发
$ git checkout dev # 继续返回dev写未完成的工作
$ git status # 此时工作区是干净的,那之前保存的现场在哪里呢
$ git stash list # 查看保存的工作现场
3).恢复之前的现场
$ git stash pop # 恢复现场并删除该stash
$ git stash list # 再次查看发现,已经没有stash了
等价于
$ git stash apply # 恢复现场
$ git stash drop # 删除stash
$ git stash list # 再次查看发现,已经没有stash了
还可以多次stash,恢复指定现场
$ git stash apply stash@{0} # 恢复指定编号的现场
feature分支(新功能分支)
开发一个新功能,最好新建一个分支,至于怎么新建分支这个上面已经有概述
特殊情况:如果需要丢弃一个未合并的分支
$ git branch -D feature-vulcan # 如果要强制丢弃一个未合并的分支需要用大写的D参数
多人协作
1). 查看远程仓库信息
$ git remote # 查看远程仓库信息
$ git remote -v # 查看远程仓库详细信息
2). 推送分支到远程库
$ git push origin dev # 推送当前分支到指定的远程分支
但是,==并不是一定要把本地分支往远程推送==,那么,哪些分支需要推送,哪些不需要呢?
master分支是主分支,因此要时刻与远程同步;
dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
总之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!
3). 抓取分支
3.1. 模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆:
$ git clone git@github.com:用户名/仓库名.git # 从服务器上拉取项目到本地
$ git branch # 拉取下来之后本地默认只能看到一个master分支
3.2. 现在你的小伙伴要在dev分支上面进行开发了,于是他需要创建dev分支在本地
$ git checkout -b dev origin/dev # 创建=(远程)=的dev分支到本地
3.3. 现在就可以开发dev分支,并时不时提交到远程
$ git add env.txt # 模拟开发dev分支
$ git commit -m "add env"
$ git push origin dev # 提交分支到远程
3.4. 如果多个人都在开发dev分支呢,为了保证一致性,需要先更新再提交
$ git add env.txt # 模拟你的提交
$ git commit -m "add new env"
$ git push origin dev # 假设你的队友已经修改过dev分支了,那么会推送失败
$ git pull # 需要先pull更新代码为最新,可能还会报错,
## 因为没有建立本地dev与远程dev分支的直接链接
$ git branch --set-upstream-to=origin/dev dev # 按照报错信息建立链接
$ git pull # 这时候返回pull成功,但是可能你和队友修改了同样地方会提示有冲突
$ git commit -m "fix env conflict" # 解决冲突后,先提交,然后push
$ git push origin dev# 修复冲突后再次推送
rebase(变基:说白了就是美化分支线)
多人协作,分支很混乱,如果有强迫症非要整理成一条线呢
1). 原来分支图是这样
$ git log --graph --pretty=oneline --abbrev-commit # 查看分支线
* 582d922 (HEAD -> master) add author
* 8875536 add comment
* d1be385 (origin/master) init hello
* e5e69f1 Merge branch 'dev'
|\
| * 57c53ab (origin/dev, dev) fix env conflict
| |\
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
...
注意到Git用(HEAD -> master)和(origin/master)标识出当前分支的HEAD和远程origin的位置分别是582d922 add author和d1be385 init hello,本地分支比远程分支快两个提交。
2). 当我们再次提交,很不幸,有个人已经提前提交了,出现了冲突,按照习惯先pull一下
$ git push origin master # 假设失败了
$ git pull # 先pull一下
$ git status # 查看状态
$ git log --graph --pretty=oneline --abbrev-commit # 再次查看分支线,发现分叉了,
## 但是现在继续提交时完全没有问题的,就是线不好看
$ git rebase # 用命令美化线为一条
$ git log --graph --pretty=oneline --abbrev-commit # 再次查看分支线,发现变为了一条直线
$ git push origin master # 最后提交分支到远程
==rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比==。
标签管理
tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。例如:v1.2
1). 创建标签
$ git branch # 查看分支
$ git checkout master # 切换到分支
$ git tag v1.0 # 打标签
$ git tag # 查看所有标签
如果前面的忘记打标签现在要补上怎么办
$ git log --pretty=oneline --abbrev-commit # 找到对应版本号
$ git tag v0.9 f52c633 # 指定版本号打标签
$ git tag # 查看标签
标签不是按时间顺序列出,而是按字母排序的。可以用git show <tagname>查看标签信息
$ git show v0.9 # 查看标签信息
$ git tag -a v0.1 -m "version 0.1 released" 1094adb # 要想带上标签说明,可以用-m参数
$ git show v0.1 # 这个命令同样可以查看到标签的说明信息
2). 操作标签
删除本地标签
$ git tag -d v0.1
推送某个标签到远程
$ git push origin v1.0
一次性推送全部尚未推送到远程的本地标签
$ git push origin --tags
删除远程标签
$ git tag -d v0.9 # 先从本地删除
$ git push origin :refs/tags/v0.9 # 删除远程标签
Github上参与别人的开发
在GitHub上,可以任意Fork开源仓库;
自己拥有Fork后的仓库的读写权限;
可以推送pull request给官方仓库来贡献代码。
使用国内的码云(gitee)
1). 先注册账号后配置公钥
2). 如果已经有了一个本地仓库,想要关联到码云
[1]. 首先在码云上面创建这个项目
[2]. 将本地库只与码云库关联
$ git remote add origin git@gitee.com:用户名/项目名.git
==如果在使用命令git remote add时报错,说明本地已经关联了一个远程库==
此时先查看远程库信息
$ git remote -v
然后删除远程库信息,关联到gitee
$ git remote rm origin # 删除远程库
$ git remote add origin git@gitee.com:用户名/项目名.git # 关联码云库
$ git remote -v # 再次查看远程库信息
[3]. 同时关联github和码云
使用多个远程库时,我们要注意,git给远程库起的默认名称是origin,如果有多个远程库,我们需要用不同的名称来标识不同的远程库。
1). 先删除已关联的名为origin的远程库
$ git remote rm origin
2). 添加Github链接
$ git remote add github git@github.com:用户名/仓库名.git
# 注意远程库叫github不叫origin了
3). 添加码云链接
git remote add gitee git@gitee.com:用户名/仓库名.git
# 注意远程库叫gitee不叫origin了
$ git remote -v # 此时发现关联了github和gitee等多个远程库
[4]. 然后就可以正常的pull和push了
## 更新代码为最新
git pull gitee master
git pull github master
## 推送到github远程库
$ git push github master
## 推送到gitee远程库
$ git push gitee master
## 如果不行就用
git push -u gitee master
忽略特殊文件(不能提交到远程库)
有些文件必须在项目目录,但是又不能提交他们,比如数据库密码配置文件
解决方法:在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件,详情参考廖雪峰git。
忽略文件的原则是:
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
- 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
有些时候,你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore忽略了
这个时候可以采用两种方法
1). 一种是-f直接强制提交
$ git add -f App.class
2). 一种是检查是不是过滤用的配置文件写错了
$ git check-ignore -v App.class
配置别名(给git命令添加别名)
配制方法:
$ git config --global alias.st status # 执行之后,就可以用st代替status
$ git config --global alias.unstage 'reset HEAD' # 用unstage代替reset HEAD
配置文件:配置好的规则实际上保存在本地的文件里面
配置Git的时候,加上–global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
每个仓库的Git配置文件都放在.git/config文件中,[alias]下面就是别名,可直接添加、修改或删除
[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