最近公司代码管控比较乱,有天就利用分支进行了代码梳理,当时就遇到一些困惑,抽空就把git
再学了一下。
以前我是用
git
命令的,但是敲命令对我来说还是有点麻烦(主要是git 功力不够),看到有同事用git桌面版(毕竟git官方的亲儿子)我也就跟着用了,不得不说真香,不过也有些限制,。当然现在很多IDE也都支持Git,并且也很强大,比如微软的 VSCode 对吧,推荐大家使用起来。这篇文章拖了很久,最近整理基础时索性就把这篇完善了。
几个概念:
- 版本库:git在本地开辟的一个存储空间,一般在 .git 文件里。
- 工作区(workspace): 就是编辑器里面的代码,我们平常开发直接操作的就是工作区。
- 暂存区(index/stage):暂时存放文件的地方,
git add
后也就将工作区代码放到了暂存区(缓冲区)。 - 本地仓库(Repository):
git commit
后就是将暂存区的代码放到本地仓库。 - 远程仓库(Remote):线上代码存放的地方,如 github/gitee。
资料均来源于网络,文章主要是自己的的一些操作实践和理解。行文如果 有误还希望评论指出,共同交流
四种状态
先安装个 Git
(Git - Downloads)
git --version # 看看Git 当前版本
git update-git-for-windows # windows 更新
菜鸟教程有个 (图解 Git | 菜鸟教程) 感觉对某些命令的描述还挺直观的。
当然最好的文档就是官方文档 Git - Reference,有啥疑惑就往这看就行~~
常用命令
\
基础命令
公钥生成
ssh-keygen -t rsa -C 'your-emial-dress' # 生成公钥
cd ~/.ssh # 切换该目录
cat id_rsa.pub # 查看公钥内容
然后在 同性交友网站github settings --> ssh
公钥处粘贴即可。
git help
查看某条命令
初始化(clone/init)
git clone # 克隆远程仓库到本地(新建一个与线上仓库同名的文件夹),仅克隆默认分支的代码,
git clone # 克隆远程仓库到本地,指定本地文件夹名 directory
git clone -b # 克隆远程仓库(的指定分支branch)到本地
git init # 本地新建/初始化(如果存在)一个git仓库,根目录创建 .git 文件,即版本库
git init --bare # 生成裸库,没有 .git 文件只有一些历史版本的记录文件,让源代码与版本库分离
git init 命令创建一个空的Git存储库 - 本质上是一个 .git
目录,其中包含 objects
、refs/heads
、refs/tags
和模板文件的子目录。还将创建一个引用master分支 HEAD 的初始 HEAD
文件
配置
--global
就是全局配置(针对所有仓库)
cat .git/config # 本仓库查看配置文件
cat ~/.gitconfig # 全局配置文件查看
git config --list # 查看配置列表 简写 -l
git config --global --list # 查看全局配置列表
git config [--local|--global|--system] --list # 查看 本地仓库|全局|系统 配置列表
# 配置全局用户(用户名和邮箱)
git config --global user.name "用户名"
git config --global user.email "git账号邮箱"
# 删除全局用户信息
git config --global --unset user.name
git config --global --unset user.email
# 配置命令别名,简化输入
git config --global alias.co checkout
git config --global alias.ss status
git config --global alias.cm commit
git config --global alias.br branch
git config --global alias.rg reflog
# 配置log别名,附带一些配置,当然也可以在 git lg 后面加命令参数,如: git lg -10 显示最近10条提交
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
# 上述 配置后 git log 的信息大概是: 84b2f5d - edit2 (45 minutes ago) ,有颜色标记
# 删除全局配置
git config --global --unset alias.xxx
git config --global --unset user.xxx
vim ~/.gitconfig # 编辑配置
git config --global init.defaultBranch # 配置初始化仓库的默认分支
#git init 本地初始化 master, 线上仓库 新建 main 是默认
远程配置,本地仓库与线上仓库关联就需要配置远程
git remote add # 配置远程链接
修改和提交
git status # 查看本地是否有更改(提交状态),工作区与暂存区
git status -vv # 查看本地的更改,附带文件更改细节
git status -s # 查看修改的文件
git diff # 查看更改内容,(工作区和暂存区)
git diff catched # 查看更改内容,(暂存区和本地仓库)
git add . # 提交所有更改所有至暂存区(当然被忽略的文件不会被提交)
git add . -f # 被忽略的文件也添加上
git add # 只提交某个文件至暂存区,file是文件路径
git mv # git mv 命令用于移动或重命名一个文件、目录或软连接(前提是已add或者暂存了)
# Eg: 将demo.vue更改为 demo-change.vue
git mv src/components/demo.vue src/components/demo-change.vue
git commit -m "提交信息" # 给暂存区域生成快照并提交
git commit --all -m "提交信息" # 等同于 git add . && git commit -m,
# 简写 git commit -am ""
# 注意 仅针对 追踪过&修改过 的文件,新增的文件是不可以的
git commit --amend # 修改最近一次提交信息,按 i 进入编辑,esc 然后输入:wq 回车退出即可
git commit --amend --only # 修改最近一次提交信息; 按 i 进入编辑,esc 然后输入:wq 回车退出即可
git commit --amend --only -m '提交信息修改' # 修改最近一次提交信息一步到位
git status
显示索引文件和当前HEAD提交有差异的路径,工作树和索引文件有差异的路径,以及工作树中不被Git追踪的路径(也不被[gitignore[5]](https://git-scm.com/docs/gitignore/zh_HANS-CN)忽略)。前者是你通过运行
git commit
会提交的东西;第二和第三者是你在运行git commit
之前通过运行git add
可以提交的东西
查看提交历史
git log # 查看所有提交历史
git log -5 # 查看最近五条提交历史
git log --oneline # 单行查看最近提交历史 --pretty=oneline --abbrev-commit 合用的简写
git log --graph # 查看日志图,如果存在合并时,看起来较为清晰
git log --pretty="%h, %s, %ad" # 单行查看日志,附带时间
git log --format="%h, %s, %ad" --date=short # 可以限定时间格式,short只显示年月日。
git log --format="%h, %s, %ad" --date=local # 设置时间时区,全局配置 git config --global log.date local.。
# 不习惯月日年是英文的,可以设置日期为 ISO
git log --pretty="%h, %s, %ad" --date=iso
# 当前仓库log date格式 git config log.date iso-strict-local
git log --format="%h, %s, %ad" --date=short --author="OFreshman" -3 只查看作者 OFreshman 最近三条提交
我的日志打印设置
git config --global alias.lg "log --color --graph --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%a d) %C(bold blue)<%an>%Creset' --abbrev-commit"
以上是之前 windows 的设置, 后面我在Mac上时间出不来(我看官网没有不支持星期几了)应该略作修改
git config --global alias.lg "log --color --graph --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cd) %C(bold blue)<%an>%Creset' --abbrev-commit"
效果差不多这样
git lg -3
更多参考:
git log命令全解析_碧海凌云的博客-CSDN博客_git log (自己按需配置你想要得即可)
iterm2 git 在查看提交历史、配置,分支等信息时,退出需要 ↑ + q
查看操作记录
git reflog # 查看所有操作记录,英文状态 按q 退出
推 / 拉代码
git fetch # 将会更新所有的远程分支
git fetch # 从远程 remote-name 上拉取 branch 分支的代码
git pull # 将会合并远程的默认分支到本地的当前分支
git pull # 从远程 remote-name 上拉取 branch 分支的代码并与当前分支合并
# 相当于 git fetch + git merge /
git pull origin --allow-unrelated-histories # 允许合并不相关的历史
git push # 将当前分支代码 推 到远程仓库
# 如果远程只有一个 主机remote-name, 则后面可省略,如
git push
# 为推送当前分支并建立与远程上游的跟踪
git push --set-upstream origin main
# 如果有多个主机名,可通过 -u (--set-upstream-to)设置默认主机,git push时则会推送到 默认主机默认分支
git push -u
git push -f # 将当前分支代码 强推 到远程仓库
丢弃(discard)工作区的改变
git restore
删除文件 git rm
当有些 提交过 的文件不需要了就可以删除掉.
git rm # 删除文件 从本地版本库中删除
git rm -f # 强制删除文件,如果修改过且已放暂存区
git rm --cached # 仅删除暂存区,保留工作区
git rm -r # 递归删除,删除dir文件下所有子文件
清除文件 git clean
从当前目录开始,通过递归删除不受版本控制的文件来清理工作树。
通常,只会删除 Git 未知的文件,但如果
-x
指定了该选项,也会删除忽略的文件。例如,这对于删除所有构建产品很有用。简单来说: 删除工作区未被追踪的文件(Untracked files)
当然, 也可以通过配置 .gitignore
来让文件 不提示
git clean -f # 删除 untracked files
git clean -fd # 连 untracked 的目录也一起删掉
git clean -xfd # 连 gitignore 的untrack 文件/目录也一起删掉 (慎用,一般这个是用来删掉编译出来的 .o之类的文件用的)
# 在用上述 git clean 前,建议加上 -n 参数来先看看会删掉哪些文件,防止重要文件被误删
git clean -nxfd
git clean -nf
git clean -nfd
git clean -f -d -i #递归删除Untracked 文件,支持交互,推荐这种方式
对于交互式删除(加 -i
),举个例子:
可以看到有几个选项,1 递归删除所有;3, 数字选择 ,可以单个数字,也可以1-3,选择1到3(满意了就 enter
退回主菜单,选择1清除即可), 4 就是会询问每个删除的文件。
远程配置 git remote
git remote -v # 查看本仓库连接的远程/线上仓库信息
git remote add # 添加远程仓库信息
git remote remove # 移除远程链接
git remote set-url # 设置新的链接
git remote set-url [--push] [] # 修改并推送(待测试
上述方式配置会在某些操作时需要确认账号和密码,比较繁琐。 补充一下 person access token
方式
settings => Developer settings => Personal access tokens(个人访问令牌) => tokens (classic) => Generate new token => 设置 token过期时间(我建议设置成 永远 省事, github不建议)和权限范围(建议都选上省事)=> 点击 Generate token => 复制令牌去修改本地仓库的 remote 就行了
git remote set-url https://@github.com//.git
将
标签 git tag
这个我不常用(适合发版的时候打标签),作用基本和commit一样,目的是为了你要回滚是可以选择便于记忆的自定义tag,而不是繁琐的 commit id
.
git tag #为某一次提交打标签
git show [tagname] # 查看标签信息
git tag -d [tagname] # 删除标签,标签为本地创建,所以是本地安全删除
git push origin [tagname] # 推送某个标签(对应的提交版本)到origin远程
git push origin --tags # 推送全部本地标签到origin远程
git push origin :refs/tags/ # 可以删除一个origin远程标签,此时是本地标签推送到了远程,先删除本地。
分支管控 git branch
+ git checkout
分支是啥呢,分支就是分支,emm...,废话文学
指向 commit 的指针。分支名便于记忆。
git branch -v # 查看本地所有分支
git branch -r # 查看远程分支
git branch --all # 查看本地和远程分支 缩写 -
git branch # 新建分支 ①
git checkout #切换至 branch 分支 ②
git checkout #切换至 特定的commit ③
git checkout -b #新建分支并切换到该分支 ④ = ① + ②
# ③ 可以将过去的某个提交check出来,此时是个游离(detached)分支,可以 git switch -c <新分支名> 或者 ④ 继续基于该提交开发
# 利用 ③ 甚至可以找到 以前删除分支上的commit(利用 git reflog,分支仅仅是个指针)
git branch -D # 删除本地分支,强制
git branch -d # 删除本地分支,会检查是否被merge --delete
git branch -m # 更改分支命名
git push : # 删除远程分支(推一个空分支给远程test分支)
git push -d # 同上 -d 与 --delete等同
git push : # 远程新建分支并将本地分支推送上去
#Eg: 新建origin远程分支 test,并将本地分支test 推送上去
git push origin test:test
#清空工作区所有改动(还没git add),注意只能清空文件夹的改动,不能
git checkout
git checkout -- # 丢弃工作区某个文件改动
git checkout -- # 丢弃工作区所有改动
分支合并 git merge
分支合并即将两个分支的代码进行合并,如果 a 分支要与 b 分支合并,如果以 b 分支为主,则先切到 b 分支在合并,
合并前需要保证工作区没有未提交的代码
git merge # 当前分支与 branch 分支合并
git merge --allow-unrelated-histories # 允许不相干的历史合并,相信有朋友遇到过
git merge --ff # 默认就是此选项,即fast-faward,不会和合并历史和合并信息
git merge --no-ff -m 'descriptions' # 当前分支与 branch 分支合并
# --no-ff 有合并记录且历史中有两个分支的提交记录
git merge --ff-only # 强制使用 --ff模式合并,不过有分叉的情况,合并不生效
git merge --continue # 合并有冲突了,解决冲突 add 之后运行该命令(继续合并,会有提示)相当于 commit
代码暂存 git stash
git stash 后代码就会复原为最近一次的提交(commit)或者暂存(stash)
本质上是将工作区的代码存放至暂存区,但是 git commit
时不会提交
git stash # 暂存工作区代码
git stash save "暂存信息" # 暂存附带信息
git stash list # 暂存列表,从上到下,新到旧
git stash apply # 应用最新一次暂存
git stash pop # 应用最新一次暂存,和上面一样
git stash apply stash@{1} # 应用次新的暂存
git stash clear # 删除所有暂存
git stash drop # 删除最新一次暂存
代码回滚 git reset
重置当前
HEAD
到指定的状态 官网)白话: 回退版本,可以指定退回某一次提交的版本,也就是撤回暂存区的的提交代码(add之后)
git reset [--soft | --mixed | --hard] [HEAD]
git reset HEAD # 从暂存区回退某个文件,仅git add之后的,
git reset HEAD^^ # 回退所有内容到上一版本(取消最近一次的提交),window版本需要两个 ^,一个的话会提示 More ?
git reset --hard # 清空暂存区的代码,重置为上次一次的提交
git reset --hard HEAD^^ # 同上
# push 之前可执行上面操作, 如果 push 后,reset后,强推即可。
git reset --hard origin/ # 回退到和 线上 分支branch 一样
git reset --hard # 回退到某个commit, git log 查看commit id
# 注意 对于 git commit 之后的代码回滚,需要指定 commitid, 且该commit之后的提交都会被重置掉。
默认参数是 --mixed , 工作区代码被保留,暂存区和本地仓库被清空。
举个例子,现在依次有 a, b, c 三个提交(c最新),我重置到 a(git reset
),那么现在编辑器(工作区)的代码 还是最新 c 的代码,但是 b,c 的提交的代码此时处于 未提交状态(git status 查看),可以选择性的增删,然后 git add && commit
提交即可,也可以使用 git checkout -- .
丢弃掉。
注意:谨慎使用 –hard 参数,它会删除回退点之后的所有提交和所有没有commit的内容,并且会修改commit历史。这可能更像我们理解的回滚,但可能不是我们想要的回滚,我们可能需要回滚后 保留回退点之后的代码(在工作区,git status 可查看到未提交),那么就需要 --soft
git reset --soft HEAD~2 # (柔软的)回滚到上上次提交,保留上次提交代码
git reset --soft # 回退到指定的提交,
# 且保留了 该提交到最新提交的代码(期间删除的文件不会在)
[HEAD] 有两种表示方法(window 上 ^
符号好像有些问题):
HEAD
当前版本,HEAD^
上一次,HEAD^^
上上次,....HEAD~0
当前版本,HEAD~1
上一次,HEAD~2
上上次,....
更改提交 git rebase
可以对某一段线性提交历史进行编辑、删除、复制、粘贴;因此,合理使用rebase命令可以使我们的提交历史干净、简洁!
进入编辑状态: i
退出并保存:esc + : + wq
-i
参数就是支持交互式的去操作,推荐,
# 当前 curent 分支变基至 branch 分支, current当前分支的基底是 branch 分支的最新提交
git rebase
git rebase -i [startpoint] [endpoint]
# 谨慎使用[endPoint] 省略, 即默认表示从起始commit一直到最后一个,但是一旦你填写了, 则表示 [endPoint]后面的commit全部不要了!
git rebase -i # 合并commitId 到当前的提交
git rebase -i HEAD~3 # 修改最近三个提交
# 将 startpoint 与 endpoint 的提交复制(追加)到 branchName 上
git rebase [startpoint] [endpoint] --onto [branchName]
# 另外还需切换到该分支(master),将其所指向的提交ID指向当前 HEAD 所指向的提交id
git checkout master
git reset --hard 0c72e64
git rebase --skip # 跳过提交
git rebase --abort # 取消rebase,当某些重做执行到一半的时候,临时后悔就可以执行此命令
git pull --rebase # 简化拉取代码的信息
# 等同于 git fetch origin + git rebase
变基
对于变基操作 git rebase
(当前分支为current), 找到 current 与 branch 分支 共同的提交 a, 如果找不到 则为各自分支的第一个提交,
将 current 分支上 a到最新的所有提交 追加至 branch 分支上 a到最新的所有提交。然后将整个提交追加到a上组成的提交历史。
它和merge一样都是合并其他分支的代码,不一样的是:merge
会产生一条新的merge commit
,且有一次冲突合并(如果存在),rebase
则不会产生新的提交记录,且不会有 分支合并 历史,且需要至少一次(取决于变基的当前分支有多少新的提交)的冲突(如果存在)合并。
举个例子, local
此时有如下提交
rebase-test
有如下提交
可以看到公共的提交 是从 955f4c5
开始的
然后在 local
上变基
git rebase rebase-test
然后 解决 冲突继续 rebase,此时注释中的 pick 可以打开(去掉#),也就是说需要保留该提交。
如果你此时后悔了,可以用 git rebase --abort 取消你的变基操作
git add . && git rebase --continue
接着修改提交信息,默认是 local
的最后一次提交,我改了之后然后保存.
看到原来 local
上新的提交没有了,产生了所有新的 commit(commit id
变了,commit注释没变,解决冲突时我改了其中一个提交信息)
变基成功后,local
上新的四个提交成功的基于(跟在) 了 rebase-test
的最新的提交(后), 原来是基于(跟在)共同的提交 955f4c5
(后)。
更改提交历史
当输入 git rebase -i HEAD~3
后,进入编辑页面,最新在下面
三次提交信息 前面都有pick,这里的pick是一个命令,可以改成命令集中(reword/edit/squash...)任意命令,来执行相应的操作。
在上面图中我要更改最近一次和第三次的提交信息,退出保存后,会提示两次交互,依次让你去更改提交信息,两次都保存好后就算完成了。
提交信息得到了改变
git rebase 解决的冲突,就用git add .
添加至暂存区, 不能commit, 然后git rebase --continue
rebase的几个命令解释:
- pick (p: 保留该commit
- reword (r: 保留该commit,但仅需要修改该commit的注释
edit (e: 保留该commit, 但我要停下来修改该提交(不仅仅修改注释)。a->b->c 修改a提交,
b,c 提交在rebase之后将不复存在,会被合并。但是a提交存在(区别于后面两个命令)
- squash (s: 将该commit合并到前一个commit
- fixup (f: 将该commit合并到前一个commit,但不要保留该提交的注释信息
- exec (e: 执行shell命令
- drop (d: 丢弃该commit
仔细观察的话,经过 reabse -i
之后 修改的提交, commit id 都发生了变化(即使那些未修改的提交)。
假如有这样的情况,同事在master(目前有 a, b, c三个提交, c是最新)切了一个 feature 分支,然后我在 master 分支改了a提交 (rebase 方式),那么a,b,c的commit-id 就变了, 同事这会开发完了新功能,我就将 feature 分支合并到master,commit id 不同就会将 feeature 分支的 a,b,c处理为新的提交。
结果就会有 三个重复的提交:
a (master,修改的), b(master),c(master),a(feature),b(feature),c (feature)
这可不是我想要的,所以切记:
只对尚未推送或未分享给别人的本地修改执行变基操作清理历史, 从不对已推送至别处的提交执行变基操作
关于rebase,限于篇幅没有把我实践的所有情况列出来,如果需要了解的更全面一些,这里有一些参考:
【git 整理提交】git rebase -i 命令详解_the_power的博客-CSDN博客_git rebase-i
git rebase详解(图解+最简单示例,一次就懂)_风中一匹狼v的博客-CSDN博客_git rebase
git rebase的两种用法(最全)_小垚尧的博客-CSDN博客_git rebase
回退代码 git revert
将目前的提交回滚至指定提交,并生成一条提交记录
git revert HEAD # 回退最后一次提交
git revert --no-commit HEAD~3..HEAD # 回退至倒数第三个提交并保持未commit的状态
git revert # 回退至某一次提交
git revert .. # 回退多个提交
举个例子:我要回退最新的提交,目前有四条提交历史
执行 git revert HEAD
, 进入 vi 界面, 默认提交信息是 Revert "three"
(如果修改 按 i 编辑,然后esc退出),然后 :wq
退出保存,再次查看历史
可以看到多了一条新的 commit, 此时工作区的代码即为 上上次(24be709
)提交的代码!
回退多个提交:
在回退的两条注释后面我追加了 commit。工作区代码为最近第三次提交的代码!
如果有冲突就需要合并,合并完继续 就 git revert --continue
, 直至所有提交都回退完
再举个例子:回退原来的某一次错误提交
摘取提交 git cherry pick
给定一个或多个现有提交,应用每个提交引入的更改,为每个提交记录一个新的提交。这需要您的工作树清洁(没有从头提交的修改)。
git cherry-pick # 摘取某个提交
git cherry-pick # 摘取多个提交,非连续
git cherry-pick ^.. # 摘取多个提交,连续
git cherry-pick --abort # 取消
git cherry-pick --quit # 推出
将已经提交的 commit,复制出新的 commit 应用到当前分支里,文件的增删都会更新,产生冲突时, cherry-pick 的提交回作为 incoming change,在vscode里操作很方便。
举个例子,这里有三个提交。
摘取最近第三次提交(e20d261
)
结果就是增加了一次提交(fa68143
和 e20d261
的合并)
另外注意:最后的commit中, pick commit
与 last commit
增加的文件会存在,删除的文件不会存在!相当于
。
.gitignore文件
这个文件控制着 Git 对哪些文件忽略
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
一些场景
从0-1
本地仓库怎么和线上仓库做关联呢?分以下几种情况
- 线上本地都没有(①)
- 本地新起一个项目 app,
- 线上仓库新建一个(建议同名)同名项目 app。
- 本地配置远程关联的url(参考
git remote
), 然后git push -f
强推。
- 线上有了,本地没有(②)
参考 ① 步骤2,3
本地没有线上有(③)
克隆到本地即可(参考
git clone
)线上和本地都有,只是没关联起来(④)
本地配置远程关联的url(参考
git remote
),再 pull(fetch + merge) 下来即可(另外如果有冲突需要解决掉), 需要推的时候建立 关联git push --set-upstream
。
丢弃工作区的更改
现在我编辑器(工作区)写了一些代码,但是我不想要了,此时没有提交到(git add),name直接丢弃掉就好了
git checkout --
#Eg: 丢弃 文件 demo-change.vue的更改
git checkout -- src/components/demo-change.vue
提交信息错了,我想改
参考 git rebase
或者 git commit --amend
修改最后一次提交
更改提交顺序 场景在哪??
还是 git rebase
.
小技巧:非(vi/i)模式,dd 剪切(光标),p粘贴,粘贴至光标的下一行
合并多次提交,使提交记录更干净
git rebase
结合命令 squash
删除 git 仓库
find . -name ".git" | xargs rm -Rf
# 或者直接删除 .git 目录
rm -rf .git