git是一个分布式
版本控制系统,这意味着每个人的电脑上都是一个完整的版本库,我们不再需要将代码上传至”中央服务器上“,每个人电脑里都有完整的版本库,某一个人的电脑坏掉了问题不大,从其他人那里复制一个就可以了。
//因为Git是分布式版本控制系统,所以每个机器都必须自报家门:你的名字和Email地址
$ git config --global user.name "Your Name"
$ git config --global user.email "[email protected]"
注意:git config
命令的--global
参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
mkdir Ryuko_learn_git
cd Ryuko_learn_git
# 初始化本地仓库
git init
此时文件夹内会创建一个空的仓库,并且目录下多了一个.git
的隐藏目录,这个目录是Git来跟踪管理版本库的。
git status
命令可以让我们时刻掌握仓库当前的状态。
git add
可以把文件从工作区(编写代码的地方)添加到暂存区。
`git commit -m 'message’可以把文件从暂存区提交到本地仓库。
-m
后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
git remote add origin https://github.com/..
表示添加远端仓库到本地,这句话的意思是将后面的仓库地址保存到本地仓库,origin
是给这个地址起的别名。
git push --set-upstream origin master:master
的意思是,push到名字为origin
的远程仓库,这个名字指的是我们在1.3.4中为仓库起的别名。
master:master的意思是把本地仓库的master(左)分支推到远程仓库的master分支(右),因此如果是 git push --set-upstream origin a:b
意思是把本地的a分支推到远程的b分支;–set-upstream 可以记住本次推送,下次推送可以直接选择 git push。
本节开头的例子可以简写为:git push -u origin master
首先在github上面创建一个仓库,然后建立本地和远程仓库的连接。
我用的是以前的项目来举例,所以就不再介绍如何建立连接了。
下面进入正文。
我们在刚刚拿到团队仓库中的代码后进行编码操作,新建文件夹的时候会发现,这个文件的尾部会有一个绿色的U,将a.js的状态表示为U (untracked)表示当前文件还没有加入到版本库(版本库不会记录当前文件的提交和修改)
由于版本库不会记录提交和修改,所以我们在这个文件中无论如何输入代码,都不会改变它(untracked)的状态
此时当我们使用 git add a.js之后,a.js状态为A(added),表示当前文件加入到暂存区
当我们修改a.js之后,a.js状态为M (modified),表示当前文件被修改了
当文件修改之后,仍然需要调用git add命令将修改的东西提交到暂存区(此时文件状态变为A)
我们可以使用git commit
命令将暂存区的修改提交到分支
当出现冲突的时候,状态为c (conflict),此时我们要解决冲突,不过这两点我放在后面介绍。
通过git log
可以看到每一次commit
提交后的版本信息
通过git log --pretty=oneline
可以将上述信息压缩为一行展示:
在上一节的图片中有这样一句话HEAD -> master
,在git中通过git reset --hard [回退级数]
进行版本回退
HEAD
表示当前版本HEAD^
表示上一个版本HEAD~100
表示往上100个版本 在这里通过git reset --hard HEAD~2
回到两个版本以前,也就是删除文件 README.en.md这里:
由于当时我的项目中只有README文件,所以版本回退时,工作区的内容只显示当时存在的文件,其他的文件(后续提交中编写的文件)由于当时并不存在,所以并不显示出来:
git reset
一共有三个参数:
在我们回退到以前版本的时候,回退操作倒是成功实现了,可是git log
打印提交信息的时候发现,后续的版本看不到了!(见2.3中图1),如果我们想要回到最新版本该怎么办呢?
git提供了一个命令git reflog
用来记录你的每一次命令:
使用下面命令就可以指定回到未来的某个版本:
git reset --hard b56a
// 版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
现在成功回到最新版本:
git的版本回退速度非常快,因为git在内部有个指向当前版本的 HEAD
指针,当你回退版本的时候,git仅仅是把HEAD的指向改变了。现在回到工作区看看,最新的代码已经回来了!
在前面的章节中,我用上了工作区
、版本库
、暂存区
等概念,现在整体介绍一下。
指在电脑里能看到的目录,平时你通过vscode打开某个文件夹写代码,此时的代码就写在工作区中。
工作区有一个隐藏目录 .git
,这个不算工作区,而是Git的版本库。
git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master
,以及指向master
的一个指针叫HEAD
。
修改a.js,然后通过git status
看一下状态:
使用git add .
之后再看看状态:
现在a.js文件被添加到了缓存区(stage)中,之后通过git commit
就可以一次性把暂存区的所有修改提交到分支。
一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:
继续修改a.js,当我们准备提交代码的时候,发现其中某行代码发生了错误,想要撤回到之前没有修改过的状态。
我们使用 git status
命令查看一下:
你可以发现,git提供了一个指令:git restore
可以丢弃工作区的修改。(也可以使用git checkout -- file
丢弃修改)
git checkout -- a.js
意思就是,把a.js
文件在工作区的修改全部撤销,这里有两种情况:
一种是a.js
自修改后还没有被放到暂存区,现在撤销修改就回到和版本库一模一样的状态;
一种是a.js
已经添加到暂存区后同时作了修改,现在撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次 git commit
或git add
时的状态。
假如我们在a.js中做了修改,并且使用 git add
到暂存区了,接下来我们使用 git status
查看一下,修改添加到了暂存区,还没有commit。接下来Git告诉我们,用命令 git git restore --staged
可以把暂存区的修改撤销掉(unstage),重新放回工作区。
要从版本库中删除该文件,那就用命令git rm
删掉,并且git commit
:
git rm a.js
git commit -m "remove a.js"
现在,文件就从版本库中被删除了。
另一种情况是删错了,没有commit,此时版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
git reset
git checkout -- a.js
git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样既安全,又不影响别人工作。
首先,我们创建dev
分支,然后切换到dev
分支:
// git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:
// git branch dev
// git checkout dev
git checkout -b dev
然后,用 git branch
命令查.看当前分支:
// git branch命令会列出所有分支,当前分支前面会标一个*号
git branch
// 输出
* dev
master
然后我们就可以在dev
分支上正常提交,需要注意的是,此时如果切回到master
分支:
git checkout master
切回到master
分支后,会发现刚刚在dev
分支修改的内容消失掉了,因为那个提交是在dev
分支上,而master
分支此刻的提交点并没有变。现在我们将dev
分支上的内容合并到maste
分支上:
git merge dev
此时在master
分支中成功合并了dev
分支中的修改内容。
产生冲突问题的根本原因在于多个人同时修改了同一个文件。
我们在使用版本控制工具管理源代码的时候,冲突问题不可避免。要解决冲突问题的最好方案就是多个人不要同时修改同一个文件(分工明确)。
<<<<<<< HEAD
console.log("hello world")
=======
console.log("Ryuko_黑猫几绛")
>>>>>>> 0755800755f210e82e4d3f364760a7fb6922846c
出现了冲突问题之后,我们只要把<<<<< ==== >>>>>删掉,保留正确代码,再add commit pull push就可以了
git push origin dev2 //向服务器推送dev2分支
git checkout -b dev3 origin/dev2 //从服务器抓取dev2分支 放在本地dev3分支上
还有一种可能是分支推送冲突,比如:
你的小伙伴已经向 origin/dev
分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:
// 修改dev分支下的c.js
console.log("张三修改dev分支下的c.js")
// 向dev分支提交修改
git add a.js
git commit -m "张三修改dev分支下的c.js"
// 推送到远程仓库的dev分支
$ git push origin dev
// 推送报错
To github.com:mazg1987/learngit.git
! [rejected] dev -> dev (fetch first)
error: failed to push some refs to 'git@github.com:mazg1987/learngit.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., '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
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:mazg1987/learngit
5d981b2..5f6d427 dev -> origin/dev
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> dev
// 如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to origin/
在项目目录下新建一个 .gitignore文件,在改文件中添加 不需要上传到服务器文件和文件夹,模板例如:
.DS_Store
node_modules/
dist/
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
# local env files
.env.local
.env.*.local