git.md
Git 使用
诞生
-
BitMover公司收回Linux社区对BitKeeper的免费使用权。
-
Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了
分布式
- 集中式版本控制系统:版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。
- 分布式版本控制系统: 没有“中央服务器”,每个人的电脑上都是一个完整的版本库。分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。
创建版本库(respository)
mkdir learngit
cd learngit
git init
之后,会多出.git
目录
git add . # 将 . 文件夹添加到仓库
git commit # 提交至仓库
时光机穿梭
-
当前状态:
git status
-
查看更改具体内容:
git diff
版本回退
commit之后,相当于一个快照
-
查看commit历史:
git log
查看简略日志:
git log --pretty=oneline
当前版本用
HEAD
表示 -
版本回退:
git reset --hard HEAD^
# HEAD^表示当前版本的前一个版本再次返回当前版本,找到SHA1值
git reset --hard 70131
-
查找SHA1值:
git reflog
# reference logs
-
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD
指针,当你回退版本的时候,Git仅仅是把HEAD从指向改变,再把工作区的文件更新
工作区(Working Directory)
就是你在电脑里能看到的目录,比如我的learngit
文件夹就是一个工作区
版本库(Repository)
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master
,以及指向master
的一个指针叫HEAD
。
git add
实际就是把文件修改添加到暂存区stage
git commit
实际是将stage提交到当前分支master
管理修改
Git跟踪并管理的是修改,而非文件。
git diff HEAD
命令可以查看工作区和版本库里面最新版本的区别
撤销修改
-
还未提交到暂存区
最新的git合并了checkout和reset
原来的git checkout 可以使用 git restore 代替
原来的git reset HEAD 可以使用 git restore --staged 代替
git checkout -- readme.txt
命令git checkout -- readme.txt
意思就是,把readme.txt
文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt
自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt
已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit
或git add
时的状态。
git checkout -- file
命令中的--
很重要,没有--
,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到git checkout
命令。
-
已经提交到暂存区
git restore --staged index.html
或git reset HEAD index.html
-
已经提交到版本库
进行版本回退
删除文件
-
添加新文件
touch 1.txt && git add 1.txt
-
从版本库中删除该文件:
git rm test.txt
git rm file并且git commit并不是删除了版本库里的某个版本号,而是对工作目录下的删除操作进行了一个记录,会在仓库里生成一个新的版本号,在该版本下没有该文件。但是可以用git reset --hard commit_id进行版本回退,回退到有这个文件的版本号。而git checkout commit_id file命令是从含有该文件的旧版本号里把该文件拿出来,放到现版本里,版本号不改变。
远程管理
-
在github上新建仓库
-
git remote add origin git@github.com:michaelliao/learngit.git
-
git push -u origin master
,将本地的master
分支推送到远程origin
由于远程库是空的,我们第一次推送
master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。 -
之后提交:
git push origin master
-
下载:
git clone git://XXXXXXXXXX
分支管理
创建与合并分支
HEAD
严格来说不是指向提交,而是指向master
,master
才是指向提交的,所以,HEAD
指向的就是当前分支。
当我们创建新的分支,例如dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上:
从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支:
# 创建 dev 分支同时切换至dev, -b == branch + checkout
git checkout -b dev
# 或者
git switch -c dev
# 查看当前分支
git branch
# 切换分支
git checkout dev
# 或者
git switch dev
# 合并分支
git merge dev
# 删除分支
git branch -d dev
解决冲突
-
创建新分支
feature
git switch -c feature1
-
在新分支上修改:
git add . && git commit -m 'modified something'
-
切换
master
分支git switch master
-
修改文件:
git add . && git commit -m 'modified other somthing'
现在,master
分支和feature1
分支各自都分别有新的提交,变成了这样:
这种情况下,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.
现在,master
分支和feature1
分支变成了下图所示:
删除feature1
分支:$ git branch -d feature1
git log --graph
命令可以看到分支合并图。
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
合并分支时,加上--no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward
合并就看不出来曾经做过合并。
Bug分支
stash
可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:git stash
查看:git stash list
恢复:git stash apply && git stash drop
git stash pop
Git专门提供了一个cherry-pick
命令,让我们能复制一个特定的提交到当前分支:
$ git branch
* dev
master
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;
当手头工作没有完成时,先把工作现场git stash
一下,然后去修复bug,修复后,再git stash pop
,回到工作现场;
在master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick
命令,把bug提交的修改“复制”到当前分支,避免重复劳动
Feature分支
git switch -c feature-vulcan
git add vulcan.py
git status
git commit -m "add feature vulcan"
git switch dev
# 强制删除 feature-vulcan
git branch -D feature-vulcan
如果要丢弃一个没有被合并过的分支,可以通过git branch -D
强行删除。
多人协作
当你从远程仓库克隆时,实际上Git自动把本地的master
分支和远程的master
分支对应起来了,并且,远程仓库的默认名称是origin
。
-
查看远程库的信息:
git remote
,git remote -v
-
推送分支:
git push origin master
git push origin dev
推送:
master
分支是主分支,因此要时刻与远程同步;dev
分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;- bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
- feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
你的小伙伴要在dev
分支上开发,就必须创建远程origin
的dev
分支到本地,于是他用这个命令创建本地dev
分支:
$ git checkout -b dev origin/dev
碰巧你也对同样的文件作了修改,并试图推送:
$ cat env.txt
env
$ git add env.txt
$ git commit -m "add new env"
[dev 7bd91f1] add new env
1 file changed, 1 insertion(+)
create mode 100644 env.txt
$ git push origin dev
To github.com:michaelliao/learngit.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
解决:Git已经提示我们,先用git pull
把最新的提交从origin/dev
抓下来,然后,在本地合并,解决冲突,再推送:
git pull # 失败,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接
git branch --set-upstream-to=origin/dev dev
git pull
# 解决冲突
git push origin dev
因此,多人协作的工作模式通常是这样:
- 首先,可以试图用
git push origin
推送自己的修改; - 如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
试图合并; - 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用
git push origin
推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to origin/
。
这就是多人协作的工作模式,一旦熟悉了,就非常简单。
Rebase
标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。按照tag v1.2查找commit就行
创建标签
-
切换需要标签的分支:
git branch
git checkout master
-
打标签:
git tag v1.0
-
查看:
git tag
-
git tag v0.9 f7d08jjd
-
查看标签信息:
git show <tagname>
-
还可以创建带有说明的标签,用
-a
指定标签名,-m
指定说明文字:$ git tag -a v0.1 -m "version 0.1 released" 1094adb
操作标签
-
删除标签:
git tag -d v0.1
-
推送标签到远程:
git push origin v1.0
一次性推送某个标签到远程:
git push origin --tags
-
删除远程标签:
- 删除本地标签:
git tag -d v0.9
- 删除远程标签:
git push origin :refs/tags/v0.9
- 删除本地标签:
使用Github,Gitee
自定义Git
.gitignore
忽略文件的原则是:
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的
.class
文件; - 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
git check-ignore
命令检查.gitignore
配置别名
git config --global alias.st status
Git Cheat Sheet
Git
.gitnore # 忽略文件列表
git init
status
add filename
diff
reset
config
--global user.name "XXXXX"
--global user.email "XXXX"
commit -m "XXXXXXX"
rm --cached filename # 不再追踪
brach XXX # add 分支
checkout XXX # 转换到某个分支
merge XXX #合并
brach -d XXX
-D XXX # 强制删除
remote add origin XXX #远程推送
push --set-upstream
log [--prettty=oneline]
# 回退
|-----|--add--|-----|--commit--|-----|
time 1 2 3 4 5
1. 手动删除修改
git checkout --file filename
3. git reset HEAD filename
git checkout --file filename
5. 回退版本
# 恢复
git checkout --filename
git remote add origin "address"
push -u origin master
push orign master