廖雪峰—git
菜鸟教程
撤销commit
git是一个管理文件的工具。
当你需要做一个大工程的时候,文件的管理无疑是非常庞大的工作,因为你需要不断的修改更新文件内容,同时可能还要保留旧版本保证可以复原,这样就需要备份多个版本的文件。
并且在大多数情况下一个工程需要在多数人来共同维护,那么这种情况下不同人之间修改内容的合并也是非常麻烦的,这时使用git就可以很轻松的解决这些问题。
在ubuntu下安装git只需要一个命令即可:
sudo apt-get install git
安装完成后输入git ,就会出现帮助信息。
设置自己的个人信息:
$ git config --global user.name "Your Name"
$ git config --global user.email "[email protected]"
当然一台设备上可以有多个仓库,不同的仓库可以设置不同的个人信息。
当使用git config命令的–global参数后,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
什么是git版本库?
你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
所以,创建一个版本库非常简单,首先,选择一个合适的地方,创建一个空目录:
$ mkdir learngit
$ cd learngit
$ pwd
/Users/michael/learngit
第二步,通过git init命令把这个目录变成Git可以管理的仓库:
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/
创建了一个新的仓库,并且在当前目录下会新增一个.git目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
附:ls -a 查看以“.”开头的隐藏文件。
我们先来理解下 Git 工作区、暂存区和版本库概念:
工作区:就是你之前为了git创建的这个目录。
暂存区:临时存放的区域。
版本库:最终提交到的管理文件的地方。
下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:
Git 常用的是以下 6 个命令:git clone、git push、git add 、git commit、git checkout、git pull。
首先这里再明确一下,所有的版本控制系统,其实只能跟踪文本文件的改动 ,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。
而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。
git add filename
git commit -m "备注"
双引号内可以添加本次提交的说明之类的。
附:为什么Git添加文件需要add,commit一共两步呢?因为commit可以一次提交多个文件,所以你可以多次add不同的文件,比如:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
为了清楚的给出文件提交的信息,git log 可以查看提交日志 :
“version_1”是我在提交时的说明信息。黄色的一大串数字是版本id。
附:如果你嫌输出的信息繁多,加上参数 --pretty=oneline 查看简要信息。
git status: 查看当前仓库的状态,显示变更的文件。
git diff 可以用于查看版本之间的差异。
查看工作区与版本库之间的差异(这里我感觉是比较工作区和暂存区的差异,因为当你添加到暂存区就无法比较了):
git diff
将文件提交到暂存区之后,可以比较暂存区与版本库之间的差异:
git diff HEAD <file>
当你发现工作区与暂存区不一样时,你可以丢弃工作区的改动:
git checkout <file> #丢弃工作区的改动
当你提交到暂存区,但发现错误时,可以取消暂存:
git reset HEAD <file>
当你发现你提交了错误的文件,要撤回,那么就使用git reset 命令吧。
要退回上一个版本,首先要知道当前版本:gitk查看,head指向的就是当前版本。
方法一:
git reset HEAD^ #将上一个版本回退到暂存区
git checkout #将暂存区的文件丢回到工作区,那么这样工作区的文件就是上一个版本的了
方法二:
git reset --hard HEAD^ #直接退回上一个版本
既然head^ 是上一个版本,那么上上一个版本就是head^^ ,当然往上100个版本写100个 ^比较容易数不过来,所以写成HEAD~100。
那么我们现在退回到版本1了,但是又向恢复到版本2了,该怎么办呢?
只需要找到那个版本的版本id,然后:
git reset --hard <version_id> #版本id不用写全,只需写前几位也可
但如果,你已经撤回很多天了,找不到版本id了该怎么办?放心git记得你每一次的提交和退回:
git reflog #git记录了你在这个仓库的所有提交和退回,只需输入git reflog 即可查看
git的版本回退非常迅速,只要一个指令就可完成。
其实在git中又一个head指针永远指向当前版本,我们在做的就是让head指向上一个或下一个版本。
然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。
┌────┐
│HEAD│
└────┘
│
└──> ○ version_3
│
○ version_2
│
○ version_1
改为指向version_2:
┌────┐
│HEAD│
└────┘
│
│ ○ version_3
│ │
└──> ○ version_2
│
○ version_1
当你将文件提交到版本库,误删工作区文件该怎么办?可以从版本库恢复。
git checkout <file>
当你真的想删除一个文件时:
rm <file> #从工作区删除文件
git status #查看改动
git rm <file> #从暂存区删除
git commit -m "rm file" #提交改动
git 支持远程仓库(就像云一样存在)。
找一台电脑充当服务器的角色,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。
github:提供Git仓库托管服务的,只要注册一个GitHub账号,就可以免费获得Git远程仓库。
创建属于自己的github仓库,并添加密钥。廖雪峰—git
将本地仓库与远程仓库关联:
用法:git remote add [<选项>] <名称> <地址>
git remote add origin [email protected]:zjh123456-art/my-first.git
这是我的远程仓库,git把远程仓库的名字默认为origin。
Git支持多种协议,包括https,但ssh协议速度最快。
下一步,就可以把本地库的所有内容推送到远程库上:
把本地库的内容推送到远程,用git push 命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
git push -u origin master
在向github远程仓库推送master分支时,可能会出现以下错误:
解决方案:git push报错
可以看到已经提交到github远程仓库了。
从现在起,只要本地作了提交,就可以通过命令:
git push origin master
把本地master分支的最新修改推送至GitHub。
想要将远程库克隆到本地,输入:
git clone [email protected]:zjh123456-art/second_git.git #地址一定要正确
即可,在当前目录下会生成second_git文件夹。
git 克隆所有分支到本地,并自动切换到tag 的提交
命令:git clone --branch [tags标签] [git地址] 或者 git clone -b [tags标签] [git地址]
例如:git clone -b V1.0.0 https://github.com/xxx/xxx.git
single-branch 指定下载tag所在的单个分支 (可以减少一些下载内容,缩减大小)
git clone -b --single-branch <地址>
depth 指定下载tag所在单个分支,并指定深度 (depth=1时只下载tag一个提交)
git clone -b --depth 1
git 支持多个分支,每个分支之间互不影响。如你想要不影响现在分支上的状况,又想要提交修改,那么你就可以创建一个新的分支。
你在新分支上不停的修改提交,等到时机成熟时,就可以将新分支合并到主分支上,这样就万事大吉。
一开始只有一条master分支,head指向master:
每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。
当我们创建了一个新分支dev,并让head指向dev,就表示当前分支时dev:
创建分支两种方法
方法一:
git checkout -b dev #创建dev分支,-b 切换到dev分支
方法二:
git branch dev #创建dev分支
git checkout dev #切换到dev分支
查看当前分支:
git branch
当我们想要将两个分支合并时:
git checkout master #切回主分支
git merge dev #将dev分支合并到当前分支
注意: 主分支不能向其它分支合并。
当合并完成后,你甚至可以删除dev分支:
git branch -d dev
删除后,查看branch,就只剩下master分支了:
git branch
附:switch 也可以创建或切换分支(不过我的Ubuntu不支持):
git switch -c dev #创建并切换到dev分支
git switch dev #切换到dev分支
小结:
Git鼓励大量使用分支:
查看分支:git branch
创建分支:git branch
切换分支:git checkout 或者git switch
创建+切换分支:git checkout -b 或者git switch -c
合并某分支到当前分支:git merge
删除分支:git branch -d
当我们在两个分支上都提交了新版本时,分支合并会产生冲突:
git status 也会提示:
查看同时修改的文件内容:
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容。
此时,就需要手动去修改冲突,两种修改方式:
然后在主分支提交修改:
git add file1
git commit -m "手动修改冲突"
提交完成后,就变成了这样:
git log或gitk可以查看分支结构。
git log --graph #查看分支合并图
c@c-virtual-machine:~/git_test$ git log --graph --pretty=oneline --abbrev-commit
* dc6082e 手动修改冲突
|\
| * 62903b7 dev 修改file1
* | af8be27 master 修改 file1
|/
* ede5a3b git branch test
* e430d00 rm_test
* 7199da0 test
* f19880f version_3
* cb814bc version_2
* ae6e6ce version_1
接着删除分支就好了:
git branch -d dev
观察gitk 的分支结构,可以发现删除分支并没有删除分支路径,只是删除了dev 指针。
使用–no-ff 参数可以禁用fast forward模式,因为这相当于一次提交,所以可以加注释:
git merge --no-ff -m "备注" dev
合并完成后,分支图:
(fast forward方式合并,合并后dev和master指向的是同一个提交。)
合并分支时,加上–no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:
当你将修改的版本提交到暂存区时,使用命令:
git stash
可以隐藏暂存区的内容,此时git status查看暂存区时干净的:
c@c-virtual-machine:~/git_test$ git status
位于分支 dev
无文件要提交,干净的工作区
刚才的工作现场存到哪去了?用git stash list命令看看:
git stash list
想要恢复工作现场,又两个办法:
方法一:
git stash apply #恢复工作现场
git stash drop #删除stash内容
方法二:
git stash pop #恢复的同时删除stash内容
你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:
git stash apply stash@{0}
git stash save “save message” : 执行存储时,添加备注,方便查找,只有git stash 也要可以的,但查找时不方便识别。
git stash show :显示做了哪些改动,默认show第一个存储,如果要显示其他存贮,后面加stash@{$num},比如第二个 git stash show stash@{1}
git stash show -p : 显示第一个存储的改动,如果想显示其他存存储,命令:git stash show stash@{$num} -p ,比如第二个:git stash show stash@{1} -p
当我们在一个分支上做了一个提交,二另一个分支也想要做同样的修改怎么办?
是不是只有合并分支,或手动修改,git提供了cherry-pick命令,帮助我们只复制一个提交,这样就不用合并分支啦。
git checkout dev
git cherry-pick 4c805e2 #后面跟的是提交的版本id
复制提交时,git会提示冲突,这时需要手动去修改冲突在提交。
当你在一个新分支上做了新的提交后,并没有合并,你想直接删除该分支,这时git会提醒你,没有合并(删了在这个分支上的提交全部作废),不能删除:
这是用 -D 可以强制删除分支:
git branch -D dev1
git remote #查看远程分支
git remote -v #查看远程分支详细信息
git push origin <分支名> #推送指定分支到远程分支
当你git clone时从远程仓库抓取主分支,如果远程库存在dev分支,你可以
git checkout -b dev
创建远程的origin的本地dev分支,然后在这个分支上提交并推送。
当你和你的小伙伴同时向远程dev分支提交时,会产生冲突:
此时需要先用git pull把最新的提交从远程dev抓下来,然后,在本地合并,解决冲突,再推送:
git pull
git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支连接,根据提示,设置dev和origin/dev的链接:
$ git branch --set-upstream-to=<远程库名>/<远程分支名> dev
分支 dev 设置为跟踪来自 myfrist_origin 的远程分支 dev。
输入git pull成功,但是合并有冲突,需要手动解决。
$ git pull
自动合并 file1
冲突(内容):合并冲突于 file1
自动合并失败,修正冲突然后提交修正的结果。
当你需要完成一个大版本需要发布时,你可以给此次提交打标签(内核版本)。
首先,切换到需要打标签的分支上,然后,敲命令git tag 就可以打一个新标签:
git tag v1.0
可以用命令git tag查看所有标签:
$ git tag
v1.0
默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?
方法是找到历史提交的commit id,然后打上就可以了:
git tag v0.9 <版本id>
再用命令git tag查看标签。
查看标签详细信息:
git show <标签> 查看标签信息
还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:
git tag -a v0.1 -m "version 0.1 released" 1094adb
如果标签打错了,也可以删除:
git tag -d <tagname>
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
如果要推送某个标签到远程,使用命令git push origin :
git push origin <tagname>
或者,一次性推送全部尚未推送到远程的本地标签:
git push origin --tags
要删除远程标签就麻烦一点,先从本地删除:
git tag -d v0.9 删除本地标签
git push origin :refs/tags/v0.9 删除远程标签
廖雪峰——git
gitee
自定义git