Git的简单使用
1,创建仓库
建立仓库目录
mkdir rep
2,初始化仓库
使用如下命令
git init
初始化后会产生隐藏文件.git
初始化成功之后配置一下自己的个人信息:
$ git config --global user.name "Your Name" $ git config --global user.email "email@example.com"
因为git是版本控制系统,每个账号要有自己的身份,每个机器都必须自报家门:你的名字和Email地址。你也许会担心,如果有人故意冒充别人怎么办?这个不必担心,首先我们相信大家都是善良无知的群众,其次,真的有冒充的也是有办法可查的。
注意git config
命令的--global
参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址
使用如下命令查看配置信息:
git config -l
3,仓库添加文件
git add readme.txt
Unix的哲学是“没有消息就是好消息”,说明添加成功。
4,提交文件到仓库
git commit -m "first file"
命令格式和svn类似,SVN提交文件命令为:svn commit -m "first file"
5,修改文件提交文件,查看当前仓库状态
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")
看到文件状态为被更改
6,差分查看修改的内容
使用如下命令
git diff readme.txt
结果如下
diff --git a/readme.txt b/readme.txt index 5bd6a90..3b62fc4 100644 --- a/readme.txt +++ b/readme.txt @@ -1,2 +1,3 @@ Git is a version control system. First time to modified! +Second time to modified!
7,提交修改
和SVN不同的是提交前要先add然后再commit
8,查看提交记录
git log
commit d9334820e9f8746578868808d113fc6b8f3360f1 Author: xxxxxx <xxxxxx@163.com> Date: Tue Feb 7 23:49:56 2017 +0800 Secmond time to modified commit 97321e6d811c2c2dc2f2fce8e270676480af8b72 Author: xxxxxx <xxxxxx@163.com> Date: Tue Feb 7 23:29:58 2017 +0800 first time to modifed commit 0fc77bb42c42482b355fed5960a5338c2db2ca25 Author: xxxxxx <xxxxxx@163.com> Date: Tue Feb 7 23:19:42 2017 +0800 first file
提交日志按时间降序排列
如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline
参数:
$ git log --pretty=oneline d9334820e9f8746578868808d113fc6b8f3360f1 Secmond time to modified 97321e6d811c2c2dc2f2fce8e270676480af8b72 first time to modifed 0fc77bb42c42482b355fed5960a5338c2db2ca25 first file
需要注意的是前面一长串d9334820e9f8746578868808d113fc6b8f3360f1是版本号,这有别于SVN,SVN是连续的正整数。这是因为git是分布式的版本管理系统,每个仓库的版本号都不能重复。
9,回退版本
在git中用HEAD表示当前版本,也就是最新提交的d9334820e9f8746578868808d113fc6b8f3360f1版本,上一个版本就是HEAD^,上两个版本是HEAD^^,往前100个版本HEAD~100.
$ git reset --hard HEAD^
输出结果如下
$ git reset --hard HEAD^ HEAD is now at 97321e6 first time to modifed
查看日志如下
$ git log commit 97321e6d811c2c2dc2f2fce8e270676480af8b72 Author: zealzhangz <tenderfacepalm@163.com> Date: Tue Feb 7 23:29:58 2017 +0800 first time to modifed commit 0fc77bb42c42482b355fed5960a5338c2db2ca25 Author: zealzhangz <tenderfacepalm@163.com> Date: Tue Feb 7 23:19:42 2017 +0800 first file
如果还想回到之前最新的版本,需要知道版本号
git reset --hard d93348
git reset --hard d93348 HEAD is now at d933482 Secmond time to modified
然后又回到最新版本了,版本号d93348不一定要写全,但是要能唯一确定一个版本。
如果版本号已经不记得了,可以使用git reflog,git reflog
用来记录你的每一次命令
$git reflog
结果如下
$ git reflog d933482 HEAD@{0}: reset: moving to d93348 97321e6 HEAD@{1}: reset: moving to HEAD^ d933482 HEAD@{2}: commit: Secmond time to modified 97321e6 HEAD@{3}: commit: first time to modifed 0fc77bb HEAD@{4}: commit (initial): first file
这样就能查找到相关的版本号了。
盗张图:
详细可参考:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/0013744142037508cf42e51debf49668810645e02887691000
10、工作区和暂存区
工作区:GIT存储文件的物理目录,比如我当前的/Documents/dev/p/rep目录。工作区有一个隐藏的.git目录,和svn类似,svn也有一个隐藏目录.svn。可以把.git称之为版本库目录(Respository Directory)里面存储了git的很多配置信息。最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master
,以及指向master
的一个指针叫HEAD
。
盗图如下:
回顾前面的提交文件到git分两步走:
第一步、git add ,实际上是把文件添加到暂存区。
第二部、git commit ,实际上是把内容提交到当前分支。我们创建Git版本库时,Git自动为我们创建了唯一一个master
分支,所以,现在,git commit
就是往master
分支上提交更改。
因此我们可以分多次把文件提交到暂存区,然后一次性commit。
需要注意的是:commit只会提交本次暂存区里面的文件,没有add到暂存区的文件,在commit时是不会提交到分支的。
使用如下命令可以对比工作区和最新版本库的区别:
git diff HEAD -- readme.txt
11、撤销修改
1)只在工作区进行了修改没有add
$ 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会告诉你,git checkout -- file
可以丢弃工作区的修改
因此使用如下命令丢弃工作区的修改
$ git checkout -- readme.txt
$ git status
On branch master
nothing to commit, working tree clean
注意:如果有多次修改,并且已经有几次add到暂存区,那么git checkout -- readme.txt 命令将会回到添加到暂存区后的状态,也就是执行命令后工作区修改的内容被丢弃了,并且和当前暂存区一致。git checkout -- file
命令中的--
很重要,没有--
,就变成了“切换到另一个分支”的命令
2)修改已经add到暂存区
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: readme.txt
Git同样告诉我们,用命令git reset HEAD file
可以把暂存区的修改撤销掉(unstage),重新放回工作区:
git reset HEAD readme.txt Unstaged changes after reset: M readme.txt $ 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")
3)修改已经add且commit
可以按照上面回退版本的方法进行回退,不过,这是有条件的,就是你还没有把自己的本地版本库推送到远程,因为Git是分布式版本控制系统。
12、删除文件
使用如下两个命令
$git rm del.txt $git commit -m "deleted file" del.txt
$git rm del.txt rm 'del.txt' $ ls LICENSE readme.txt $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: del.txt
13、远程仓库
git本地仓库与GitHub远程仓库协同工作
1)注册GitHub账号不走略过,在本地使用如下命令生成公钥并上传到GitHub
$ssh-keygen -t rsa -C "myemail@163.com"
一路回车后就生成好了公私钥文件在用户的根目录,注意目录是隐藏的。
$ ls config id_rsa id_rsa.pub known_hosts
拷贝公钥内容id_rsa.pub到GitHub
2)添加GitHub远程库并同步本地git库到GitHub
建好库之后我们根据提示选择推送本地已经有的库到刚建好的GitHub远程仓库。
把本地库的内容推送到远程,用git push
命令,实际上是把当前分支master
推送到远程。
$ git remote add origin https://github.com/xxxxx/learngit.git $ git push -u origin master Username for 'https://github.com': xxxxxx Password for 'https://xxxxxx@github.com': Counting objects: 30, done. Delta compression using up to 8 threads. Compressing objects: 100% (23/23), done. Writing objects: 100% (30/30), 2.49 KiB | 0 bytes/s, done. Total 30 (delta 8), reused 0 (delta 0) remote: Resolving deltas: 100% (8/8), done. To https://github.com/xxxxxxx/learngit.git * [new branch] master -> master Branch master set up to track remote branch master from origin.
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。推送成功后,可以立刻在GitHub页面中看到远程库的内容已经和本地一模一样.
3)从远程仓库克隆
上一节讲的是如果先有了本地项目,如何同步到GitHub远程仓库。如果我们是一个新的项目,从零开始的项目,最好先在远程仓库上创建然后再克隆到本地工作。创建仓库参照上一步骤,创建好之后我们使用以下命令进行克隆。
$ git clone git@github.com:zealzhangz/clonerepository.git Cloning into 'clonerepository'... The authenticity of host 'github.com (192.30.253.113)' can't be established. RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'github.com,192.30.253.113' (RSA) to the list of known hosts. remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (3/3), done.
GitHub给出的地址不止一个,还可以用https://github.com/michaelliao/gitskills.git
这样的地址。实际上,Git支持多种协议,默认的git://
使用ssh,但也可以使用https
等其他协议。使用https
除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh
协议而只能用https
。
14、创建与合并分支
分支的概念就不多讲了,原理和SVN分支的概念如出一辙。下面讲一下创建并且管理分支的一些命令:
1)创建并切换分支
$ git checkout -b dev Switched to a new branch 'dev'
git checkout 加上-b参数表示创建并切换分支,等同于以下两条命令:
$ git branch dev
$ git checkout dev
2)查看当前分支
$ git branch * dev master
git branch
命令会列出所有分支,当前分支前面会标一个*
号。
3)切换分支
$ git checkout master Switched to branch 'master' Your branch is up-to-date with 'origin/master'.
4)合并分支
把dev分支合并到master分支,首先切换到master分支,然后使用以下命令合并:
$ git merge dev Updating 6a61034..17447bc Fast-forward readme.txt | 1 + 1 file changed, 1 insertion(+)
注意到上面的Fast-forward
信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。合并完成后,就可以放心地删除dev
分支了:
$ git branch -d dev
Deleted branch dev (was 17447bc).
查看当前分支信息:
$ git branch
* master
5)常用命令小结
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
15、解决冲突
使用上面的学习的命令git checkout -b feature1创建并切换分支到feature1分支,然后分支上更改文件并提交,切换到master分支,切换到master分支时会有如下提示:
$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 1 commit.
会提示feature1分支比master分支超前一个commit。
更改同一行,并提交,现在分支情况如下:
这种情况下git无法执行快速合并,只能试图把各自的修改合并起来,但是这种合并就会有冲突,比如两个分支更改了同一行。产生冲突如下:
$ git merge feature1 Auto-merging readme.txt CONFLICT (content): Merge conflict in readme.txt Automatic merge failed; fix conflicts and then commit the result.
查看状态如下:
$ git status On branch master Your branch is ahead of 'origin/master' by 3 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")
此时打开文件手动修复冲突:
1 Git is a version control system. 2 First time to modified! 3 Second time to modified! 4 Git is a free software 5 Git has a mutable index called stage. 6 Git tracks changes. 7 acbcd 8 switch brance 9 <<<<<<< HEAD 10 Creating a new branch iS quick AND simple. 11 ======= 12 Creating a new branch IS quick AND simple. 13 >>>>>>> feature1
以下表示当前版本的内容
9 <<<<<<< HEAD
10 Creating a new branch iS quick AND simple.
11 =======
以下表示feature1分支当前版本的内容
11 =======
12 Creating a new branch IS quick AND simple.
13 >>>>>>> feature1
因此需要手动解决冲突如下
1 Git is a version control system. 2 First time to modified! 3 Second time to modified! 4 Git is a free software 5 Git has a mutable index called stage. 6 Git tracks changes. 7 acbcd 8 switch brance 9 Creating a new branch is quick AND simple.
再次提交
$ git add readme.txt $ git commit -m "resolved conflict" [master d1c25a1] resolved conflict
现在两个分支情况如下
使用git log -graph查看分支合并情况
git log --graph --pretty=oneline --abbrev-commit * d1c25a1 resolved conflict |\ | * 371a890 feature1 second commit * | 8998da9 master second commit |/ * 03fef75 feature1 * 17447bc commit brance * 6a61034 delete file * d7d12ed 123 * 781f988 deleted file * a30f515 delete test.txt * 23702d1 add test.txt * bb2a69a git tracks changes * 53638b2 understand how stage works * 4bbda7e changed * d933482 Secmond time to modified * 97321e6 first time to modifed * 0fc77bb first file
最后删除feature1分支
$ git branch -d feature1
Deleted branch feature1 (was 371a890).
16、分支管理策略
通常情况下合并分支时git会使用fast forward模式,这种模式下删除分支后会丢掉分支信息。如果禁用Fast forward模式git会在merge分支时生成一个新的commit,这样就可以保存分支的历史信息。
下面实战一下加上参数--no-ff
方式的git merge。
1)首先建立并切换分支更改readme.txt文件并提交
$ git checkout -b dev Switched to a new branch 'dev'
2)切换到master分支并加上--no-ff并merge到master分支
$ git merge --no-ff -m "merge with no-ff" dev Merge made by the 'recursive' strategy. readme.txt | 1 + 1 file changed, 1 insertion(+) $ git log --graph --pretty=oneline --abbrev-commit * 4d3f53d merge with no-ff |\ | * 950fa74 --no-ff git merge |/ * d1c25a1 resolved conflict |\ | * 371a890 feature1 second commit * | 8998da9 master second commit |/ * 03fef75 feature1 * 17447bc commit brance * 6a61034 delete file * d7d12ed 123 * 781f988 deleted file * a30f515 delete test.txt * 23702d1 add test.txt * bb2a69a git tracks changes * 53638b2 understand how stage works * 4bbda7e changed * d933482 Secmond time to modified * 97321e6 first time to modifed * 0fc77bb first file
不使用Fast forwar模式merge就如下图:
开发中管理分支的几个基本策略:
①master分支为稳定发布分支,也可以叫release分支,每次往生产环境发布的分支
②Dev分支为开发分支,每位开发者分别从Dev分支拉自己的开发分支,本地测试通过后提交到Dev分支,等待测试小组测试通过了merge到master分支发布。
③feature/bug分支,如果增加了新功能且改动较大做好重新拉一个分支,待开发结束后再合并到Dev分支、再合并到master分支,bug分支或者经紧急对应分支,每次生产报了一个阻塞用户或者比较紧急的bug可以单独拉一个分支对应。如果bug不紧急可以等到下次正常发布一起。
盗图如下:
17、保存现场stash功能
有这样一个场景,我正在开发新功能,开发到一半突然来个紧急bug要修。新功能一时半会开发不完,因此已经更改或新建的新功文件能不能和bug一起提交。这个时候就需要保留开发到一半的现场,等我修完bug再接着开发,因此git有stash(存储,储存的意思)功能来保留现场。实战如下:
$ git status On branch master Your branch is up-to-date with 'origin/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 stash Saved working directory and index state WIP on master: 4d3f53d merge with no-ff HEAD is now at 4d3f53d merge with no-ff $ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working tree clean
可以看到执行git stash命令后查看工作区状态就是干净的.
现在要执行如下步骤修bug,完成后并回复工作区间
①切换到指定分支一般是当前的稳定分支(trunk)分支并拉出bug分支
②在bug分支issure-001修复bug后提交
③切换到trunk分支合并bug分支并发布
④好了之后可以删除bug分支切换到刚开始的Dev分支恢复之前的工作现场继续开发
注意:这里的工作区间可以恢复到任何分支,比如刚创建的分支,但可能会有冲突需要解决
恢复stash有两种方法:
法一:
使用git stash list查看当前的保存的工作区间列表,使用git stash apply stash@{0}来进行恢复,这种恢复方式,恢复之后之前保存的stash还是存在的,不会被删除。需要手动删除。
$ git stash list $ git stash apply stash@{0} $ git stash drop
法二:
用git stash pop
,恢复的同时把stash内容也删了
$ git stash pop
详细操作步骤如下:
$ git checkout master
$ git checkout -b issure-001
Switched to a new branch 'issure-001' $ vim readme.txt $ git add readme.txt $ git commit -m "fix bug 001" [issure-001 821f542] fix bug 001 1 file changed, 1 insertion(+), 1 deletion(-) $ git checkout master Switched to branch 'master' Your branch is up-to-date with 'origin/master'. $ git merge --no-ff -m "merged bug fix 001" issure-001 Merge made by the 'recursive' strategy. readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) $ git stage list fatal: pathspec 'list' did not match any files $ git stash list stash@{0}: WIP on master: 4d3f53d merge with no-ff $ git branch -d issure-001 Deleted branch issure-001 (was 821f542). $ git checkout -b dev Switched to a new branch 'dev' $ git stash list stash@{0}: WIP on master: 4d3f53d merge with no-ff $ git branch * dev master $ git stash apply stash@{0} Auto-merging readme.txt CONFLICT (content): Merge conflict in readme.txt $ git stash list stash@{0}: WIP on master: 4d3f53d merge with no-ff $ git stash drop Dropped refs/stash@{0} (417d5d4606fde5e2e92f9c7659e38d498caa6b63) $ git status On branch dev Unmerged paths: (use "git reset HEAD <file>..." to unstage) (use "git add <file>..." to mark resolution) both modified: readme.txt no changes added to commit (use "git add" and/or "git commit -a")