廖雪峰Git教程
猴子都能懂的Git入门
可视化工具gitk
图解Git
工作区和暂存区
设置全局身份
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
配置密码(SSH公钥)
#1)生成密钥,一路回车、用默认选项
ssh-keygen -t rsa
#2)复制到粘贴板
cat ~/.ssh/id_rsa.pub | clip # Windows
cat ~/.ssh/id_rsa.pub | pbcopy # MacOS
#3)粘贴到Github的SSH Keys[注意:一定要把不可见的换行符去掉]
创建版本库
- 创建一个目录
$ mkdir learngit //新建一个learngit文件夹
$ cd learngit
$ pwd //Print Working Directory显示当前目录
/Users/michael/learngit
- 初始化一个仓库
git init
- 添加文件至暂存区
git add <file>
git add -u //将文件的修改、文件的删除,添加到暂存区。
git add . //将文件的修改,文件的新建,添加到暂存区。
git add -A //将文件的修改,文件的删除,文件的新建,添加到暂存区。
- 暂存区提交到到仓库
git commit -m <message> //-m后面输入的是本次提交的说明,例如 git commit -m "add 3 files."
版本回退
- 查看操作记录
git log //可以显示所有提交过的版本信息,看完后输入“q”退出
git log --graph --oneline //指定--graph选项,能以文本形式显示更新记录的流程图。指定--oneline选项,能在一行中显示提交的信息。
git reflog //查看所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作)用git log则是看不出来被删除的commitid,用git reflog则可以看到被删除的commitid,我们就可以买后悔药,恢复到被删除的那个版本
- 回退到某一版本
git reset --hard <commit id>
HEAD^ //上一个版本 HEAD指针指向当前版本
HEAD^^ //上上一个版本
HEAD~100 //上100个版本
查看状态与区别
- 查看暂存区中有哪些文件
git ls-files
- 仓库当前状态
git status
- 工作区与暂存区的区别
(1)git diff //当工作区有改动,临时区为空,diff的对比是“工作区与最后一次commit提交的仓库的共同文件”;当工作区有改动,临时区不为空,diff对比的是“工作区与暂存区的共同文件”。
(2)git diff --cached 或 git diff --staged //显示暂存区(已add但未commit文件)和最后一次commit(HEAD)之间的所有不相同文件的增删改(git diff --cached和git diff –staged相同作用)
(3)git diff HEAD //显示工作目录(已track但未add文件)和暂存区(已add但未commit文件)与最后一次commit之间的的所有不相同文件的增删改。
(3.1)git diff HEAD~X或git diff HEAD^^^…(后面有X个^符号,X为正整数):可以查看最近一次提交的版本与往过去时间线前数X个的版本之间的所有同(3)中定义文件之间的增删改。
(4)git diff <分支名1> <分支名2> //比较两个分支上最后 commit 的内容的差别
删除文件
rm file //删除工作区的文件
git rm file //删除版本库中的文件,此时工作目录中此文件也会被删除。
//执行完此命令后也只是在暂存区,所以还有恢复的可能,用git reset HEAD <file>可以撤销修改
//完整流程应该记得要再提交一次git commit -m <message>
添加远程库
- 关联一个远程库
git remote add <name> <url> //添加了一个远程数据库,地址是<url>,并给它起了一个别名叫<name>
git remote add origin git@server-name:path/repo-name.git
执行推送或者拉取的时候,如果省略了远程数据库的名称,则默认使用名为”origin“的远程数据库。因此一般都会把远程数据库命名为origin。
- 推送修改
git push <远程主机名> <本地分支名> <远程分支名> //一般形式
git push origin master:refs/for/master //即是将本地的master分支推送到远程主机origin上的对应master分支, origin 是远程主机名,第一个master是本地分支名,第二个master是远程分支名。refs/for 的意义在于我们提交代码到服务器之后是需要经过code review 之后才能进行merge的,而refs/heads 不需要
git push -u origin master //第一次推送master分支的所有内容
//push时的 -u 参数表示把origin这个主机设置为默认远程主机,以后用 git push 就可以把当前分支推送到这个主机上的对应分支,是一种简化写法
git push origin master //推送最新修改
从远程库克隆
git clone <repository> <directory> //Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快
//在<repository>指定远程数据库的URL,在<directory>指定新目录的名称
创建切换删除分支
- 查看分支
git branch
- 创建分支
git branch <name>
- 切换分支
git checkout <name>
- 创建+切换分支
git checkout -b <name>
- 合并某分支到当前分支
git merge <name>
- 删除分支
git branch -d <name>
git branch -D <name> //强行删除一个没有被合并过的分支
//区别
//git branch -d 会在删除前检查merge状态(其与上游分支或者与head)。
//git branch -D 是git branch --delete --force的简写,它会直接删除。
在dev分支修改了文件,但是没有提交到仓库,实际上就是相当于在本地手动修改了这个文件,仓库并不能保存你做的改动,所以在master分支能看到文件被改动了(相当于没用dev分支直接修改了这个文件一样),所以可以用master分支add、commit。即对于所有分支而言, 工作区和暂存区是公共的。
分支合并之merge
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息
- 用 Fast forward 模式合并
git merge bugfix
或者像这样
- 用普通模式合并
git merge --no-ff -m "merge with no-ff" dev //若不加--no-ff表示使用Fast forward模式合并
分支合并之rebase
如果使用rebase方法进行分支合并,会出现下图所显示的历史记录
git checkout bugfix //切换到bugfix分支
git rebase master //使用rebase方法进行分支合并
首先,rebase bugfix分支到master分支, bugfix分支的历史记录会添加在master分支的后面。如图所示,历史记录成一条线,相当整洁。
这时移动提交X和Y有可能会发生冲突,所以需要修改各自的提交时发生冲突的部分。
rebase之后,master的HEAD位置不变。因此,要合并master分支和bugfix分支,即是将master的HEAD移动到bugfix的HEAD这里。
解决冲突
当Git无法自动合并分支时,就必须首先解决冲突。
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。
如果是merge,解决冲突后,再次add和commit。
git add myfile.txt
git commit -m "modified myfile.txt"
rebase的时候,修改冲突后的提交不是使用commit命令,而是执行rebase命令指定 --continue选项。若要取消rebase,指定 --abort选项。
git add myfile.txt
git rebase --continue
Applying: 添加pull的说明
BUG分支
git stash //把所有未提交的修改(包括暂存的和非暂存的)都保存起来,用于后续恢复当前工作目录
git stash save "test-cmd-stash" //实际应用中推荐给每个stash加一个message,用于记录版本
git stash list //查看现有stash
git stash apply //恢复最近一次的stash,但是恢复后,stash内容并不删除,可以恢复指定的一次stash
git stash drop //删除最近一次的stash
git stash pop //就是git stash apply + git stash drop
git stash clear //删除所有缓存的stash
默认情况下,git stash会缓存下列文件:
- 添加到暂存区的修改(staged changes)
- Git跟踪的但并未添加到暂存区的修改(unstaged changes)
但不会缓存以下文件:
- 在工作目录中新的文件(untracked files)
- 被忽略的文件(ignored files)
多人协作
git remote //查看远程库的信息
git remote -v //显示远程库更详细的信息
git fetch
执行pull,远程数据库的内容就会自动合并。但是,有时只是想确认本地数据库的内容而不想合并。这种情况下,请使用fetch。执行fetch就可以取得远程数据库的最新历史记录。取得的提交会导入到没有名字的分支,这个分支可以从名为FETCH_HEAD的退出。
例如,在本地数据库和远程数据库的origin,如果在从B进行提交的状态下执行fetch,就会形成如下图所示的历史记录。
在这个状态下,若要把远程数据库的内容合并到本地数据库,可以合并FETCH_HEAD,或者重新执行pull。
- 推送修改
git push <远程主机名> <本地分支名> <远程分支名> //一般形式
git push origin master:refs/for/master //即是将本地的master分支推送到远程主机origin上的对应master分支, origin 是远程主机名,第一个master是本地分支名,第二个master是远程分支名。refs/for 的意义在于我们提交代码到服务器之后是需要经过code review 之后才能进行merge的,而refs/heads 不需要
git push -u origin master //第一次推送master分支的所有内容 -u 参数表示把origin这个主机设置为默认远程主机,以后用 git push 就可以把当前分支推送到这个主机上的对应分支,是一种简化写法
git push origin master //推送最新修改
git pull <远程主机名> <远程分支名>:<本地分支名> //取回远程主机某个分支的更新,再与本地的指定分支合并
git pull = git fetch + git merge FETCH_HEAD
git pull --rebase = git fetch + git rebase FETCH_HEAD
- 查看远程库信息,使用git remote -v;
- 本地新建的分支如果不推送到远程,对其他人就是不可见的;
- 在本地创建和远程分支对应的分支,使用git checkout -b branch-name
origin/branch-name,本地和远程分支的名称最好一致; - 从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;
- 建立本地分支和远程分支的关联,使用git branch --set-upstream-to=origin/branch-name branch-name;
- 从远程抓取分支,使用git pull <远程主机名> <远程分支名>:<本地分支名>,如果有冲突,要先处理冲突。
标签操作
git tag <tagname> //添加轻标签,默认为HEAD,也可以指定一个commit id,例如git tag v0.9 f52c633
git log --decorate //如果在log命令添加 --decorate选项执行,可以显示包含标签资料的历史记录
git tag -a <tagname> //若要添加注解标签,可以在tag命令指定 -a选项执行。执行后会启动编辑区,请输入注解,也可以指定-m选项来添加注解。
git tag -a <tagname> -m "blablabla..." //添加注解标签
git tag //查看所有标签
git tag -n //如果在tag命令指定-n选项执行,可以显示标签的列表和注解
git tag -d <tagname> //删除一个本地标签
git push origin <tagname> //推送一个本地标签
git push origin --tags //推送全部未推送过的本地标签
git push origin :refs/tags/<tagname> //删除一个远程标签
撤销修改
- 撤销工作区的修改
git checkout -- file //把文件在工作区的修改撤销到最近一次 git add 或 git commit时的内容
- 撤销暂存区的修改
git reset HEAD <file> //把暂存区的修改撤销掉(unstage),经试验并不会影响工作区内容
- 修改最近的提交
指定amend选项执行提交的话,可以修改同一个分支最近的提交内容和注解
主要使用的场合:
添加最近提交时漏掉的档案
修改最近提交的注解
git add sample.txt
git commit --amend
- 取消过去的提交
在revert可以取消指定的提交内容。使用后面要提到的rebase -i或reset也可以删除提交。但是,不能随便删除已经发布的提交,这时需要通过revert创建要否定的提交。
主要使用的场合:
安全地取消过去发布的提交
git revert HEAD
- 遗弃提交
在reset可以遗弃不再使用的提交。执行遗弃时,需要根据影响的范围而指定不同的模式,可以指定是否复原索引或工作树的内容。
除了默认的mixed模式,还有soft和hard模式。欲了解受各模式影响的部分,请参照下面的表格。
主要使用的场合:
复原修改过的索引的状态(mixed)
彻底取消最近的提交(hard)
只取消提交(soft)
git reset --hard HEAD~~
在reset之前的提交可以参照ORIG_HEAD。Reset错误的时候,在ORIG_HEAD上reset 就可以还原到reset前的状态。
git reset --hard ORIG_HEAD
- 提取提交
您可以从其他分支复制指定的提交,然后导入到现在的分支
主要使用的场合:
把弄错分支的提交移动到正确的地方
把其他分支的提交添加到现在的分支
git checkout master
git cherry-pick 99daed2
如果发生冲突,就修改冲突的部分之后再提交
-
改写提交的历史记录
在rebase指定i选项,您可以改写、替换、删除或合并提交。
主要使用的场合:
在push之前,重新输入正确的提交注解
清楚地汇合内容含义相同的提交。
添加最近提交时漏掉的档案 -
汇合分支上的提交,然后一同合并到分支
我们介绍一下merge的特殊选项:squash
用这个选项指定分支的合并,就可以把所有汇合的提交添加到分支上。
主要使用的场合:
汇合主题分支的提交,然后合并提交到目标分支。
git checkout master
git merge --squash issue1
这样就把issue1分支的所有提交合并成一个提交,并导入到master分支。若发生冲突,则解决冲突后再add再commit