一、Git简介
1、创建版本库
第一步:创建一个空目录:
yanjundong:~ yanjundong$ cd Desktop/
yanjundong:Desktop yanjundong$ mkdir learngit
yanjundong:Desktop yanjundong$ cd learngit/
yanjundong:learngit yanjundong$ pwd
/Users/yanjundong/Desktop/learngit
pwd
命令用于显示当前目录。
第二步,通过 git init
命令把这个目录变成Git可以管理的仓库
yanjundong:learngit yanjundong$ git init
Initialized empty Git repository in /Users/yanjundong/Desktop/learngit/.git/
把文件添加到版本库
先创建一个 readme.txt
文件,内容如下
Git is a version control system.
Git is free software.
接下来把这个文件放到Git仓库。
第一步:用命令git add
告诉Git,把文件添加到仓库:
git add readme.txt
第二步:用命令git commit
告诉Git,把文件提交到仓库:
yanjundong:learngit yanjundong$ git commit -m "wrote a readme file"
[master (root-commit) d443150] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
-m
后面输入的是本次提交的说明1 file changed
:1个文件被改动2 insertions
:插入了两行内容
为什么Git添加文件需要add
,commit
一共两步呢?因为commit
可以一次提交很多文件,所以你可以多次add
不同的文件,比如:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
二、时光机穿梭
1、查看仓库状态
我们继续修改readme.txt文件,改成如下内容:
Git is a distributed version control system.
Git is free software.
现在,运行git status
命令看看结果:
yanjundong:learngit yanjundong$ 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 status
命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们,readme.txt
被修改过了,但还没有准备提交的修改。
查看具体修改了什么内容
需要用git diff
这个命令看看:
yanjundong:learngit yanjundong$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 013b5bc..d725c3d 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
Git is a distributed version control system.
-Git is free software.
\ No newline at end of file
+Git is free software!!!
\ No newline at end of file
小结
- 要随时掌握工作区的状态,使用
git status
命令。 - 如果
git status
告诉你有文件被修改过,用git diff
可以查看修改内容。
2、版本回退
git log
命令可以查看历史记录:
yanjundong:learngit yanjundong$ git log
commit 0cdb7dc15d8b404f615a7de94fb77b4b21d831f3 (HEAD -> master)
Author: yanjundong <yanjundong97@163.com>
Date: Thu Feb 13 01:07:10 2020 +0800
append text
commit 0a527f9787d9365cac711b6ba58bb3af8b758cea
Author: yanjundong <yanjundong97@163.com>
Date: Thu Feb 13 00:56:16 2020 +0800
append GPl
commit d4431501e6b863057e8c7b44d40359cd8e426f11
Author: yanjundong <yanjundong97@163.com>
Date: Thu Feb 13 00:52:43 2020 +0800
wrote a readme file
把 readme.txt
回退到上一个版本。
在Git中,用HEAD
表示当前版本,上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。
yanjundong:learngit yanjundong$ git reset --hard HEAD^
HEAD is now at 0a527f9 append GPl
如果这时候后悔了,,想回到之前的那个最新版本,只要上面的命令行窗口还没有被关掉,就可以指定回到未来的某个版本:
yanjundong:learngit yanjundong$ git reset --hard 0cdb7dc15d8b404f615a7de94fb77b4b21d831f3
HEAD is now at 0cdb7dc append text
如果找不到想恢复版本的 commit id
,Git提供了一个命令git reflog
用来记录你的每一次命令:
yanjundong:learngit yanjundong$ git reflog
0cdb7dc (HEAD -> master) HEAD@{0}: reset: moving to 0cdb7dc15d8b404f615a7de94fb77b4b21d831f3
0a527f9 HEAD@{1}: reset: moving to HEAD^
0cdb7dc (HEAD -> master) HEAD@{2}: commit: append text
0a527f9 HEAD@{3}: commit: append GPl
d443150 HEAD@{4}: commit (initial): wrote a readme file
从输出可知,append text
的commit id是 0cdb7dc
3、工作区和暂存区
工作区就是在自己的电脑上能够看到的文件。
stage
:暂存区master
分支
工作区现有 readme.txt
和 LICENSE
,在执行git add
命令实际上就是把要提交的所有修改放到暂存区(Stage):
执行git commit
就可以一次性把暂存区的所有修改提交到分支:
4、撤销修改
命令 git checkout -- readme.txt
意思就是,把readme.txt
文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt
修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一样的状态;
一种是readme.txt
已经添加到暂存区后,又作了修改,现在,撤销修改就回到和暂存区一样的状态。
总之,就是让这个文件回到最近一次 git commit
或 git add
时的状态。
git checkout -- readme.txt
小结
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file
。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD
,就回到了场景1,第二步按场景1操作。
5、删除文件
在Git中,删除也是一个修改操作。
我们在工作区添加一个 test.txt
文件,并执行git add
和 git commit
命令提交。
现在删除 test.txt
文件,
rm test.txt
git status
命令会知道哪些文件被删除了:
yanjundong:learngit yanjundong$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
可以使用git rm
命令,并且git commit
,就可以从版本库删除该文件:
yanjundong:learngit yanjundong$ git rm test.txt
rm 'test.txt'
yanjundong:learngit yanjundong$ git commit -m "delete test.txt"
[master 87c8558] delete test.txt
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 test.txt
如果是误删的,可以通过 git checkout -- test.txt
命令恢复。
三、远程仓库
本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:
第1步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:
$ ssh-keygen -t rsa -C "youremail@example.com"
你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。
如果一切顺利的话,可以在用户主目录里找到.ssh
目录,里面有id_rsa
和id_rsa.pub
两个文件,这两个就是SSH Key的秘钥对,id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心地告诉任何人。
第2步:登陆GitHub,打开“settings”,“SSH and GPG Keys”页面:
然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub
文件的内容:
1、添加远程库
创建新的Git仓库
根据GitHub的提示,在本地的learngit
仓库下运行命令:
$ git remote add origin https://github.com/yanjundong/learngit.git
添加后,远程库的名字就是origin
,这是Git默认的叫法,
下一步,就可以把本地库的所有内容推送到远程库上:
yanjundong:learngit yanjundong$ git push -u origin master
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 6 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (13/13), 1.06 KiB | 1.06 MiB/s, done.
Total 13 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), done.
To https://github.com/yanjundong/learngit.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
把本地库的内容推送到远程,用git push
命令,实际上是把当前分支master
推送到远程。
由于远程库是空的,在第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送到远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
推送成功后,可以在GitHub页面中看到远程库的内容:
从现在起,只要本地作了提交,就可以通过命令:
$ git push origin master
把本地master
分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库!
2、从远程库克隆
git clone
命令克隆。
Git支持多种协议,包括https
,但通过ssh
支持的原生git
协议速度最快。
四、分支管理
你可以创建一个属于自己的分支,别人看不到(不会影响别人工作),你在自己的分支上工作,知道开发完毕后再一次性合并到原来的分支上。既安全,也不影响别人工作。
1、创建和合并分支
Git中 master
指向最新的提交,用 HEAD
指向当前的分支。->一开始,Git就是一条线,只有master
分支,而 HEAD
指向master
。
当创建并切换到新的分支,例如dev
时,Git会新建一个指针dev
,指向master
相同的提交,再把HEAD
指向dev
,表示当前分支在dev
上:
从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
在dev
上的工作完成后,就可以把dev
合并到master
上。
1)、创建和切换
$ git checkout -b dev
Switched to a new branch 'dev'
git checkout
命令加上-b
参数表示创建并切换,相当于以下两条命令:$ git branch dev $ git checkout dev Switched to branch 'dev'
2)、查看当前分支
$ git branch
* dev
master
3)、合并分支
git merge
命令用于合并指定分支到当前分支。
$ git merge dev
Updating c1d78a7..f9078b7
Fast-forward
git.md | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
上面的
Fast-forward
信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。
4)、删除分支
$ git branch -d dev
2、解决冲突
准备一个新的分支 feature
,
$ git checkout -b feature
修改 readme.txt
的内容为:
Creating a new branch is quick AND simple.
在feature1
分支上提交。
切换到master
分支:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Git还会自动提示我们当前master
分支比远程的master
分支要超前1个提交。
在master
分支上把readme.txt
文件的最后一行改为如下内容并提交:
Creating a new branch is quick & simple
现在,master
分支和feature
分支各自都分别有新的提交,变成了这样:
这种情况下,Git无法执行“快速合并”,只能在解决了冲突后再提交。
$ 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")
1)、git log
用带参数的git log
也可以看到分支的合并情况:
$ git log --graph --pretty=oneline --abbrev-commit
* 5d62d29 (HEAD -> master) test
|\
| * 851eed6 (feature1) 冲突测试
* | 502c339 master冲突测试
|/
* c35b117 (origin/master) and feature
* f9078b7 branch dev
* c1d78a7 远程仓库结束
* 6281d8d 学完基础
3、分支管理策略
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
合并命令,请注意--no-ff
参数,表示禁用Fast forward
::
$ git merge --no-ff -m "merge with no-ff" dev
1)、分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
4、现场保存
如果现在要新创建一个分支去工作,但当前分支的工作还没有提交。况且工作才进行到一半,还没法提交,这时就需要把当前工作现场“储藏”起来,等以后恢复现场后继续工作。
1)、保存现场
$ git stash
在储藏之后,用git status
查看工作区,就是干净的
2)、查看现场列
$ git stash list
stash@{0}: WIP on dev: f52c633 add merge
3)、恢复现场
需要恢复现场,有两个办法:
-
用
git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用git stash drop
来删除; -
用
git stash pop
,恢复的同时把stash内容也删了
$ git stash apply stash@{0}
4)、小结
保存现场:git stash
查看现场:git stash list
恢复现场:git stash apply
或 git stash pop
5、bug修复
在master分支上修复的bug,想要合并到当前dev分支,可以用 git cherry-pick
命令,把bug提交的修改“复制”到当前分支,避免重复劳动。
6、功能分支
开发一个新feature,最好新建一个分支;
如果要丢弃一个没有被合并过的分支,可以通过git branch -D
强行删除。
7、多人协作
1)、查看远程库
$ git remote
origin
#查看更详细信息,上面显示了可以抓取和推送的origin的地址
$ git remote -v
origin https://github.com/yanjundong/learnGit.git (fetch)
origin https://github.com/yanjundong/learnGit.git (push)
2)、推送分支
推送 dev
分支:
$ git push origin dev
哪些分支需要推送,哪些不需要呢?
master
分支是主分支,因此要时刻与远程同步;dev
分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;- bug分支只用于在本地修复bug,就没必要推到远程了
- feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
3)、抓取分支
8、Rebase
五、标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。
tag
就是个容易记的名字,它跟某个 commit
绑在一起。
1、创建标签
1)、最新提交
默认标签是打在最新提交的commit上的。
#先切换到需要打标签的分支上
$ git checkout master
#打一个新的标签
$ git tag V1.0
2)、过去提交
如果要对之前的某次提交打一个标签,就需要找到历史提交的commit id。
#git log查看历史记录
$ git log --pretty=oneline --abbrev-commit
12a631b (HEAD -> master, tag: v1.0, origin/master) merged bug fix 101
4c805e2 fix bug 101
e1e9c68 merge with no-ff
f52c633 add merge
cf810e4 conflict fixed
#对 `add merge` 这次提交打标签,它对应的commit id是f52c633,
$ git tag v0.9 f52c633
2、查看标签
用命令git tag
查看标签:
$ git tag
v0.9
v1.0
3、查看标签信息
用git show <tagname>
查看标签信息:
$ git show v0.1
4、推送标签到远程
如果要推送某个标签到远程,使用命令git push origin
:
$ git push origin v1.0
一次性推送全部尚未推送到远程的本地标签:
$ git push origin --tags
5、删除标签
如果创建的标签只存储杂气本地,那么打错的标签可以很快的从本地删除。
$ git tag -d v0.1
如果标签已经推送到远程,要删除远程标签就麻烦一点,
-
从本地删除
$ 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