git基础

# git emoji

执行 git commit 时使用 emoji 为本次提交打上一个 "标签", 使得此次 commit 的主要工作得以凸现,也能够使得其在整个提交历史中易于区分与查找。

emoji emoji 代码 commit 说明
🎉 (庆祝) :tada: 初次提交
✨ (火花) :sparkles: 引入新功能
🔖 (书签) :bookmark: 发行/版本标签
🐛 (bug) :bug: 修复 bug
🚑 (急救车) :ambulance: 重要补丁
🌐 (地球) :globe_with_meridians: 国际化与本地化
💄 (口红) :lipstick: 更新 UI 和样式文件
🎬 (场记板) :clapper: 更新演示/示例
🚨 (警车灯) :rotating_light: 移除 linter 警告
🔧 (扳手) :wrench: 修改配置文件
➕ (加号) :heavy_plus_sign: 增加一个依赖
➖ (减号) :heavy_minus_sign: 减少一个依赖
⬆️ (上升箭头) :arrow_up: 升级依赖
⬇️ (下降箭头) :arrow_down: 降级依赖
⚡️ (闪电)
🐎 (赛马)
:zap:
:racehorse:
提升性能
📈 (上升趋势图) :chart_with_upwards_trend: 添加分析或跟踪代码
🚀 (火箭) :rocket: 部署功能
✅ (白色复选框) :white_check_mark: 增加测试
📝 (备忘录) :memo: 撰写文档
🔨 (锤子) :hammer: 重大重构
🎨 (调色板) :art: 改进代码结构/代码格式
🔥 (火焰) :fire: 移除代码或文件
✏️ (铅笔) :pencil2: 修复 typo
🚧 (施工) :construction: 工作进行中
👷 (工人) :construction_worker: 添加 CI 构建系统
💚 (绿心) :green_heart: 修复 CI 构建问题
🔒 (锁) :lock: 修复安全问题
🐳 (鲸鱼) :whale: Docker 相关工作
🍎 (苹果) :apple: 修复 macOS 下的问题
🐧 (企鹅) :penguin: 修复 Linux 下的问题
🏁 (旗帜) :checked_flag: 修复 Windows 下的问题

最后附上gitmoji网站

https://gitmoji.carloscuesta.me (opens new window)

以及github支持的表情超市

https://www.webfx.com/tools/emoji-cheat-sheet (opens new window)

# git实用命令

git rebase -i HEAD~4 合并commit 的记录
git clone --depth=1
git log
git reflog
git remote -v :查看仓库源
git checkout -b [name]:创建并切换
git reset -–hard ID :回退代码   git reset --hard origin/master 可以用来清空本地的代码
git merge [name] :合并分支
git branch -l :查看本地分支
git branch -r :查看远程分支
git branch -a :查看全部分支(远程的和本地的)
git branch -m dev develop   :换本地分支名字,然后删除远程的,提交下就可以同步更新
git branch -d (分支名称) :可以删除本地分支(在主分支中)
git push origin --delete [name] :删除线上的分支
git checkout -- <file>  :丢弃工作区的修改
git checkout .  :本地所有修改的。没有的提交的,都返回到原来的状态
git blame src/pages/item/ItemTable/ItemTable.js  :查看当前谁修改过代码
git reset HEAD <file>  :当你不但改乱了工作区某个文件的内容,还添加到了暂存时,想丢弃修改
git commit --amend -m'新提交消息'  :提交信息出错`
git stash  :储藏
git stash pop  :删除当前储藏
git  config --global  user.name 'hezhenfeng'  :修改git的用户名 
git  config --global  user.email 'feng960106@163.com'  :修改git的邮箱
git config –-list   :查看信息
git config --global credential.helper cache  :设置记住密码(默认15分钟)
git config credential.helper 'cache --timeout=3600'  :设置记住密码(1h)
git config --global credential.helper store :永久
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# git revert 和 git reset 的区别

  • git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。

  • 在回滚这一操作上看,效果差不多。但是在日后继续merge以前的老版本时有区别。因为git revert是用一次逆向的commit“中和”之前的提交 ,因此日后合并老的branch时,导致这部分改变不会再次出现,但是git reset是之间把某些commit在某个branch上删除, 因而和老的branch再次merge时,这些被回滚的commit应该还会被引入。

  • git reset 是把HEAD向后移动了一下,而git revert是HEAD继续前进,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容。

# 拉取代码 git pull --rebase

假设提交线图在执行 pull 前是这样的:

   A---B---C  remotes/origin/master
                /
           D---E---F---G  master
1
2
3

如果是执行 git pull 后,提交线图会变成这样:

 A---B---C remotes/origin/master
                /         \
           D---E---F---G---H master
1
2
3

结果多出了 H 这个没必要的提交记录。如果是执行 git pull --rebase 的话,提交线图就会变成这样:

  remotes/origin/master
                           |
           D---E---A---B---C---F'---G'  master
1
2
3

F G 两个提交通过 rebase 方式重新拼接在 C 之后,多余的分叉去掉了,目的达到

注意:

git pull = git fetch + git merge

git pull --rebase = git fetch + git rebase

# rebase合并分支

master分支,节点链表指向为: c1<--C3<--c4

dev分支,节点链表指向为: c1<--C2<--c5

master分支和dev分支祖先为c1,假定在master分支上做git merge dev合并,得到的提交历史为:

c1<--c2<--c3<--c4<--c5<--C6 (c1、 c4、 c5做了-次三方合并发现冲突,手工处理完毕后git add/commit增加了提交节点c6)

采用git merge dev处理提交log是按照时间戳先后顺序的。

假定采用的是git rebase处理过程为:

git checkout dev
git rebase master //将dev上的c2、 c5在master分支 上做次衍合处理,并且保证在dev新增的commit 会排在 log 的最后
//git提示出现了代码冲突,此处为之前埋下的冲突点,处理完毕后
git add . //添加冲突处理后的文件
git rebase --continue  //单加上--continue参数让rebase继续处理 ,无需执行 git-commit 
//开发完成, 在 master 上用 mergin 合并dev,不会自动产生合并分支的 'commit'
1
2
3
4
5
6

(opens new window)

# git分支命名

master:主分支,负责记录上线版本的迭代,该分支代码与线上代码是完全一致的。

develop:开发分支,该分支记录相对稳定的版本,所有的feature分支和bugfix分支都从该分支创建。其它分支为短期分支,其完成功能开发之后需要删除

feature/*:特性(功能)分支,用于开发新的功能,不同的功能创建不同的功能分支,功能分支开发完成并自测通过之后,需要合并到 develop 分支,之后删除该分支。

bugfix/*:bug修复分支,用于修复不紧急的bug,普通bug均需要创建bugfix分支开发,开发完成自测没问题后合并到 develop 分支后,删除该分支。

release/*:发布分支,用于代码上线准备,该分支从develop分支创建,创建之后由测试同学发布到测试环境进行测试,测试过程中发现bug需要开发人员在该release分支上进行bug修复,所有bug修复完后,在上线之前,需要合并该release分支到master分支和develop分支。

hotfix/*:紧急bug修复分支,该分支只有在紧急情况下使用,从master分支创建,用于紧急修复线上bug,修复完成后,需要合并该分支到master分支以便上线,同时需要再合并到develop分支。

# git 提交前缀

在软件开发中,常见的 用于表示提交的类型或目的。这些前缀有助于清晰地表达提交的意图,以便其他开发人员更容易地理解和管理代码变更。常见的 Git 提交前缀及其含义包括:

feat: 新功能(feature)
fix: 修复问题(bug fix)
docs: 更新文档(documentation)
style: 代码风格、格式调整(formatting, missing semi colons, etc)
refactor: 重构代码(refactoring)
test: 添加或修改测试(when adding missing tests)
chore: 构建过程、工具或其他辅助工具的变动(maintenance, build process, etc)
perf: 提高性能的代码更改(performance improvements)
1
2
3
4
5
6
7
8

# git 第一次push 怎么指定远程连接

git init 、add 、commit 这些就不用多说了

  1. 添加远程仓库地址到本地仓库配置:

git remote add origin <远程仓库的URL>

  1. 确认远程仓库已经添加:

git remote -v

  1. 创建一个新的分支并切换到该分支(可选,但推荐):

git checkout -b <分支名> // 创建并切换到该分支 有master分支权限 可直接忽略这一步

  1. 推送代码到远程仓库

git push -u origin <你的分支名> // -u 本地分支与远程分支的关联关系,后期可以简写 git push

# gitlab的key

ssh-keygen -t rsa -C "feng960106@163.com"

cat ~/.ssh/id_rsa.pub

# 迁移 github

sed -i '' "s/115.28.166.109/123.57.86.216/g" .git/config

# git 无法添加文件夹下文件

最近做项目时,发现无法提交某个子文件夹下的文件。

google后发现可能是该子文件夹下有.git文件夹导致无法上传。

删除子文件夹下.git后,依然无法提交子文件夹下的文件。

继续google,

尝试以下方法:

git rm --cached directory git add directory

注: directory为子文件夹的路径。

但是执行 git rm --cached directory 时,提示

fatal: Unable to create 'xx/.git/index.lock': File exists.

执行 rm -f xx/.git/index.lock 后解决

# 扫盲 .git

// .gitCOMMIT_EDITMSG  # 最新一次提交的备注信息(并不是HEAD所指备注),git系统不会用到,给用户一个参考
├config # 配置文件,项目用户名、邮箱在该文件配置
├description # 仓库的描述信息,主要给gitweb等git托管系统使用
├FETCH_HEAD # 是一个版本链接,指向着目前已经从远程仓库取下来的分支的末端版本
├HEAD # 映射到ref引用,即当前commit对象哈希值。
├index # 该文件表示暂存区。
├ORIG_HEAD # 当前commit对象的前一个commit对象hash值,与logs/HEAD文件的最新一行的第一个hash值保持一致。
├<hooks> #Git执行特定事件(如commit、push、receive等)后触发运行的一些shell脚本。
│ ├applypatch-msg.sample
│ ├commit-msg.sample
│ ├fsmonitor-watchman.sample
│ ├post-update.sample
│ ├pre-applypatch.sample
│ ├pre-commit.sample
│ ├pre-merge-commit.sample
│ ├prepare-commit-msg.sample
│ ├pre-push.sample
│ ├pre-rebase.sample
│ ├pre-receive.sample
│ └update.sample
├<info> # 存放仓库的信息
│ └exclude
├<logs> # 保存所有更新的引用记录
│ ├HEAD # (切换前的commit对象hash值,切换后的commit对象hash值,切换者,切换者邮箱,切换时间,切换操作信息)
│ ├<refs>
│ │ ├stash
│ │ ├<heads> # 所有分支提交信息,保存每个分支的commit对象链
│ │ │ ├master # master分支的commit对象链
│ │ │ └testchanges # testchanges分支的commit对象链
├<objects> #对象目录,object有:commit、tree、blob、tag
│ ├<08>
│ │ └8aa22897a71f1ff5d34eedd8b30c35997a8ce8
│ ├<0b>
│ │ └20aead00e76e9afc1ee8775b2705c77e790cf6
│ ├<12>
│ │ └d5be03b603f88bc9611979039e58848afa647b
│ ├<14>
│ │ └ed3a8427cc35eec36784d7579db54646b098e9
│ ├<15>
│ │ └aa63597ef2de8f3574b2258a912c8ab63d0694
│ ├<info>
│ └<pack><refs> #commit/tree/blob/tag对象的引用
│ ├stash
│ ├<heads> # 当前分支的当前head指针,便于分支切换
│ │ ├master # master分支当前head指针
│ │ └testchanges # testchanges分支当前head指针
│ ├<tags> # 保存所有的标签哈希值
│ │ └first # 保存first标签的哈希值
│ │ └fsecond # 保存second标签的哈希值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# git获取最后一次提交的的时间和commit id

获取最后一次commit的时间

git show --pretty=format:"%ci %cr" | head -1

2022-05-11 14:54:23 +0800 37 minutes ago

获取最后一次提交的commit Id

git rev-parse HEAD

31cab83b6cc7b844dc407291d47ac72f1d495690

获取最后一次提交的short commit Id

git rev-parse --short HEAD

31cab83b6

# git怎么删除所有tag

# 只能在终端用   命令后面有个空格

远端:  git tag|grep "2022"|xargs git push origin --delete tag  

本地:  git tag|grep "2022"|xargs git tag -d 
1
2
3
4
5

删除本地tag

git tag -d $(git tag -l)

拉取远程tag

git fetch

删除远程tag

git push origin --delete $(git tag -l)

删除本地tag

git tag -d $(git tag -l)

获取最后一次 tag 的名称

git describe --abbrev=0

# git放弃本地文件修改

未使用git add 缓存代码

使用git checkout -- 文件名,注意中间有 --

git checkout -- filename
1

放弃所有文件修改 git checkout .

git checkout .
1

此命令用来放弃掉所有还没有加入到缓存区(就是 git add 命令)的修改:内容修改与整个文件删除。 此命令不会删除新建的文件,因为新建的文件还没加入git管理系统中,所以对git来说是未知,只需手动删除即可。

已使用git add 缓存代码,未使用git commit

使用 git reset HEAD 文件名

git reset HEAD filename
1

放弃所有文件修改 git reset HEAD

git reset HEAD
1

此命令用来清除 git 对于文件修改的缓存。相当于撤销 git add 命令所在的工作。在使用本命令后,本地的修改并不会消失,而是回到了第一步 1. 未使用git add 缓存代码,继续使用用git checkout --filename,就可以放弃本地修改

已经用 git commit 提交了代码

使用 git reset --hard HEAD^ 来回退到上一次commit的状态。

git reset --hard HEAD^
1

或者回退到任意版本git reset --hard commit id ,使用git log命令查看git提交历史和commit id。

git reset --hard commit id

1
2

# git diff

git diff 命令可以对比两个版本的差异,具体来说包括:

  • 本地工作区和暂存区的diff信息:git diff 或者 git diff file

  • 暂存区和版本库的diff信息(使用git add 将工作区修改保存到了暂存区后):git diff --cached

  • 版本库中不同commit、分支的diff信息(使用git commit 将暂存区修改提交到了版本库):git diff commit1 commit2 或 git diff branch1 branch2

  • 可以查看最近一次提交的版本与往过去时间线前数X个的版本之间的所有文件之间的增删改 git diff HEAD~X 或 git diff HEAD^1

如何使git diff写入stdout

- git --no-pager diff

# git 切换分支

如果本地分支已经存在,你可以使用以下命令来切换到该分支:

git checkout branch-name
1

如果你想要从远程分支创建一个新的本地分支,并且不确定本地是否已经存在该分支,那么可以使用以下命令来实现:

git checkout -b branch-name origin/branch-name
1

如果你既不确定本地是否有这个分支,也不想进行区分,你可以使用以下命令:

git checkout -B branch-name origin/branch-name
1

git checkout 命令的 -B 选项和 -b 选项在使用上有一些区别:

-b 选项(小写):

使用方式:git checkout -b [] 如果指定的分支名称不存在,会创建一个新的本地分支,并将当前分支切换到这个新分支。 如果指定的分支名称已经存在,会产生一个错误,阻止分支的创建。你需要先删除同名的分支,然后再创建新的分支。 可以选择性地提供 参数,它可以是提交哈希、分支名等,用于指定新分支的起始点。

-B 选项(大写):

使用方式:git checkout -B [] 无论指定的分支名称是否已经存在,都会创建一个新的本地分支,并将当前分支切换到这个新分支。 如果同名的分支已经存在,会强制覆盖该分支,不会产生错误。这意味着同名分支的本地更改会被覆盖,所以请谨慎使用。 可以选择性地提供 参数,用于指定新分支的起始点。

# mac系统sourceTree一直提示输入密码的问题

作为一个习惯使用 SourceTree 在 MacBook Pro 上提交代码的用户,我在 macOS 上安装了该应用程序。

然而,我发现每次提交代码都需要输入密码,这对我的效率和心情产生了不良影响。我花了很多时间尝试了网上提供的各种解决方法,但是遗憾的是,都无法解决这个问题。

这个密码输入的过程让我感到非常繁琐,

其实原因是因为没有拿到push的凭据

解决办法:

1、git config credential.helper store

这将告诉 Git 使用一个简单的凭据存储方式来记住你的用户名和密码。

2、 git pull 拉下代码

在弹出的对话框中,依次输入你的用户名和密码。请注意,输入密码时不会显示任何字符,但你可以正常输入。

现在就可以 正常push了

# Mac下SourceTree无法执行pre-commit的解决方案

问题背景 最近负责的Web项目中参与人数多了起来,很多人没法自觉地遵守编码规范、在提交代码之前不检查自己的代码格式,导致提交了代码之后无法通过流水线的代码质量检查。

我是mac环境下,由于使用了nvm,不能正确识别node路径,执行代码提交后,husky一直报错:

.husky/pre-commit: line 8: npm: command not found
1

查询husky官方文档才知道如何解决:

创建~/.huskyrc文件,增加对nvm路径的支持:

# ~/.huskyrc
# This loads nvm.sh and sets the correct PATH before running hook
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
1
2
3
4

# Husky 的一些常用钩子

pre-commit: 在执行 git commit 之前触发,用于在提交前运行一些检查,比如代码风格检查、单元测试等。
prepare-commit-msg: 在 git commit 执行前,用于编辑提交信息的默认信息。可以用来自定义提交信息的格式。
commit-msg: 用于检查提交信息的格式,可以在提交完成后执行一些脚本,如验证提交信息是否符合规范。
post-commit: 在 git commit 执行后触发,可用于执行一些与提交相关的后续操作。
pre-receive: 在服务器端处理推送操作之前触发,用于在代码被推送到远程仓库之前执行一些检查。   在服务器上运行
update: 在服务器端的一个仓库中,当推送到远程分支时触发,用于检查推送的分支是否符合规定的条件。 在服务器上运行
post-receive: 在服务器端处理推送操作之后触发,用于在代码被推送到远程仓库之后执行一些操作。  在服务器上运行
pre-push: 在执行 git push 之前触发,用于在推送前运行一些检查。
1
2
3
4
5
6
7
8

# npm version 版本号升级

npm version 命令默认会在更新版本号时创建一个 Git 标签。

如果你希望在更新版本号时不创建 Git 标签,可以使用 --no-git-tag-version 选项。

npm version 默认会提交到暂存区,--no-git-commit-version 选项允许你控制版本号更新是否自动提交到 Git 仓库

--no-commit-hooks 选项是 Git 命令的一个选项,用于阻止在执行提交操作时触发任何预先定义的提交钩子(commit hooks)。

业务场景: 自动更新 version,不进行打tag,然后提交到远程仓库 , commit 的 msg 默认是当前的版本号

npm version patch --no-git-tag-version --no-commit-hooks && git add package.json && git commit -m "$(node -p "require('./package.json').version")"
1

# 删除远程带 sandbox 关键词的分支,排除 sandbox 本身

git branch -r | grep 'sandbox' | grep -v 'origin/sandbox$' | sed 's/origin\///' | xargs -I {} git push origin --delete {}
1
# 24 * 60 * 60 * 30 * 6   15552000
# 删除六个月前的分支
git for-each-ref --sort=-committerdate --format='%(committerdate:unix) %(refname:short)' refs/remotes/origin | \
while read -r timestamp branch; do
  branch_name=${branch#origin/}  # 去掉 origin/ 前缀
  current_time=$(date +%s)
  time_diff=$((current_time - timestamp))
  if [ "$time_diff" -gt 15552000 ] && [ "$branch_name" != "main" ] && [ "$branch_name" != "master" ]; then
    echo "Deleting old remote branch: $branch_name (last commit: $(date -d @"$timestamp" +"%Y-%m-%d"))"
    git push origin --delete "$branch_name"
  fi
done

# 删除六个月前的tag
git tag --format='%(creatordate:unix) %(refname:short)' | \
while read -r timestamp tag; do
  current_time=$(date +%s)
  if (( current_time - timestamp > 2592000 )); then
    git push origin --delete "refs/tags/$tag"
  fi
done

# 本地删除
git for-each-ref --format='%(creatordate:unix) %(refname:short)' refs/tags | \
while read -r timestamp tag; do
  current_time=$(date +%s)
  if (( current_time - timestamp > 2592000 )); then
    git tag -d "$tag"
  fi
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30