Git 工作流概览
个人信息配置(首次 commit 前会询问)
git config --global user.email "xxxx@gmail.com"
git config --global user.name "zn_dk"
配置公钥免密推送SSH
-
生成公钥
ssh-keygen -t rsa # 一路回车 # 生成的目录位于 C盘用户文件夹\.ssh
-
复制公钥文件 id_rsa.pub (id_rsa 为私钥)
-
复制公钥到 gitee 添加 SSH 公钥
-
done 可以免密提交了
-
免密的克隆 不使用 HTTPS 可以使用 SSH 链接
代理
有些时候由于连不上git服务器而我们又需要推送代码,这时就需要设定git代理服务器。
1. http和https代理
如果说使用的是项目http或者https地址,就配置http与https代理即可,输入以下命令:
git config --global http.proxy "socks5://地址:端口"
git config --global https.proxy "socks5://地址:端口"
例如设定本地代理:
git config --global http.proxy "socks5://127.0.0.1:7890"
git config --global https.proxy "socks5://127.0.0.1:7890"
这样使用git clone/push/pull所有http或者https地址项目都会走代理。
还可以使用下面命令取消代理设置:
git config --global --unset http.proxy
git config --global --unset https.proxy
2. ssh代理设定
如果说项目使用的ssh地址,那么就需要配置ssh代理。
我们需要编辑ssh的配置文件,位于用户文件夹下的.ssh文件夹下。
Windows ssh配置文件路径:C:\Users\你的用户名.ssh\config
Linux ssh配置文件路径:/home/你的用户名/.ssh/config
使用文本编辑器打开配置文件config加入下列配置:
ProxyCommand connect -S 代理地址:端口 %h %p
如果说.ssh文件夹不存在或者config文件不存在就自己创建一个。
配置好了,ssh就会走代理了。
上面是配置全局走代理,事实上一般只需要为指定网址配置代理,例如只为github配置代理,就在配置文件加入:
Host github.com
ProxyCommand connect -S 代理地址:端口 %h %p
Host后面接的就是指定要走代理的地址,可以接多个地址例如:
Host github.com gitlab.com
ProxyCommand connect -S 代理地址:端口 %h %p
可见多个地址使用空格隔开放在Host后面即可,这个例子就是同时指定ssh访问github和gitlab时走代理。
例如配置ssh访问github走本地代理:
Host github.com
ProxyCommand connect -S 127.0.0.1:1080 %h %p
init 创建
Git 使用 git init 命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令。
在执行完成 git init 命令后,Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变。
使用方法
使用当前目录作为 Git 仓库,我们只需使它初始化。
git init
该命令执行完后会在当前目录生成一个 .git 目录。
使用我们指定目录作为Git仓库。
git init newrepo
初始化后,会在 newrepo 目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。
status 状态
查看当前仓库状态 (包括未跟踪待 commit 的文件等)
git status
remote 远程管理
git remote # 查看当前远程库名称
查看状态
git remote -v # 查看远程库状态
典型的示例:
> git remote -v
# origin git@github.com:Zn-Dk/branch-test.git (fetch)
# origin git@github.com:Zn-Dk/branch-test.git (push)
- 这便是在告诉你, 现在你要pull/push 等操作的库, 对应名称是
origin
以及相应的地址, 比如git push origin master
就是在origin
库的master
分支上做操作。
(在同时有其他远程库,比如 Gitee Github 共存的时候起好名字区分是有必要的)
添加远程库
git remote add <name> <url>
添加一个 的远程库, 远程库地址为
比如:
git remote add github git:xxxxx
后续操作:
git push github <branch-name> ...
移除远程库
git remote remove <name>
更新分支状态
远程仓库分支变动后,如果本地不手动刷新,是看不到最新的分支情况的。
这时候就可以使用 update 子命令来更新远程状态。
git remote update origin -p(--prune)
clone 克隆
将仓库的内容拉取到本地, .git 地址从托管库中获取
git clone "https://XXXX.git"
push/pull 推送/拉取
将本地仓库的代码推送到远程仓库 origin
git push origin main(github)/master(gitee)
从远程仓库拉取最新代码(本地已有版本库,非公开库需要输入用户名密码)
git pull origin main(github)/master(gitee)
add/commit 添加/提交
- git add 命令可将该文件添加到暂存区。
添加一个或多个文件到暂存区:
git add [file1] [file2] ...
添加指定目录到暂存区,包括子目录:
git add [dir]
添加当前目录下的所有文件到暂存区:
git add .
以上命令将目录下以 .c 结尾及 README 文件提交到仓库中。
注: 在 Linux 系统中,commit 信息使用单引号 ',Windows 系统,commit 信息使用双引号 "。
所以在 git bash 中 git commit -m ‘提交说明’ 这样是可以的,在 Windows 命令行中就要使用双引号 git commit -m “提交说明”。
例子 本地文件更新之后 执行的顺序
git add .
git commit -m "commit的注释写在这里"
git push origin main
- commit 附加说明
# git commit [OPTIONS]
OPTIONS
-a, --all
Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected.
“加了 -a ,后,会自动把 modified 和 deleted add 到 stage 里,但是新增文件不会受影响。 ”
也就是在 commit 的时候,能帮你省一步 git add ,但只是对修改和删除文件有效, 新文件还是要 git add,不然就是 UNtracked !什么是 UNtracked ?就是没有跟踪,不在git的代码仓里面,你更改了什么是不会提示你更改的,你提交也是不会提交到远程仓库的。
–amend 追加/修改上次commit
适用场景:上一次提交遗漏了一些文件,需要补交,但不想多一次commit,
或者想让提交记录不要那么多,强迫症的福音
#修改最近一次提交
git commit --amend
#进入注释页面,进行修改
# - (可以不改 commit msg 直接 :wq 保存, 也可以在这里补充修改你的 commit 信息)
#修改后保存退出
↓ ↓ ↓ ↓ ↓ ↓
# 情形 1: 没有 push 到远程
#因为没推送到远程,不需要做任何事,就当无事发生~
# 情形 2: 已经 push 到远程, 则需要强制推送
git push -f (git push --force-with-lease)
reset 版本追溯
在远程仓库网页上可以看到项目提交的历史记录和浏览历史版本文件(这里以 Gitee 为例),我们可以看到每次 commit 的版本号简写,事实上我们在进行 reset 操作时只需要开头的简写版本号即可。
如果需要在本地回溯之前的版本 需要使用 reset
命令
-
硬回滚(所有状态都回到目标 commit, 其他更新文件会被一并抹去)
彻底抹除这次提交,谨慎使用,一般用在误提交 master
如果需要保留记录供 review , 建议使用 revert
git reset --hard <commit>
- 软回滚(现在已经添加了新文件,还想保留修改和新增的其他文件)
git reset --soft <commit>
示例(硬回滚):
这里做的操作是将版本的 head 指针指向了历史提交树, 可以通过日志 git reflog 进行查看, 示例代码:
- 创建了一个 commit 52fb314 添加文件 3.txt 此时指针在本次 commit
git reflog
# 52fb314 (HEAD -> main, origin/main, origin/HEAD) HEAD@{0}: commit: 3.txt added
# 6bfe69f HEAD@{1}: clone: from github.com:Zn-Dk/branch-test.git
- reset 回退到之前的版本, 我们检查目录文件 3.txt 已经不存在了
而且 .git/refs/heads/main 的版本号也已经改变
git reset --hard 6bfe69f
# HEAD is now at 6bfe69f "Merge branch 'dev'"
- 再次查看 reflog, 可以发现指针回退到了之前的版本
git reflog
# 6bfe69f (HEAD -> main) HEAD@{0}: reset: moving to 6bfe69f # 越往前越新
# 52fb314 (origin/main, origin/HEAD) HEAD@{1}: commit: 3.txt added
# 6bfe69f (HEAD -> main) HEAD@{2}: clone: from github.com:Zn-Dk/branch-test.git
- 也可以返回到之前的状态
git reset --hard 52fb314
# HEAD is now at 52fb314 3.txt added
- 提交这次回滚到远程,此时会提示本地的版本落后于远程
git push
error: failed to push some refs to 'gitlab.com:Zn-Dk/fetch-rebase.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
-
使用 push -f 强制提交(慎用,因为别人的代码提交到 master 的代码可能会被搞没,这个需要权限,建议和 leader 沟通好)
如果是自己的项目 权限可以在仓库设置中允许 force push
revert 回退修改
上面的示例代码是回溯, 那么如果我们要撤回 commit 就得使用 revert 命令了
git revert
用于撤销文件,撤销文件后不会影响其他的提交。
需要注意的是,
reset
指要回滚到哪个版本,此版本以后的提交都会被回滚(不包含当前版本),而revert
只能撤销某一条提交
-
git revert <commit-id>
:撤销某个提交 -
git revert -n|--no-commit <commit-id>
:撤销某个提交,但执行命令后不进入编辑界面,也就是不会自动帮你提交文件,需要手动提交,这与第1点的差别就是撤销和提交分开了。 -
这里回退版本 控制台显示删除了刚才的文件
git revert 52fb314
[main 54c39b9] Revert "3.txt added"
1 file changed, 1 deletion(-)
delete mode 100644 3.txt
- 可以看到现在指向了一个新版本 54c39b9 此时我们推送到远程
git reflog
54c39b9 (HEAD -> main) HEAD@{0}: revert: Revert "3.txt added"
52fb314 (origin/main, origin/HEAD) HEAD@{1}: reset: moving to 52fb314
6bfe69f HEAD@{2}: reset: moving to 6bfe69f
52fb314 (origin/main, origin/HEAD) HEAD@{3}: commit: 3.txt added
6bfe69f HEAD@{4}: clone: from github.com:Zn-Dk/branch-test.git
git push origin main
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 244 bytes | 244.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
remote: no repo id givemno repo id givem
To github.com:Zn-Dk/branch-test.git
52fb314..54c39b9 main -> main
- 在 github 上面查看, 这次的提交被注明为指定版本的撤销 回滚状态
合并错误代码后的处理方法如下:
- 找到对应的merge记录
- 执行revert操作
- 将revert产生的分支,重新merge到master,将更改抵消
总结
- commit 之后 本地文件是否删除都可以通过版本追溯找回
- 团队开发时 commit 备注一定要写清楚 方便追溯.
- 开发项目过程中, 每天至少 commit 一次.(保险 防丢失)
- 要知道使用 git reset --hard 和 git revert 的场景
branch 分支
查看所有分支
git branch # 列出所有本地分支
git branch -r # 列出所有远程分支
git branch -a # 列出所有本地和远程分支
git branch -v # 查看所属分支 commit ID message
分支管理
git branch <name>(分支名) # 创建指定分支
git branch -d 本地分支名 # 删除本地分支
git push origin --delete 远程分支名 # 删除远程分支
git push origin 远程分支名 # 推送空分支到远程(删除远程分支另一种实现)
checkout/switch 切换分支
git checkout develop # 切换到刚才的 develop
git checkout -b dev # -b参数表示创建并切换
-
完成后续操作之后, 在该分支下对文件的更改就会被记录到新分支中正常的 add commit push
注意后续 pull/push 是从/到 origin develop
git push origin develop:develop
-
如果切换回原有 master/main 分支 则新分支的文件会不可见(物理上也是)
switch
切换分支使用git checkout <branch>
,而撤销修改则是git checkout -- <file>
,同一个命令,有两种作用,确实有点令人迷惑。
因此,最新版本的Git提供了新的git switch
命令来切换分支(推荐):
创建并切换到新的 branchName
分支,可以使用:
git switch -c <branchName>
直接切换到已有的 branchName
分支,可以使用:
git switch <branchName>
merge 合并
合并到主分支
先 checkout 切换到master/main
执行以下:
git merge <分支名>
git push origin master/main
在项目正式完成开发后, dev 分支的文件需要和原始的文件进行合并, 新增的文件会自动添加.
合并其他分支
但是如果项目由不同开发组开发(假定devA devB),devA devB要先合并到dev,再提交到master, 如果有版本冲突, 需要A B组人员沟通核实确认后, 选择一个版本进行覆盖
git checkout devA
git merge devB
# ...冲突 解决后 merge
git checkout dev
git merge devA/devB
# ....
git checkout master
git merge dev
rebase 变基
基本工作流程图解:
A---B---C topic
/
D---E---F---G master
git rebase master # 当前的分支变基自 master
git rebase master topic # 变基自 master (明确分支名,效果一样)
A'--B'--C' topic
/
D---E---F---G master
对比 merge 和 rebase
git merge 适合 多个私有分支的提交合并到(共享)分支
git rebase 适合 将共享分支的提交合并到自己的私有分支里(rebase不好追溯)
rebase 的最大好处并不是消除 merge,而是避免 merge 的交织。
简要来说,就是在 merge 进被合分支(如master)之前,最好将自己的分支给 rebase 到最新的被合分支(如master)上,然后用pull request创建merge请求。
在pull request里面还是采用普通的merge,当然可能有的人喜欢rebase merge等,要看具体情况。
用 rebase 合并提交
使用 rebase 可以使提交的历史记录显得更简洁。
- 现在有 3 个 commit 需要合并成一个进行最终 push
- rebase - i
git rebase -i HEAD~~~(或者 HEAD~3) # 代表合并之前三次提交
- 输出
pick b456172 A
pick 03b28ac B
pick ded0eee C
# Rebase cf6f52b..ded0eee onto cf6f52b (3 commands)
# p, pick <commit> = use commit 使用此次提交
# r, reword <commit> = use commit, but edit the commit message 修改这次提交的 message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit 压缩这次提交
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous 压缩提交 追加而不改变之前的策略
# commit's log message, unless -C is used, in which case
# keep only this commit's message; -c is same as -C but
# opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit 丢弃这次提交
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
-
提示修改 commit message
On branch feature2
pick b456172 A
# This is a combination of 3 commits. <-提示 这是一次合并提交
# This is the 1st commit message:
ABC # <-改成ABC
# This is the commit message #2:
B
# This is the commit message #3:
C
# Please enter the commit message for your changes. Lines starting
- :wq 保存 此时3次提交被合并
cherry-pick 获取某次提交
用于获取某一分支的某一次提交,而不需要将整个时间线进行同步
cherry-pick 获取后 commit 的信息是与源头一致的
git cherry-pick <commit>
典型例子:
- 现有 feature1 feature2 两个分支都由你管理
- 在 feature1 新建了文件 cherry.txt 并且 commit push 远程
- 切换到 feature2 分支,此时想要获取这个 cherry.txt
- 复制 feature1 的提交号,比如
git cherry-pick ab9702f8
- 此时可以看到 cherry.txt 已经出现在 feature2 分支里面了
stash 暂存
用于临时切换分支,但是修改(注意是修改,而不是新增)的文件还未 commit 又要保留当前文件更改的情况
git stash # 将当前更改暂存
git stash -m <message> # 将当前更改暂存(自定义message)
git stash list # 查看 stash 暂存栈
git stash pop [index] # 弹出暂存栈 可以指定 index
git stash apply [index] # 应用储藏(不清空栈)
git stash drop <stash idx> 删除指定下标的储藏
git stash clear 清空储藏栈
典型例子:
- 现有 feature1 feature2 两个分支都由你管理
- 在 feature1 修改了文件 feature1.txt
- 暂存 feature1 的更改
git stash
# Saved working directory and index state WIP on feature1: 099a42e add feature1.txt
- 切换新的分支 feature2 继续工作
- 暂存 feature2 的更改
git stash
# Saved working directory and index state WIP on feature2: 3855ed3 add feature2.txt
- 查看 stash 暂存栈
git stash list
stash@{0}: WIP on feature2: 3855ed3 add feature2.txt
stash@{1}: WIP on feature1: 099a42e add feature1.txt
# 可以看到 对 feature2 的修改位于栈顶,之前 feature1 的暂存在栈底部
- 选择恢复在 feature1 做的更改
git stash pop # 弹出栈顶储藏 - vscode 叫弹出最新储藏
git stash pop 1 # 弹出储藏 [1] - vscode 叫弹出储藏... <-使用这个
- 查看 feature1.txt 发现已经恢复了之前的状态
多平台格式问题
windows: 默认在提交时把换行符CRLF转换为LF,在拉取代码时将LF转换为CRLF。(git安装时,没有额外设置的,默认使用这个方案)
Linux/Unix和Mac OS和Mac OS X: 默认在提交时将CRLF转换为LF,在拉取时不进行转换。(git安装时,没有额外设置的,默认使用这个方案)
// 提交时转换为LF,检出时转换为CRLF(一般设置这个)
git config --global core.autocrlf true
// 提交时转换为LF,检出时不转换
git config --global core.autocrlf input
// 提交检出均不转换
git config --global core.autocrlf false
如果在 Windows 平台下开发, 会出现 LF 仓库代码拉下来变成 CRLF 进而导致如 ESLint 报错的情况, 这时候我们设置:
git config --global core.autocrlf input
git config --global core.safecrlf true
更常见的场景 :
开发了一些内容发现还在master,没切换分支
// 1、将新开发的内容暂存,暂存后git status能看到当前工作台是干净的~
git stash save 'feature'
// 2、切换分支
git checkout -b <yourbranch> / git switch <yourbranch>
// 3、查看stash中的内容,可以看到暂存的一些内容列表
git stash list
// 4、将stash中的内容pop到新的分支上面,pop之后会将缓存在堆栈中的对应stash删除
git stash pop
prune
根据官方的解释,直白一点的翻译就是删除 git 数据库中不可访问的对象,那我的理解是这样的,git prune删除的是你本地 .git 下的 object 目录下,没有被使用到的 hash 值,我理解的是它会删除 origin/xx 开头的没有用到的分支,这个分支在你的远程的 git 服务器中已经删除但是本地任然存在 origin/xxx 的映射,这个时候你就可以使用git prune来删除本地的 origin/xxx 的映射。
但是官方推荐使用的是 git gc,而想删除本地的 xxx 分支,就只能只用git branch -D XXX,
清除本地无用分支的shell :
git fetch --all --prune && git branch -vv | grep gone | awk '{ print $1 }' | grep -v pit | xargs git branch -D
作者:agatex
链接:https://www.jianshu.com/p/f215964f40a5
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
提交规范
多人协作时,为了更好管理分支以及提交日志,我们最好建立相关规范,提高协作效率。
示例分支规范
分支 | 介绍 | 环境 |
---|---|---|
master | 仓库默认分支,暂无使用 | - |
release | 线上保护分支 | Prd |
release-pre | 预发环境分支,用于 QA 做固定 Pre 测试 | Pre |
test | 测试环境分支,用于 QA 做固定 Test 测试 | Test |
feature/xxx | 功能开发分支 | Dev/Test |
hotfix/xxx | 热修复分支 | Pre/Prd |
release-2022xxx | 上线分支;上线当日部署到 Pre 进行验证,通过以后直接作为上线分支使用 | Pre/Prd |
日常开发中,对于线上环境、预发环境和测试环境建议使用固定分支进行部署,所有分支以 release
作为基线分支。之所以不用release-pre
或者 release
作为上线分支,是因为上线当天经常因为各种原因导致只有部分feature
是可以上线的。上线当天创建 release-20220818
并部署到Pre,如果验证通过直接部署上线,如果部分上线,则直接丢弃,再创建一个新的上线分支即可,避免污染 release
。
提交规范
提交代码时,务必填写提交日志,日志清晰明了,说明本次提交的目的。同时需要遵循一定的提交规范。
<type>(<scope>): <subject>
-
Header:包括三个字段:type(必需)、scope(可选)和 subject(必需)。
-
Type:提交 commit 的类别,建议使用下面标识
feat: 加入新特性
fix: 修复 bug
improvement: 在现有特性上的改进
docs: 更改文档
style: 修改了代码的格式
refactor: 代码重构,不包含 bug 的修复以及新增特性
perf: 提升性能的改动
test: 测试用例的改动
build: 改变了构建系统或者增加了新的依赖
ci: 修改了自动化流程的配置文件或者脚本
chore: 除了源码目录以及测试用例的其他修改
revert: 回退到之前的一个 commit
-
Scope:用于说明 commit 影响的范围,默认可忽略。
-
Subject:简短精炼的提交描述。
github 仓库搜索技巧**
- xxx in:name 按照名称搜索
xxx in:description 按照描述搜索
xxx in:readme 按照readme中包搜索 - xxx stars:>n language:java 这个非常有用。
xxx forks:>n language:java - 将某个文件代码行高亮,编辑 URL 链接(#L行号)。如果多行就是(#L行号-#L行号)
例如
https://github.com/Zn-Dk/zn-web-learn/blob/master/vue3-learn/jsconfig.json // 高亮第 7-11 行文本 https://github.com/Zn-Dk/zn-web-learn/blob/master/vue3-learn/jsconfig.json#L7-#L11
- 按 T 键, 在项目网页中调出搜索,搜索指定文件
- 按 “.” 键, 打开github中vscode
记得养成一个良好git发布流程的习惯
# 分支合并发布流程:
git add . # 将所有新增、修改或删除的文件添加到暂存区
git commit -m "版本发布" # 将暂存区的文件发版
git status # 查看是否还有文件没有发布上去
git checkout test # 切换到要合并的分支
git pull # 在test 分支上拉取最新代码,避免冲突
git merge dev # 在test 分支上合并 dev 分支上的代码
git push # 上传test分支代码