Git 实际上包括两个仓库,一个是本地仓库,另一个是远程仓库。其中的 git add, git commit 等命令都是针对本地仓库的。这也就是说,你完全可以不采用 Github 以及 Gittee 之类的远程仓库来管理自己的软件。但是常用的方式还是将代码和远程仓库关联起来,这些关联需要用到 git push, git pull 之类的命令。
本地仓库管理
如果不需要和 Github 或者 Gittee 的远程仓库进行关联,那么可以按照下面的命令进行版本管理。
新建仓库
# 新建程序文件夹
mkdir MyProject
cd MyProject
# 新建本地仓库
git init
添加文件到仓库
假设新建了 videoProcess.py 文件
git add videoProcess.py
当然,可以同时添加多个文件:git add file1 file2 file3 ...
当然,也可以多次添加文件
提交文件到仓库
如上所示,已知 videoProcess.py 已经被添加到仓库了,如果要提交
git commit -m 'some messeage'
运行上面的命令,将会把所有 add 到仓库的文件一次性提交到仓库。-m
后面的信息可以是任意的,但是最好是一些和版本相关的信息,方便进行回溯:运行 git log
可以查看之前提交的版本,以及 commit 信息(-m
后的 messeage)
add 命令将工作区的文件添加到暂存区(stage),commit 命令实现最终的暂存区到版本区提交。
文件状态查询
文件是否被修改,是否被新建,是否添加到了暂存区,是否被提交,是否被跟踪表示了文件的状态。
git status
工作区和版本库文件差异对比
git diff HEAD -- videoProcess.py
撤销修改
撤销修改
- 用暂存区或者版本库里面的文件来替换当前工作区的文件,从而撤销对当前文件的一些修改。
- 撤销暂存区的文件
第一种情况:
git checkout -- videoProcess.py
第二种情况:
git reset HEAD videoProcess.py
实现将添加到暂存区的文件,git reset
命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用 HEAD
时,表示最新的版本。
- reset 就相当于一次重置操作,类似于电脑重启。它会清空暂存区的信息(貌似这里会回退到工作区)
其中的
--
很重要,不加就是分支操作
删除文件
本地新建文件和删除文件,都会被 git 记录,工作区新建只需要 git add
和 git commit
就可以实现工作区和版本库的统一了。如果本地删除文件的话,那么如果需要版本库和工作区文件统一,就需要
git rm filename
git commit -m "remove filename"
如果是误删文件,那么可以利用上面所说的撤销修改命令 git checkout
从版本库或者暂存区进行文件的恢复
回退版本
每一次 commit 就是一次版本的更新,对于版本的倒退、前进等操作类似一个指针的操作。HEAD 表示当前版本,HEAD^ 表示上一个版本,HEAD^^ 表示上上一个版本,以此类推。当然如果回溯版本太多也可以用 HEAD~100(回退100)之类的简写
git reset --hard HEAD^
reset 命令将版本指针移动实现版本的切换。调用上述的回退指令之后,再次调用 git log 将发现最近一次的 commit 记录已经消失。
hard 的作用
版本前进
git 可以实现版本的回退,当然也可以实现版本的前进,只需要 reset 版本指针即可。每一个版本,都有一个唯一的 commit id 用来作为版本的标识,例如(调用 git reflog
来查看每一次 commit 信息,即使版本回退,也可以查到之前 commit 的 id):
图中,黄色十六进制数字就是每一次 commit 的 id,借助这个 id 可以实现版本的任意跳转(不同的 id)
git reset --hard a9d51c4
运行上述命令,版本将会变为最后一次 commit 的版本,也即是实现了版本的前进(相对于之前回退来说)
远程仓库关联
假设远程仓库和本地以及本地 git 的 SSH Key 公钥以及配置好。
那么在 Github 或者 Gittee 新建一个 repository,之后将本地的仓库与之关联即可。
本地和远程关联
采用 git remote add
命令
git remote add origin git@github.com:yangyangtaotao/test.git
origin 是远程仓库的名称,这是默认名称
本地文件推送到远程库
git push -u origin master
由于远程库是空的,我们第一次推送 master 分支时,加上了 -u
参数,Git 不但会把本地的 master 分支内容推送的远程新的 master 分支,还会把本地的 master 分支和远程的 master 分支关联起来,在以后的推送或者拉取时就可以简化命令(省略 -u)。
查看远程库信息
git remote -v
远程库为 origin
删除远程库
要删除 orgin 库的话
git remote rm origin
此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。要真正删除远程库,需要登录到GitHub,在后台页面找到删除按钮再删除。
远程克隆
这是应用最为广泛的一条命令之一
git clone git@github.com:yangyangtaotao/test.git
分支管理
分支创建与合并
在 版本回退 里,你已经知道,每次提交,Git 都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在 Git 里,这个分支叫主分支,即 master 分支。HEAD 严格来说不是指向提交,而是指向master,master 才是指向提交的,所以,HEAD指向的就是当前分支。
新建分支
git checkout -b dev
git checkout 命令加上 -b 参数表示创建并切换,相当于以下两条命令:
git branch dev
git checkout dev
git checkout
都有一种从非工作区进行信息提取的感觉(?)
由于 git checkout 前面可以用于对文件的操作,所以容易混淆,可以采用下面的代码实现同样的功能
git switch -c dev
-c 有 create 之意,切换已有分支 git switch dev
查看当前分支
git branch
分支合并
git checkout master
git merge dev
将 dev 分支合并到了当前分支(master 分支)
Git 怎么合并呢?最简单的方法,就是直接把 master 指向 dev 的当前提交,就完成了合并,这就是 fast forward 模式罗?
删除分支
git branch -d dev
-d 参数表示删除之意
解决冲突
例如 master 和 feature1 分支中的相同文件存在不同之处,就会产生冲突。因为合并的时候,无法确定以哪个文件作为标准。在这种情况下需要手动修改冲突的文件(合并命令之后冲突地方将会被标注出来),然后进行提交。提交之后上面的分支将变为:
git log --graph
可以查看分支图
分支管理
通常,合并分支时,如果可能,Git 会用 Fast forward 模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用 Fast forward 模式,Git 就会在 merge 时生成一个新的 commit,这样,从分支历史上就可以看出分支信息。
git merge --no-ff -m "merge with no-ff" dev
禁止 Fast forward 模式:
--no-ff
需要 -m 实现一次 commit
和前面图对比?(当然不能和分支冲突解决那个图对比)
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理(多分支):
- 首先,master 分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
- 干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
- 你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
这些分支都是本地的版本库,团队开发肯定需要借助远程库作为中转的。那么远程库如何中转呢?
Bug 分支
处理 bug 的通常流程是:新建一个处理 bug 的分支,将当前尚未完成的工作隐藏(因为当前分支上面还有没有完成的工作,而且还没有提交,如果直接切换分支,之后切回来之后没有提交的信息将丢失),然后切换到 bug 分支完成 bug 修复,最后恢复之前没有完成的工作。
由此可见,分支切换,是从版本区进行分支的恢复(会丢失没有提交的信息,所以完成的工作需要及时提交)。文件恢复同样类似,所以二者才会采用相同的命令
git checkout
隐藏工作现场
git stash
修复 bug 时,先要确定在哪个分支进行 bug 的修复。确定之后在该分支新建一个 bug 分支即可,修复完之后与原分支合并。
查看被隐藏的现场
git stash list
恢复隐藏现场
git stash pop
# 等价于
# git stash apply
# git stash drop
git stash apply
不会删除 stash 内容,需要用git stash drop
来进行删除
复制特定提交到当前分支
git cherry-pick 4c805e2 # 4c805e2 是修改 bug 分支提交的 commit id
运行这个命令,会自动进行一次 commit 提交操作
多人协作
创建远程库的分支
要在 dev 分支上开发,就必须创建远程 origin 的 dev 分支到本地,于是用这个命令创建本地 dev 分支
git checkout -b dev origin/dev
推送其它分支到远程库
git push origin dev
这是需要首先创建远程库的分支,然后才能这样推送; 貌似我这边测试,直接新建 dev 分支之后,直接就可以推送 dev 分支到远程库
多分支 pull
如果别人更新了远程库,你如果需要更新需要首先从远程库进行 pull 抓取更新,然后在本地解决冲突合并,然后在 push 到远程库。如果存在多个分支的话,不能直接利用 git pull
进行分支的抓取,需要采用
git branch --set-upstream-to=origin/dev dev
# 如果是更新 master 的话:git branch --set-upstream-to=origin/master master
直接 pull 不成功原因是没有指定本地 dev 分支与远程 origin/dev 分支的链接,根据提示,采用上面大的命令设置 dev 和origin/dev 的链接。
由此观之,pull 的话不是直接将整个远程库的东西全抓取过来,它是根据分支进行抓取的。并且我们 push 的时候也是按分支进行 push。如果说远程库也具有版本的记忆能力的话,那么它记忆的最多是每次 push 的版本。
因此,多人协作的工作模式通常是这样:
- 首先,可以试图用
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>
。
这就是多人协作的工作模式,一旦熟悉了,就非常简单。
由此观之,貌似是这样的:只有本地的版本库能够保存诸多 commit 之后的版本。远程库可以共享本地的多个分支,但是每个分支只能保存最新的版本。所以版本的控制貌似主要是针对于本地的版本库,远程的只能保存最新的???