一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情。
git的优势
svn称为集中式版本控制系统,它是存在不足的,只有服务器存在一套完整的版本历史,在个人电脑上是没有的。如果要查看版本历史就必须要客户端连接服务器,所以后来诞生了分布式的版本控制系统。
git的优势
- 服务端和客户端都存在完整的版本库
- 脱离服务端,客户端照样可以管理版本
- 查看历史和版本比较等多数操作都不需要访问服务器
git基本配置
配置user是每次提交的时候git会自动记录他们,方便出现问题git会发送邮件等。
git confit --global user.name 'pcj'
git config --global user.email 'email@163.com'
config有三个作用域:
- local:只对某个仓库有效
- global:对当前用户所有仓库都有效
- system:对系统所有登录的用户有效
几个概念
- 工作目录:就是你写代码的文件夹
- 暂存区:就是你写好一个文件,执行git add,那么这个文件就被git放在暂存区,即被git管理了
- 版本库:版本库也叫版本历史,就是你本地提交的版本commit,git会在本地维护一个版本历史,而无需向远端的服务器请求,相关的信息存放在.git文件夹里面。
- remote服务器:就是把本地的版本库推送到服务器,让服务器也管理一份
.git目录
git可以离线在本地进行版本管理,这里面的核心就是.git文件的作用了。.git文件夹下面的目录有如下几个:
- HEAD: 指向当前的工作路径,也就是在哪个分支上
- config: 存放本地仓库(local)相关的配置信息
- refs/heads: 存放分支
- refs/tags: 存放tag,又叫里程牌 (当这次commit是具有里程碑意义的 比如项目1.0的时候 就可以打tag)
- objects: 存放对象 .git/objects/ 每次commit,git 都会将当前项目的所有文件夹及文件快照保存到objects目录
分离头指针(detached HEAD)
通常,我们工作在某一个分支上,比如 master 分支。这个时候 master 指针和 HEAD 指针是一起前进的,每做一次提交,这两个指针就会一起向前挪一步。但是在某种情况下(例如 checkout 了某个具体的 commit),master指针和HEAD指针这种「绑定」的状态就被打破了,变成了分离头指针状态。
场景:如果临时想基于某个commit做变更,试试新方案是否可行,就可以采用分离头指针的方式。测试后发现新方案不成熟,直接git reset --hard HEAD 或者切换分支<git checkout master>即可,省却了建、删分支的麻烦了。
对于某个commit进行修改,使用如下命令(这个命令是切换分支),也可以切换commit id:
git checkout ads3648df
如果对某个文件做了修改后,直接切换到别的分支上了git checkout master,那么这次修改会丢失。如果想要保存这次的修改,就可以通过git branch -b
<branchname>,新建一个分支。
理解HEAD和branch
HEAD和branch的关系就是,HEAD不仅可以指向某个分支,还可以指向某个commit,即分离头状态。
git重要命令
git branch
查看分支
git branch 不带参数:列出本地已经存在的分支,并且在当前分支的前面用"*"标记
git branch -av 查看所有分支列表,包括本地和远程
创建分支
git branch dev 创建名为dev的分支,创建分支但依然停留在当前分支
git checkout master 将分支切换到master
git checkout -b master 如果分支存在则只切换分支,若不存在则创建并切换到master分支
- branch是用来查看或创建分支的
- checkout是用来切换分支的 把branch checkout一起来执行 git checkout -b newMaster 就是创建新分支并切换到新分支上。
本地分支和远程分支的跟踪track
如果你的分支是从远程分支创建的
从远程分支分出来的分支(分支名要与远程分支名相同)都是跟踪分支(track) ,当对该分支进行push和pull时,如果该分支和远程分支同名,git会知道推送到远程哪个分支,从哪个远程分支同步到本地分支。这个时候本地分支和远程分支是跟踪的
如果本地新建了一个分支branch_name,但是在远程没有
如果本地新建了一个分支branch_name,但是在远程没有,这时候push和pull指令就无法确定该跟踪谁,一般来说我们都会使其跟踪远程同名分支,所以可以利用git push --set-upstream origin branch_name,这样就可以自动在远程创建一个branch_name分支,然后本地分支会track该分支。后面再对该分支使用push和pull就自动同步。无需再指定分支。
创建分支并push到远程
# 根据master分支创建dev分支
git branch dev master
# dev分支推动到远程的dev分支,并绑定跟踪
git push -u origin dev
删除分支
git branch -d branch_name:使用-d 在删除前Git会判断在该分支上开发的功能是否被merge的其它分支。如果没有,不能删除。如果merge到其它分支,但之后又在其上做了开发,使用-d还是不能删除。如果强制删除就使用 -D
// 删除本地分支
git branch -d branchname
// 强制删除分支
git branch -D branchname
// 然后删除远程分支
git push --delete origin branchname
删除远程分支后,本地分支仍然有remote/origin/dev这样的分支,执行以下命令:
git remote prune origin // prune是修剪的意思
git merge
git merge 是 Git 中用于将两个分支的历史合并在一起的命令,它通常用于将一个分支的更改合并到当前分支中。
假设你有一个 main 分支和一个 feature-branch 分支,你想将 feature-branch 的更改合并到 main 分支:
切换到目标分支
git checkout main
执行合并
git merge feature-branch
在合并过程中,如果两个分支对同一部分代码进行了不同的修改,Git 会提示冲突。你需要手动解决这些冲突:
1. 终止合并
git merge --abort
2. 查看冲突文件:Git 会标记出冲突的部分
3. 编辑文件:手动编辑文件,解决冲突
4. 标记冲突已解决:使用 git add 命令标记冲突已解决
5. 完成合并:git commit 提交合并结果。
git pull
git pull 其实就是 git fetch 和 git merge 的简写:
git pull <远程主机名> <远程分支名>:<本地分支名>
git fetch是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。
git fetch <远程主机名> <分支名>
git merge首先切换到要合并的分支,比如现在要把dev分支合并到master分支,那么首先要切换到master分支上,然后执行:
git merge dev
如果远程有人修改了同一分支的内容,我们要git push之前需要执行git pull,但是此时我们执行git log时会出现一个Merge branch 'master' of github.com:hello/demo的commit信息,这个对于我们来说是无用的,那么怎么解决呢?加上--rebase配置即可。
git pull --rebase
【注意】在执行git pull时,如果本地有文件没有执行git add .,此时执行会失败,但是此时又不想丢弃已经改的内容,那么可以使用git stash:
# 把本地发生改动的文件贮藏一下
$ git stash // stash是存储的意思
# 把远程最新的 commit 以变基的方式同步到本地
$ git pull --rebase
# 把本地的 commit 推送到远程
$ git push
# 把本地贮藏的文件弹出,继续修改
$ git stash pop
git remote
首先通过git remote -v,来查看是否有关联到远程备份。如果没有则添加远程:
git remote add origin(可以随意命名,一般是origin) git@gitee.com:pengchangjun/git-test.git
如果添加了远程,如何删除:
git remote remove origin
git reset
比如我先修改了文件,并且执行了git add和git commit,但是现在我想要回退到上一个版本,可以执行:
git reset --hard HEAD^
git reset: 用respository中特定commit来重置head下的repository、stage、workspace。
git reset 实际上就是在修改head指向的commit,所以相当于会将head指向repository中特定的commit。同时根据修改head后,是否级联修改stage、workspace,可进一步的细分成三种模式hard、sort、mixed(默认),如下图所示:
- hard: 会同时重置repository、stage、workspace
- soft: 会重置repository,但保留stage、workspace中的改动
- mixed: 会重置repository、stage,只保留workspace中的改动
比如我现在修改了工作目录(也就是还没有执行git add),但是发现修改的不对,想要暂存区里面的代码覆盖工作目录的代码,可以执行:
git checkout .
checkout不仅有切换分支的意思,还有检出文件的意思,当带具体的文件路径时,从指定的respository 或者 stage 中恢复文件。
git stash
场景1:目前在工作区还在进行修改,如果此时需要在当前文件下进行紧急修复bug,就需要把工作区正在修改的文件stash暂存起来,进行bug修复工作,在完成bug修复工作提交commit后,将暂存的工作区文件内容拿出来继续工作,相当于是返回到自己上一个commit。
场景2:我在本地修改好后,发现远程分支已经被改动了,此时我本地也被改动了就造成了冲突,无法push或者pull。此时,就可以用 git stash 来处理。
// 把本地的改动暂存起来
git stash
// 拉取远程分支(此时本地分支会回滚到上次commit的情况,你的改动都存在stash中)
git pull
// 将stash中改动重新加回本地分支,就可以继续修改了,当然,如果改好了就是add,commit,push git stash pop
场景3:如果忘记切换,将代码写错了分支,直接在 master 分支上做改动,这里假设我的分支是 feature/category_vechice 分支
// 把本地当前改动暂存起来,此时master分支就恢复到了上次拉取时的状态
git stash
// 切换到需要改动的分支 git checkout test
// 将改动pop到自己当前的分支
git stash pop
stash 就是一个栈,平时我们把需要暂存的文件存到栈中,把代码恢复到上次拉取的状态以进行操作。
git tag
创建 tag 是基于本地分支的 commit,而且与分支的推送是两回事,就是说分支已经推送到远程了,但是你的 tag 并没有,如果把 tag 推送到远程分支上,需要另外执行 tag 的推送命令。
git tag -a v1.0.0 -m '测试完成' // 创建本地tag
git push --tags // 推送到远程仓库