git 入门到精通

Created

2024-10-28 13:39:26

Updated

2024-10-29 19:41:21

Caution

未完待续..

1 配置

1.1 修改配置

git config --global user.name "tt"
git config --global user.email "tt@email.com"
# 删除配置
git config --global --unset user.name

1.2 config的作用域

# 只对当前仓库有效, 当前仓库目录/.git/config
git config --local
# 对当前用户所有仓库有效 , ~/.gitconfig
git config --global
# 对系统所有用户的仓库有效, /etc/gitconfig
git config --system

1.3 显示配置

git config --list --system
git config --list --global
git config --list --local

1.4 我的配置

[user]
    name = yourusername
    email = youremail
[core]
    trustctime = false
    editor = vim
    filemode = false
    autocrlf = false
    # 如果是true ,非ASCII 字符会用八进制进行转义
    # 现在终端都支持输出其他字符了,所以取消
    quotepath = false
[alias]
    last = log -1 --stat
    cp = cherry-pick
    co = checkout
    cl = clone
    ci = commit
    st = status -sb
    br = branch
    unstage = reset HEAD --
    dc = diff --cached
    lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %Cblue<%an>%Creset' --abbrev-commit --date=relative
    pull = pull --ff-only
    df = difftool
    mg = mergetool
[merge]
    tool = vimdiff
[diff]
    tool = vimdiff
[difftool]
    prompt = false

1.5 git clone慢的解决方法

这样你git clone https://github.com/xx/yy 就会变成 git clone https://ghproxy.com/https://github.com/xx/yy

git config --global url."https://ghproxy.com/https://github.com/".insteadOf https://github.com/
取消url insteadOf
git config --global  --unset url."https://ghproxy.com/https://github.com/".insteadOf
使用http代理
git config --global https.proxy http://127.0.0.1:1080
git config --global https.proxy https://127.0.0.1:1080
使用socks5代理
git config --global http.proxy 'socks5://127.0.0.1:1080' 
git config --global https.proxy 'socks5://127.0.0.1:1080'
取消使用代理
git config --global --unset http.proxy
git config --global --unset https.proxy

1.6 .gitignore

从 github1 上下载 对应语言的.gitignore 文件

1.6.1 保留文件夹,忽略里面的内容

在目标文件夹内单独建一个.gitignore文件,而不是写在根目录的.gitignore里

*
!.gitignore

或者 目标文件夹data里面 添加一个.gitkeep 总的.gitignore

data/*
!data/.gitkeep

1.6.2 查看文件是否符合.gitignore规则

# 看下当前data目录下的文件, 是否被规则匹配到.
git check-ignore -v ./data/abc
# 返回值是 0 表示该目录文件被 匹配到,将被忽略

2 开发人员一般步骤

1.git clone 下公司代码仓库

git clone git@gitee.com:xyz/test.git

2.以dev或其他开发中的主分支比如WIP(用公司可能用这个分支名字)或者其他 v1.0 这种 创建一个分支进行开发 注意一个功能,或一次bug修复就用一个分支.

git co -b fix/xyz dev

3.写代码, git add,commit, 至少每天下班前 git commit 一次, 功能开发完毕 合并前,可以随意commit,随意的推送该分支到远程

git add .
git ci -m "msg"

4.提交pull request 阶段

将你的commit 合并成一个commit 这个commit_id 是你 git co -b fix/xyz 分支时的最新commit

git reset commit_id
git add .
git ci -m "msg"

保留第一行的那个commit ,不动, 就是你第一次提交的commit 将其他前面的pick 改成s ,然后 保存退出, 提示 输入新的commit msg 再保存退出

git rebase -i commit_id

合并现在最新的dev代码到你的分支

# 先拉取最新的dev代码到本地dev
git pull origin dev:dev
# 本地合并来自 dev的最新代码 不要用merge
# 就相当于你在最新代码的dev分支的基础上进行开发,merge 完全没必要
git rebase dev
* 123ffca - add c.txt
* 5721887 - add b.txt
* b8172ad - add a.txt
* 128d312 - (origin/master, origin/HEAD, master) readme
*   e33481e - (HEAD -> wip) Merge branch 'fix/hello' into wip
|\
| * be69a6e - (origin/fix/hello, fix/hello) fix b.txt add hello
|/
* 123ffca - add c.txt
* 5721887 - add b.txt
* b8172ad - add a.txt
* 128d312 - (origin/master, origin/HEAD, master) readme
* 95d6da4 - (HEAD -> fix/one) fix c.txt add one
* 123ffca - add c.txt
* 5721887 - add b.txt
* b8172ad - add a.txt
* 128d312 - (origin/master, origin/HEAD, master) readme
*   99a02ef - (HEAD -> fix/one) Merge branch 'wip' into fix/one
|\
| *   e33481e - (wip) Merge branch 'fix/hello' into wip
| |\
| | * be69a6e - (origin/fix/hello, fix/hello) fix b.txt add hello
| |/
* / 95d6da4 - fix c.txt add one
|/
* 123ffca - add c.txt
* 5721887 - add b.txt
* b8172ad - add a.txt
* 128d312 - (origin/master, origin/HEAD, master) readme
* 8c0df48 - (HEAD -> fix/one) fix c.txt add one
*   e33481e - (wip) Merge branch 'fix/hello' into wip
|\
| * be69a6e - (origin/fix/hello, fix/hello) fix b.txt add hello
|/
* 123ffca - add c.txt
* 5721887 - add b.txt
* b8172ad - add a.txt
* 128d312 - (origin/master, origin/HEAD, master) readme

3 常用命令

3.1 clone

仅仅为了使用那个仓库
# 只下载最近一个commit, 我们下载最新的代码就行了
git clone --depth=1  https://github.com/vuejs/vuepress.git
# --branch 分支名:  对比 git clone ,checkout 指定的分支, 其他分支还是会拉取
git clone --branch dev https://github.com/vuejs/vuepress.git
# 只下载指定分支  需要再加上 --single-branch ,其他分支不会拉取
git clone --branch dev --single-branch https://github.com/vuejs/vuepress.git

3.2 pull

# 拉取远程分支remote_dev 合并到 本地的local_dev 分支
git pull origin remote_dev:local_dev
  • 当合并的分支为当前分支的后代时, 会自动执行 –ff (Fast-forward)模式,
  • 如果不是后代则执行 –no-ff (non-Fast-forward)合并模式

在任何情况下都会创建新的commit

只会按照 Fast-forward 模式进行合并,如果不是当前分支的直接后代,则会拒绝合并请求并退出

3.3 remote 仓库

# 查看远程仓库信息
git remote -vv
# 添加 新的远程仓库,你可以添加多个远程仓库
git remote add new_origin git@github.com:llibeohs/llibeohs.github.io.git
# 直接修改远程仓库地址
git remote set-url origin git@.../test.git
# 删除
git remote rm origin

本地删除已经不存在的远程分支, 别人提交的远程分支,你 pull过, 然后别人删除了远程分支,这个时候你本地还保留着,想要删除

查看远程的相关信息
git remote show origin
执行结果
* remote origin
        Fetch URL: /root/gittest/./rep
        Push  URL: /root/gittest/./rep
        HEAD branch: master
        Remote branches:
          master                  tracked
          refs/remotes/origin/gen stale (use 'git remote prune' to remove)
删除
git remote prune origin

3.4 branch

git br -r #只显示远程的分支
git br # 本地分支
git br -a #显示所有的分支
切换到master分支, 分支名必须存在
git co master
# 根据当前分支 创建了一个分支,并切换到该分支
git co -b your_branch
# 以某个commit 创建分支
git co -b your_branch commit_id
# 推送本地分支到远程分支,这个远程分支名与你的本地分支同名
git push origin your_branch
# :左边是本地分支 右边是远程分支名
git push origin dev:dev
# 将你新建的本地分支dev与远程分支关联起来
# 这样 你就可以直接 git push, 不需要每次都 git push origin dev
git br -u origin/dev dev
# 可以看到关联信息
git br -vv
     #dev2   a950611 up  <--- 无关联
     #master a950611 [origin/master: ahead 3] up<---- 有关联
# 第一次push 时直接关联远程分支
git push -u origin dev

# 该方式直接从远程分支origin/dev 创建本地分支dev,并切换到 该本地分支dev,
# 这个时候 已经互相关联上了,可直接git pull/push
git co -b dev origin/dev
删除本地分支
git br -d 分支名 # 会提示一些警告,比如你当前分支没有做过merge什么的, 不能删除
git br -D 分支名 # 加入你确定要删除分支, 则使用-D 来
删除远程分支
#git push 远程名 :远程分支名
git push origin :dev # 删除远程分支 dev
git br -m old_br new_br

3.5 checkout (co)

3.5.1 co –

# 就是你 git add 一次到了暂存区,然后又做了修改, 但是你发现
# 还不如 暂存区的内容好,所以想要将工作区的内容变成和暂存区一模一样
# 修改内容后, git status  一样可以看到提示
git co -- a.txt

3.5.2 孤儿分支

# 会将原分支的内容拷贝, 然后新的分支里 没有一个commit ,你需要git add ,commit 做一个初始提交.
# 或者你可以删除里面所有的内容, 这样 变成一个空的分支,你做啥都行.
git co --orphan 分支名

3.6 add

3.7 unstage

# unstage = reset HEAD --
#将文件从暂存区 放到工作区.
#就是 git add 后, 我们 取消这个操作
git unstage world.txt

3.8 commit 注释修改

修改最新的commit的注释信息
git ci --amend
修改之前的commit的注释信息
git lg
    * df6b55b - (HEAD -> master) modify:d 
    * 565bd5a - modify c.txt
    * b50b285 - stg3yy
    * 23fd353 - stg2
    * 4164370 - stg1
# 比如你要修改23fd353 的msg,那么你需要
git rebase -i 4164370 #(你要修改的commit的上一个commit)
# 会弹出一个窗口
# 注释也说的很明白,
# 这里比如我们要修改2个commit的msg,我们将前面默认的pick 修改为r,
#   r, reword = use commit, but edit the commit message
# 其他不要变的 就还是pick
# :wq 保存退出,会跳出另外一个窗口
    r 23fd353 stg2
    r b50b285 stg3yy
    pick 565bd5a modify c.txt
    pick df6b55b modify:d

3.9 cherry-pick (cp)

将任意分支上的某几个提交,应用到当前分支上, 相当于在当前分支上你做了这些修改

git cp commit_id
# 两个commit区间的所有commit, ..左边的是旧的commit,不包含该commit
git cp commit_id1..commit_id2
# 2个分支之间的.
git cp dev..fix/one
git lg
* 411be80 - d
* f145376 - c
* ca55914 - b
git cp ca55914..411be80  相当于
  git cp f145376
  git cp 411be80
# 如果有冲突
    git add .
    git cp --continue
# 或者 取消
git cp --abort

3.10 clean 删除不在工作区的文件

# -n = --dry-run
# 表示先看看会删除什么 ,删除不在工作区的文件. 就是还从没git add过的
# 只会看文件
git clean -n
# -d 会看不在工作区 的文件(默认就会看的)和文件夹
git clean -n -d
# 会提示你 clean.requireForce defaults to true and neither -i, -n, nor -f given; refusing to clean
git clean
# 执行删除操作 , 需要-f
git clean -f
# -d 文件夹也删除
git clean -f -d
# -x 在.gitignore 文件里的 也会删除,默认不会删除
git clean -f -d -x

3.11 rm

# 删除文件, 状态已经是 在 暂存区了, 相当于 rm 文件后, 然后git add filename
git rm filename
# 本地保留该文件, 然后untrack 该文件, 就是不要版本控制它...
# 一般情况下,我们会接着将该文件添加到.gitignore中
git rm --cached filename

3.12 diff 比较

# 更旧的放前面,新的放后面,想看commit2 相对commit1做了哪些更改
# 可以是不同的分支的commit id哦
git diff commit1 commit2 filename
#查看将要推送的内容, (比较本地的dev分支对 远程dev做了哪些修改)
git diff origin/dev..dev
# 实际与 git diff HEAD~~ HEAD 一样
git diff HEAD~~..HEAD~

# 比较tmp分支和master分支 a.txt文件的区别
git diff tmp master -- a.txt
# 直接用commit_id 来比较 也是一样的
# 因为实际上分支名比较的原理就是 就是因为分支名指向的就是该分支最新的一次提交,还是commit_id
git diff tmp_commit_id master_commit_id -- a.txt

3.13 冲突

world.txt(merge 冲突后的文件)
woxld
<<<<<<< ours
oyo
=======
omo
>>>>>>> theirs

一般情况下 会看到 上面 一个是我们分支的版本 一个是你想要merge的分支的版本

oyo 是我们的版本的修改

omo 是别人的版本的修改

Important

可以通过设置 merge.conflictstyle 选项为 diff3 来做为以后合并冲突的默认选项
git config --global merge.conflictstyle diff3
merge 是默认选项, diff3 就会生成3方内容来对比

增加原始版本
git checkout --conflict=diff3 world.txt

<<<<<<< ours
oyo
||||||| base
ooo
=======
omo
>>>>>>> theirs

ooo是原来的情况

#假设我们决定使用我们的版本来作为解决冲突,那么可以
git co --ours world.txt

#想要使用他们的
git co --theirs world.txt

git diff --ours # 查看相对于我们本地的版本 做了哪些更改
git diff --ours filename
git diff --theirs # 查看相对于他们的版本 做了哪些更改

3.14 log

# 查看项目的提交记录
git lg
git lg 文件名  #(查看该文件的提交记录 log)
git lg commit_id  #(查看这个commit_id 以及之前的提交记录log)
#-p 按补丁格式显示每个更新之间的差异。
# 会显示提交的内容
git lg -p commit_id # (查看这个commit_id 以及之前所有的提交内容)
git lg -p #(查看提交内容  相比git lg, 多了内容的查看)
git lg -p    + 文件名 #(可查看该文件以前每一次commit的修改内容)
git lg -p -1 + 文件名 #(只查看该文件当前这一次的commit内容)
git lg -p dev..fix/one # 查看你的分支相对于本地dev分支做了哪些修改
git lg -p origin/master..HEAD # 当前分支最新代码对与远程master分支做了哪些修改

3.15 rebase

git rebase -i commit_id
# git rebase -i 无法把第一个commit进行合并等操作
# 这个可以 将所有commit 合并为一个.
git rebase -i --root

3.16 shortlog 显示发布简报

Tip

可用于显示这次发布,更新了哪些东西

# v1.1.2 是你上次发布的版本.这里就显示 这次更新了哪些东西.
# 会显示用户 提交的 commit message
git shortlog --no-merges v1.1.2..master
git shortlog --no-merges master --not v1.1.2

3.17 blame

git blame world.txt
# 查看 world.txt 99-120行 的代码情况 . 最后更新人与时间
git blame -L 99,120 world.txt

3.18 bundle 打包

# 打包整个master 分支
git bundle create x.repo HEAD master
# 根据打的包 clone 仓库 到 tmp_dir 目录
git clone x.repo tmp_dir

#打包几个提交
#打包当前我们新增加的几个提交,本地fix/one分支相对于远程提交了哪些
git bundle create ../new_commit.bundle origin/dev..fix/one


# 切换到dev分支,拉取最新,然后校验看是否能更新ok
git co dev
git pull
git bundle verify ../new_commit.bundle
# 将fix/one里的提交 ,导入到wip 分支
git pull ../new_commit.bundle   fix/one:wip

3.19 tag

# 列显已有的标签
git tag
# 过滤显示
git tag -l 'v1.4.2.*'
    v1.4.2.1
    v1.4.2.2
    v1.4.2.3
# 新建标签,以当前commit为准 打上
git tag -a v1.4 -m 'my version 1.4'
git show v1.4
#轻量级 打标签
git tag v1.4

# 为某个commit 打上tag
git tag -a v1.2 9fceb02
# 推送标签到远程
git push origin v1.5
# 推送所有标签到远程
git push origin --tags

4 进阶技巧

4.1 hook2

4.1.1 基础

4.1.2 pre-commit framework

Important

todo…

pip install pre-commit
# 然后在 你的git 仓库中添加 配置文件 .pre-commit-config.yaml
# 更新yaml 里设置的repo的设置的rev版本
pre-commit autoupdate
# 执行该命令会将 hook 写到 .git/hooks/pre-commit, 这样每次你commit 就会自动执行了
pre-commit install
# 比如这样, 你就会看到 Initializing environment for ... 这类初始化信息
# 我们去 ~/.cache/pre-commit ,可以看到比如 .pre-commit-config.yaml 里设置的 repo,会git clone 到这个目录
# 首次会 初始化, 以后就不用了, 然后会执行 commit hook 检查
git ci -m 'up'

4.2 CHANGELOG

4.2.1 git cliff3

TODO

生成changelog 工具

# 使用cargo (rust工具) 来安装
cargo install git-cliff
# 初始化配置文件 cliff.toml, 再自行修改
git cliff --init
cliff.toml 修改其中的postprocessors 配置
postprocessors = [
    { pattern = '\$REPO', replace = "改成你的项目git仓库地址" }, # replace repository URL
]
# 生成changelog 文件
git cliff -o CHANGELOG.md

4.2.2 git-chglog4

4.3 子模块

4.3.1 subtree

4.3.2 submodule

4.3.2.1 修改.gitmodules仓库url后需要的操作
[submodule "themes/docsy"]
    path = themes/docsy
    url = https://github.com/google/docsy.git
    branch = v0.2.0

后来这个地址变更了,拉取最新, .gitmodules 是变更了

.gitmodules 变更后的结果
[submodule "themes/docsy"]
    path = themes/docsy
    url = https://github.com/google/docsy2.git
    branch = v0.2.0
# 发现还是 用的老地址 进行clone
git submodule update --init --recursive --depth 1
需要更新到本地的配置
# 会将.gitmodules 的配置更新到 .git/config中去, 实际用到的仓库url在本地配置里
git submodule sync --recursive
# 查看
cat .git/config # 可以看到更新了
# 可以了
git submodule update --init --recursive --depth 1

4.4 git filter-repo

Important

比如commit了一个可执行文件,推送到了远程,然后下个commit你将它删除,推送,实际上这个文件还是存在的. 如何彻底删除它?

安装5

# 只保留README.md文件 和 guides/目录和 tools/releases目录 ,相对于根目录位置的文件
git filter-repo --path README.md --path guides/ --path tools/releases
# 与上面刚好相反, 只删除这几个 东西 , 相对于根目录位置的文件
git filter-repo --path README.md --path guides/ --path tools/releases --invert-paths
#--path-glob '*.DS_Store' 指定这个的话 , 相比path,这个会 包含这类foo.DS_Store or bar/baz.DS_Store
#--path 基于根目录的

#使用这个命令后, 不需要再 清理reflog 和original里的东西 (git filter-branch 这个官方的需要这么做), 因为已经帮你做了
#直接
git push -f --all
git push -f --tags

4.4.1 实际仓库瘦身

#执行命令, 会进行压缩打包, 在 .git/objects/pack 目录下生成一个pack文件,
# objects/ 原本好多的文件夹都不见了. 都压缩打包到了这里
git gc

#可以看到pack打包的文件里 具体是什么文件
git verify-pack -v .git/objects/pack/*.idx

#查看当前所有的文件.  多次commit 都会显示哦
git rev-list --objects --all

#-k 3 对第三列的字段进行排序.  -n 表示用数值排序
git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n  

#找到占用空间比较大的文件名
git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -15 | awk '{print$1}')"

#下方命令中的 path/to/large/files 是大文件所在的路径,千万不要弄错!
git filter-branch --tree-filter 'rm -f path/to/large/files' --tag-name-filter cat -- --all


#如果提示
Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f

git filter-branch -f --tree-filter 'rm -f path/to/large/files' --tag-name-filter cat -- --all

# 运行后
提示 所有分支都重写了. 所有分支里的这个文件 都没有了哦
Ref 'refs/heads/master' was rewritten
Ref 'refs/heads/x' was rewritten
Ref 'refs/remotes/origin/master' was rewritten
Ref 'refs/remotes/origin/x' was rewritten

# 提交

git push origin --tags --force
git push origin --all --force
# 其他开发人员
git pull --rebase

4.4.2 提升目录为根目录

仓库当前目录结果
module/
   foo.c
   bar.c
otherDir/
   blah.config
   stuff.txt
zebra.jpg
只保留module下的文件, 并且将module下的所有文件移动到根目录下
git filter-repo --subdirectory-filter module/
执行完后的仓库目录结构,其他都没了
foo.c
bar.c

4.4.3 移动所有文件到其他目录下

仓库当前目录结果
module/
   foo.c
   bar.c
otherDir/
   blah.config
   stuff.txt
zebra.jpg
git filter-repo --to-subdirectory-filter my-module/
执行后的目录结构
my-module/
   module/
      foo.c
      bar.c
   otherDir/
      blah.config
      stuff.txt
   zebra.jpg

4.5 commit message

4.5.1 commitizen6

4.5.2 commitizen-go7

Back to top