按实例的方式进行学习。
概念
Git 本地数据管理,大概可以分为三个区:
工作区(Working Directory):电脑里能看到的目录,例如下面例子中的D:/learngit
就是一个工作区
版本库(commit History):存放已经提交的数据。工作区有一个隐藏目录.git
,这个不算工作区,而是Git的版本库。
暂存区(Stage/Index):数据暂时存放的区域。Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
工作区的文件 git add 后到暂存区,暂存区的文件 git commit 后到版本库。
创建、提交、撤销
本地已创建了项目文件夹(D:/learngit),现在需要将此项目纳入到git版本管理
创建git版本库
cd d:/learngit
git init
通过git init
命令把D:/learngit
目录变成Git可以管理的仓库,文件夹下将会产生一个.git
文件夹
添加文件
touch index.php
查看状态
git status
我们对文件的各种操作新建、编辑(写代码)都是在工作区
完成的,但是工作区的文件还是不被Git所管理的,Git会告诉你index.php
是未被追踪的文件,需要执行git add 文件名
把index.php提交到暂存区
以便纳入到Git版本管理中来。
在git中,文件的状态有4种:Untracked(未跟踪)、Staged(暂存状态)、Unmodify(文件已经入库,未修改)、Modified(文件已修改)
- Untracked: 未跟踪
此文件在文件夹中, 但并没有加入到git库, 不参与版本控制。 -
Staged: 已暂存
执行git add 将文件状态变为Staged。执行git rm –cached将文件状态变回Untracked。执行git commit则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为Unmodify状态. -
Unmodify 文件已经入库, 未修改
执行git commit将文件状态变为Unmodify. 这种类型的文件有两种去处, 如果它被修改, 则变为Modified. 如果使用git rm移出版本库, 则成为Untracked文件 -
Modified: 文件已修改
仅仅是修改, 并没有进行其他的操作. 这个文件也有两个去处, 通过git add可进入暂存staged状态, 使用git restore 丢弃修改返回到unmodify状态。在git add后执行git reset HEAD filename取消暂存, 文件状态变回Modified
把文件存入暂存区
git add .
用命令git add
告诉Git,把文件存入暂存区。
此时index.php
文件的状态是已暂存
。
如果我们想把现在的暂存撤销,可以使用git rm --cached index.php
命令来撤销,如果想提交到版本库,就再执行git commit
操作就可以了。
如果add了多个文件,想一次性撤销,可以使用git rm --cached -r ./
(./
代表当前文件夹)
将暂存区的文件提交到版本库
git commit -m '说明'
提交后,此时index.php
文件的状态是已提交
。
现在修改index.php
文件,打开index.php,填写如下内容:
<?php
phpinfo();
此时查看状态可以看到index.php文件处于已修改
状态:
index.php被修改后,可以把工作区修改的文件git add
提交到暂存区,也可以使用git restore --staged index.php
把暂存区的文件从暂存区撤出,然后使用git restore index.php
把工作区的修改撤销,这样,文件就会回退到上一次提交时的状态。
如果有多个工作区的文件想撤销可以使用git restore ./
如果使用git commit提交了此次修改后,想回退至上一个版本,那么可以使用:
git reset --hard HEAD^
小结
初始化一个Git仓库,使用git init
命令。
添加文件到Git仓库,分两步:
1、使用命令git add <file>
,注意,可反复多次使用,添加多个文件;
2、使用命令git commit -m <message>
,完成。
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git restore <file>
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>
或git restore --staged <file>
,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,用命令git reset --hard HEAD^
。
版本回退
假如工作区中有一个test.txt文件,该文件的内容如下:
111
222
333
444
该文件一共提交了5次,第一次是提交了一个空的test.txt文件,后面4次,分别为提交了111、222、333、444这4行数字。
现在如果想回退到
111
222
这个版本,那么首先通过git log查看日志:
找到这个版本对应的版本号(commit id)。
然后执行:
git reset --hard db077
db077
为版本号的前几位,没必要写全,前几位就可以了,Git会自动去找。
执行完以上命令后,打开test.txt文件,内容已恢复到111、222的版本。
此时如果想回退至111、222、333这个版本,该如何操作呢?
此时通过git log
已经不能看到333、444这两个版本了:
也就意味你回退到了某个版本,又后悔了,想恢复到新版本怎么办?找不到新版本的commit id怎么办?
Git提供了一个命令git reflog
用来记录你的每一次命令:
通过该命令可以查看到333这个版本的版本号了。
小结
HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id
。
穿梭前,用git log
可以查看提交历史,以便确定要回退到哪个版本。
要重返未来,用git reflog
查看命令历史,以便确定要回到未来的哪个版本。
删除文件
直接在工作区中删除了文件,通过git status查看状态如下:
现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm
删掉,并且git commit
。
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
git restore <file>
小结
命令git rm
用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
远程仓库
添加远程库
如果你希望把你本地的git仓库同步到Github或GitLab上,那么就需要添加远程库。
首先在Github上新建一个learngit
的库。
然后在本地关联远程库:
git remote add origin https://github.com/dedemao/learngit.git
如果是gitlab:
git remote add origin ssh://git@git.884358.com:211/884358/learngit.git
其中211是ssh的端口号(一般默认为22)
从远程仓库拉取
git pull origin main
推送到远程库
git push origin main
从远程库克隆
现在,假设我们从零开发,那么最好的方式是先创建远程库,然后,从远程库克隆。
git clone https://github.com/dedemao/learngit.git
查看远程库信息
git remote -v
移除远程仓库
git remote remove origin
分支管理
查看分支
git branch
创建分支
git branch <name>
切换分支
git checkout <name>
或
git switch <name>
创建并切换分支
git checkout -b <name>
或
git switch -c <name>
合并某分支到当前分支
git merge <name>
删除分支
删除本地分支:
git branch -d <name>
删除远程分支:
git push origin --delete <name>
实战
1、创建并切换到dev分支
git switch -c dev
2、修改文件并提交
例如修改README.md文件内容
git add README.md
git commit -m "update README.md"
3、切换回main分支
git checkout main
4、把dev分支的工作成果合并到master分支上
git merge dev
5、删除dev分支
git branch -d dev
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。
解决冲突
在合并分支时,可能会造成冲突,如图:
只需打开冲突文件,Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,只需要根据标记修改后,重新提交即可。
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。
用git log --graph
命令可以看到分支合并图。
分支管理策略
通常,合并分支时,如果可能,Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
git merge --no-ff -m "merge with no-ff" dev
Bug分支
场景:当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交,并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
幸好,Git还提供了一个stash
功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:
git stash
现在,用git status
查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。
首先确定要在哪个分支上修复bug,假定需要在main
分支上修复,就从main
创建临时分支:
git switch main
git switch -c issue-101
修复bug后提交:
git add .
git commit -m "fix bug 101"
修复完成后,切换到main分支,并完成合并,最后删除issue-101分支:
git switch main
git merge --no-ff -m "merged bug fix 101" issue-101
现在,是时候接着回到dev分支干活了!
$ git switch dev
Switched to branch 'dev'
$ git status
On branch dev
nothing to commit, working tree clean
工作区是干净的,刚才的工作现场存到哪去了?用git stash list
命令看看:
$ git stash list
stash@{0}: WIP on dev: f52c633 add merge
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
一是用git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用git stash drop
来删除;
另一种方式是用git stash pop
,恢复的同时把stash内容也删了;
这里使用git stash pop
再用git stash list
查看,就看不到任何stash内容了。
你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:
$ git stash apply stash@{0}
在master分支上修复了bug后,我们要想一想,dev分支是早期从main分支分出来的,所以,这个bug其实在当前dev分支上也存在。
那怎么在dev分支上修复同样的bug?重复操作一次,提交不就行了?
有木有更简单的方法?
有!
同样的bug,要在dev上修复,我们只需要把4c805e2 fix bug 101
这个提交所做的修改“复制”到dev分支。注意:我们只想复制4c805e2 fix bug 101
这个提交所做的修改,并不是把整个main分支merge过来。
为了方便操作,Git专门提供了一个cherry-pick
命令,让我们能复制一个特定的提交到当前分支:
$ git branch
* dev
master
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
Git自动给dev分支做了一次提交,注意这次提交的commit是1d4b803,它并不同于main的4c805e2,因为这两个commit只是改动相同,但确实是两个不同的commit。用git cherry-pick,我们就不需要在dev分支上手动再把修bug的过程重复一遍。
小结
修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;
当手头工作没有完成时,先把工作现场git stash
一下,然后去修复bug,修复后,再git stash pop
,回到工作现场;
在main分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick <commit>
命令,把bug提交的修改“复制”到当前分支,避免重复劳动。
多人协作
1、test用户克隆远程仓库
ssh://git@git.884358.com:211/884358/learngit.git
2、test用户创建本地dev
分支
git switch -c dev origin/dev
3、在dev
分支上提交代码
git add .
git commit -m "test commit"
git push origin dev
因此,多人协作的工作模式通常是这样:
首先,可以试图用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>
。
这就是多人协作的工作模式,一旦熟悉了,就非常简单。
小结
查看远程库信息,使用git remote -v
;
本地新建的分支如果不推送到远程,对其他人就是不可见的;
从本地推送分支,使用git push origin branch-name
,如果推送失败,先用git pull
抓取远程的新提交;
在本地创建和远程分支对应的分支,使用git switch -c branch-name origin/branch-name
,本地和远程分支的名称最好一致;
从远程抓取分支,使用git pull
,如果有冲突,要先处理冲突。