1、创建版本库
版本库:版本库(repository)可以理解为一个目录,在这个目录里的文件都可以被git管理起来,可以进行增删改查操作,并且可以追逐历史记录,或者在将来对文件进行某时刻的还原。
创建版本库流程:
(1)创建一个空目录:mkdir git&&cd git
(2)通过git init 命令把这个目录变成git管理的仓库。运行命令后会出现.git的目录。这个目录就是git的跟踪版本库,不要手动修改这个库,否则会破坏git仓库。如果没有发现.git的目录,可以用 ls -ah命令就可以发现这个.git目录,或者在系统上把隐藏文件设置成可见。
2、把文件添加到版本库
(1)在目录下添加要上传的文件,用git add 命令告诉git,把文件添加到仓库
(2)然后使用git commit命令告诉git,把文件提交给仓库。git commit
命令,-m
后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录,不要随便输入,因为输入说明对自己和别人很重要。
3、查看命令结果
(1)可以用git status命令查看掌握仓库当前的状态。当使用git
(2)在git add 命令使用git diff命令可以查看具体修改了什么内容,git diff HEAD 命令可以查看版本库和暂存区的内容有什么区别。
4、版本回退
在实际的开发中,我们会用git commit提交多个版本,一旦把文件改乱了,或者误删了某个文件,可以使用版本回退的方法回到特定版本。
(1)用git log可以看到最近到最远的提交日志,如果嫌输出信息太多可以加上--pretty=oneline参数。用git log命令看到的是一大串16进制的commit id;
(2)返回上一版本可以使用git reset --hard ^HEAD命令。
(3)如果想回退到其他版本,可以使用git log查看提交历史,用git reset --hard commit_id,回退到某个版本。
(4)如果后悔了上面的回退大操作,想使用最新版本,可以使用git feflog查看命令历史,拿到最新版本的commit id,用git reset --hard commit_id,就可以回退到最新版本。
注意:git log是用来查看提交历史的,而git reflog是用来查看命令历史的。
5、工作区和版本库
(1)工作区是指你在电脑里看到的目录,比如我上面创建的git。.git不属于工作区,而是属于版本库。
(2)在git的版本库中有很多东西,其中最重要的是暂存区(stage),还有git为我们创建的第一分支master分支,已经指向master的HEAD指针。
(3)把工作区的文件添加至版本库分两步执行:
A:用git add 命令把文件添加至暂存区(stage)。B:用git commit把暂存区的内容添加至当前分支。
6、撤销修改
当你在修改代码出现错误还没有提交(commit)时,可以用git checkout -- file可以丢弃工作区的修改,例如git checkout --readme.txt丢弃工作区的修改。这时有两种状态,一种是有git add的时候,会返回暂存区的内容。另一种是没有git add的时候,会返回版本库的内容。git checkout 后面的“--”很重要,如果没有就表示切换分支。
7、删除文件
一般情况下通常在工作区就把文件删除了。然后用git rm删除掉版本库的内容,然后再用git commit提交。
8、远程仓库
由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:
A:创建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
是公钥,可以放心地告诉任何人。
B:登陆GitHub,打开“Account settings”,“SSH Keys”页面:
然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub
文件的内容。GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
9、添加远程仓库
A:登陆GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库git,在github上这个仓库是空的,
可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。
现在,我们根据GitHub的提示,在本地的git仓库下运行命令:
$ git remote add origin git@github.com:aaaaa/git.git
B:把本地库的内容推送到远程,用git push
命令,实际上是把当前分支master
推送到远程。
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
10:从远程库中克隆
要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone
命令克隆。
Git支持多种协议,包括https
,但通过ssh
支持的原生git
协议速度最快。
11、分支管理创建与合并
(1)创建dev
分支,然后切换到dev
分支:
$ git checkout -b dev
Switched to a new branch 'dev'
(2)git checkout
命令加上-b
参数表示创建并切换,相当于以下两条命令:
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
(3)用git branch查看当前的分支,当前分支前面会标一个*
号。
$ git branch
* dev
master
(4)然后用git add和git commit往分支里面添加内容
$ git add readme.txt
$ git commit -m "test"
(5)dev分支的工作已完成,我们切换到master分支,查看文件,会发现刚才的内容不见了,这是因为添加的内容是dev分支,而master分支此刻添加没有改变,合并分支用git merge dev
git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
git merge有两种方式默认是采用Fast-Forward(快进模式),第二钟是--no-ff模式,采用Fast-Forward模式删除分支后不会保留分支的内容,--no-off模式删除分支后会保留分支的信息。
(6)合并后可以放心删除dev分支
$ git branch -d dev
Deleted branch dev (was b17d20e).
12、解决冲突
(1)当合并分支前master和feature1
分支的内容都修改了同一处地方,
(2)多人协作时在同一分支修改了同一处地方,这时git无法快速合并,会产生合并的冲突。这时用git status可以查看告诉我们冲突的文件。
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
第一种情况我们可以直接查看冲突的地方,Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,我们修改如下后保存并提交,就可以解决冲突。
第二种情况是要先用git pull命令把远程库的内容拉回来,查看冲突的地方并进行修改,然后再提交就可以解决冲突。
13、BUG分支
当在软件开发中遇见bug要对bug进行修改,但当前的内容还没开发完,要先去修改bug,目前的内容还不能提交。可以用git stash命令把当前内容储存下来,然后把master(已上下)分支的内容拉下来进行修改,修改完bug提交。
$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
(use "git push" to publish your local commits)
$ git checkout -b issue-101
Switched to a new branch 'issue-101'
修复后可以用回到dev分支继续干活,可以用git stash lit命令查看储藏的列表
$ git stash list
stash@{0}: WIP on dev: f52c633 add merge
恢复原来的工作现场有两中方法,第一种是git stash pop,恢复后删除原来的储藏内容,第二种是git stash apply,不会删除储藏的内容,要删除的话用git stash drop来删除
提示:在开发中可以多次stash,然后用git stash list查看内容,然后恢复到对应的版本即可
14:多人合作
-
首先,可以试图用
git push origin <branch-name>
推送自己的修改; -
如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
试图合并; -
如果合并有冲突,则解决冲突,并在本地提交;
-
没有冲突或者解决掉冲突后,再用
git push origin <branch-name>
推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
。
15:rebase(变基)
上一节我们看到了,多人在同一个分支上协作时,很容易出现冲突。即使没有冲突,后push的童鞋不得不先pull,在本地合并,然后才能push成功。
每次合并再push后,分支变成了这样:
$ git log --graph --pretty=oneline --abbrev-commit
* d1be385 (HEAD -> master, origin/master) init hello
* e5e69f1 Merge branch 'dev'
|\
| * 57c53ab (origin/dev, dev) fix env conflict
| |\
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
| |/
* | 12a631b merged bug fix 101
|\ \
| * | 4c805e2 fix bug 101
|/ /
* | e1e9c68 merge with no-ff
|\ \
| |/
| * f52c633 add merge
|/
* cf810e4 conflict fixed
总之看上去很乱,有强迫症的童鞋会问:为什么Git的提交历史不能是一条干净的直线?
其实是可以做到的!
Git有一种称为rebase的操作,有人把它翻译成“变基”。
输入命令git rebase
试试,用git log
看看效果:
$ git rebase
First, rewinding head to replay your work on top of it...
Applying: add comment
Using index info to reconstruct a base tree...
M hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
Applying: add author
Using index info to reconstruct a base tree...
M hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
16、标签管理
我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。Git有commit,为什么还要引入tag?“请把上周一的那个版本打包发布,commit号是6a5819e...”“一串乱七八糟的数字不好找!”如果换一个办法:“请把上周一的那个版本打包发布,版本号是v1.2”“好的,按照tag v1.2查找commit就行!”.所以,tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
17、创建标签
(1)切换到需要打标签的分支
(2)用git tag <name>打标签
(3)可以用git tag查看所有标签。
默认标签是打字commit上的,如果在前面忘记打标签栏,可以用git log查看提交的commit id,然后用git tag <name><comit_id>就可以打死tag了。
注意:标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签。
18、操作标签
如果标签打错了,可以用git tag -d <name>删除
$ git tag -d v0.1
Deleted tag 'v0.1' (was f15b0dd)
因为创建的标签名是本地不会自动推送到远程,所以只删除本地即可
如果要将标签推送到远程可以用git push origin <name>或者一次性将所有标签推送至远程用git push origin --tags推送。
$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
* [new tag] v1.0 -> v1.0
$ git push origin --tags
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
* [new tag] v0.9 -> v0.9
如果将tag推送到了远程,要删除远程标签就麻烦一点,先从本地删除,然后再把远程的删除,命令如下
$ git tag -d v0.9
Deleted tag 'v0.9' (was f52c633)
然后,从远程删除。删除命令也是push,但是格式如下:
$ git push origin :refs/tags/v0.9
To github.com:michaelliao/learngit.git
- [deleted] v0.9