git笔记

[TOC]

  • 不要对git抱有信仰
  • git要适配工作流,如果不能提高流程的效率,不要试图推行git

本文不是一个教程文章,如果不熟悉这张图,那么你可能并未用过git,下方有些内容对你来说可能难以阅读

官方文档

https://git-scm.com/book/en/v2

设置 SSH Proxy

确保已安装netcat(linux),或connect.exe(window),打开或创建~/.ssh/config文件,ProxyCommand填写netcat或connect.exe的安装路径

Host github.com
Hostname ssh.github.com
IdentityFile YOUR_SSH_PRIVATE_KEY
User git
Port 443
ProxyCommand /usr/bin/nc -v -x 127.0.0.1:YOUR_PROXY_PORT %h %p

如果是window则将ProxyCommand填写为

ProxyCommand "C:\Program Files\Git\mingw64\bin\connect.exe" -S 127.0.0.1:7890 %h %p

设置http&https Proxy

git config --global http.proxy socks5h://127.0.0.1:7890

换行符设置

window以CRLF换行,linux/macos以LF换行

  • 期望在window编码,并且能兼顾在window/linux/macos中运行。设置为git config --global core.autocrlf true,检出代码时自动转为CRLF,提交到自动转为LF
  • 不要求开发环境,但运行环境在linux/macos中,git config --global core.autocrlf input,检出代码时不自动转换,提交时自动转为LF
  • 无任何编码和运行环境要求。git config --global core.autocrlf false,检出和提交时都不自动转换 另外方案,配置.gitattributes的eol选项

忽略文件权限变化检查

仓库文件中包含可执行文件,但本地文件权限的原因导致你下载到本地后不可执行。而你又不想对该文件的权限变动,上传到远端。可通过以下指令忽略权限修改

git config --global core.filemode false

status和log命令显示中文字符

git config --global core.quotepath false

git fetch时报错would clobber existing tag

本地的tags信息与远端的不一致时会产生该报错

  • 如果报错涉及的tag是你负责的,要看一下git log,是否其他人也在创建同名的tag,还是对方用相同的名名称重打了新tag,与对方沟通并处理好相关冲突
  • 如果tag不是你负责的,而作者重打了tag。操作git fetch --tags -f强制刷新到最新tag信息

仓库管理

fetchpullpush的命令参数中,都涉及到两个参数[<repository> [<refspec>…​],

repository 表示本地仓库名称

refspec 表示引用对象,包括分支名称,标签名称等

我从svn转过来用git时,不理解为什么会有这些参数。这源于git支持本地同时多仓库管理,比如我们期望pull追踪某些仓库的修改内容 必须指定操作的目标仓库名称repository和该仓库的引用对象refspec,clone时默认将repository设置为origin refspec设置为clone时指定的–branch参数

  • 新增仓库
    git remote add [自定义仓库名称] [仓库地址]
    
  • 删除仓库
    git remote remove [自定义仓库名称]
    

rebase

网上好多文章作了介绍rebase的用处,此处不例举,简单记录下rebase的两个高频功能:

  1. [共享分支]合并到[功能分支]
  2. 将【功能分支】的所有fast-forward提交合并为1次

[功能分支]merge到[共享分支]前的工作流程:

  • git fetch --all 拉取远端最新内容
  • git checkout [功能分支名称] 切换到功能分支
  • git rebase [共享分支名称] 解决冲突后,继续执行中断的变基git rebase –continue
  • 1git rebase -i HEAD~N`,合并最近N次,视本地的提交情况设置N的值,如HEAD~2期望合并最近2次提交
  • 弹出的编辑窗口,除了首行是pick,其它均修改为squash, 退出编辑窗口
  • git checkout [共享分支名称]
  • git merge [功能分支名称]

网上有不少有关rebase/merge的论战,双方集中在日志追踪的问题上。

  • merge --no-ff的好处是突出合并线,追踪修改时更容易查阅
  • rebase会将分支的修改排在主干的前,突出最新修改,减少分支线图清晰易读。

我的个人观点是:

  • 如果branch是多人共享的,千千千万万万!!!!!别使用rebase,因为rebase后commitId会重新生成,对于缺乏git使用经验的人员来说, 他们本地commitId与线上的不致,会导致他们下一次pull失败。
  • 如果分支是自己用的,不用操心问题1,随便rebase各种操作,包括上面提交到拉取共享分支和合并N次提交。因为其他人在你将内容合并到主干前,并不关心你改了什么内容,此种情况如果用merge将生的合并线反而令git log上的分支线更加混乱 总结:多人共享的分支只用merge,否则看个人偏好merge和rebase都可以用,建议用rebase能使分支线图更清晰

子模块的在父工程引用的问题

经常出现问题是,子模块有新的提交,但父工程忘记同时要对子模块提交一次新的版本号。其他人在拉取父工程,得不到子模块的最新更新(游离状态),此时如果操作不当,子模块未提交的内容有掉失的可能。 但不是说就能使用了,如果对方的工作内容,只是拉取最新的线上内容,那么可以通过提供脚本git fetch --all & git reset--hard origin/YOUR_BRANCH辅助他们拉取

github flow

  • 在github网站将[目标仓库]fork到个人账号下[个人仓库]
  • clone[个人仓库]到本地
  • 在本地[个人仓库]添加追踪[目标仓库] git remote add [目标仓库-别名] [目标仓库地址]
  • 拉取[目标仓库]信息 git fetch [目标仓库-别名]
  • 创建一个本地功能分支 git checkout -b [功能分支] [目标仓库-别名]/[目标分支]
  • git checkout [功能分支]
  • 开发新内容
  • ….
  • 开发完成后,将[功能分支]合并main分支,git checkout main & git merge [功能分支]
  • 拉取[目标仓库]最新代码git pull [目标仓库-别名]并解决冲突
  • github上提交pull request

cherry-pick

  • 先用git log查看,要cherry-pick的commitId,复制下来
  • git checkout [目标分支]
  • 依次git cherry-pick [commitId]
  • 如果出现冲突,解决完成用git add将解决了冲突的文件添加到暂存区
  • 标识完成冲突git cherry-pick --continue

工作目录与裸仓库

工作目录(worktree)是指`git clone YOUR_WORKSPACE_DIR`的下载目录 仓库信息目录(gitdir)是指,git clone时没特别指名,默认存放在YOUR_WORKSPACE_DIR下的.git目录 实际上,这两个目录无需存在父子目录关系。git clone时指定--bare选项,可以只下载.git裸仓库内容

git clone --bare --branch YOU_BRANCH YOUR_REPO`

如果想将.git放在其它路径,可以使用选项–separate-git-dir

git clone --separate-git-dir=/path/to/gitdir <repository_url> /path/to/workingdir

网上大多数文章介绍用--bare去搭建git远端仓库。其中一个使用场景是,如果当前不方便切换工作目录,git提供全局选项--git-dir--work-tree指工程位置 git --git-dir=YOUR_WORKSPACE/.git --work-tree=./YOUR_WORKSPACE pull

HEAD

HEAD指向本地仓库的当前分支最新提交id,orign/HEAD指向远端仓库的当前分支最新提交id HEAD可以用到任何git命令中需要commitId参数的地方,有关其它变量见https://git-scm.com/docs/git-rev-parse#_specifying_revisions 以下输出HEAD的值

git rev-parse --short HEAD
fde831d

以下示例,commit但未push提交日志的前后对比

  • commit前,两者的值相同
    git log -5 --oneline
    fde831d (HEAD -> master, origin/master, origin/HEAD) Update 2023-09-30-docker笔记.md
    b19cb24 Update 2022-06-26-整理常用linux命令.md
    afb9ed0 wsl2搭建centos、docker mongo
    33c8d2b Update 2023-09-30-docker笔记.md
    d78c147 Update 2023-09-30-docker笔记.md
    git rev-parse --short HEAD
    fde831d
    git rev-parse --short origin/HEAD
    fde831d
    
  • commit前,两者的值不相同
    git add xxxxx.jpg
    git commit -m "add git笔记 jpg"
    git log -5 --oneline
    148dc7c (HEAD -> master) add gi_repo.jpg
    fde831d (origin/master, origin/HEAD) Update 2023-09-30-docker笔记.md
    b19cb24 Update 2022-06-26-整理常用linux命令.md
    afb9ed0 wsl2搭建centos、docker mongo
    33c8d2b Update 2023-09-30-docker笔记.md
    git rev-parse --short HEAD
    148dc7c
    git rev-parse --short origin/HEAD
    fde831d
    

特殊的符号

具体参见:https://git-scm.com/docs/git-rev-parse#_specifying_revisions

~

使用形式commitId~{n},相对于commitId回溯n次提交

  • HEAD = HEAD~0
  • HEAD~ = HEAD~1
  • HEAD~~ = HEAD~2
  • HEAD{n个~} = HEAD~n

^

使用形式commitId^{n},相对于commitId上n次父提交

* HEAD^:当前分支的上一个提交。
* HEAD^^ 或 HEAD^2:当前分支的上两个提交。

@

@ 是 HEAD 的简写

.. (Double Dot)

用于指定两个提交之间的范围(不包括第一个提交)。

    A..B:表示从提交 A 的下一个提交到提交 B 的所有提交(不包括 A,包括 B)。
    master..feature:表示 master 分支不包含而 feature 分支包含的所有提交。

打包

如果打包的目的只是单纯地备份(归档),那么用git archive配合gitattributes的eol选项就足够了。但如果有定制的打包流程,比如 1. 打包时设置发布参数 2.打包需要记录版本日志 3.原始工程包的代码很,打包时要排除非发布内容 4. 对包加密等。 总的来说,你大概率仍然需要一个原始工程,执行一些打包脚本。 打包场景的特性:

  • 整包,只针对某个分支的最新提交
  • 差异包(热更包),某段范围的提交

默认情况下.git裸仓库内容,会全部下载到本地,实际在打包场景并不必要,所以可以配合以下指令加快下载速度

  • –single-branch 只下载单个分支的代码内容,配合—branch使用
  • –depth=1 只下载最后一次提交日志
  • –shallow-submodules 同–single-branch,表示子模块也只下载单个分支代码内容

假定以tag为打包目标

当打整包时,可以同时使用以上3个选项,如下表示从远端YOUR_GIT_ADDRESS,只下载下载YOUR_BRANCH分支,分支与子模块的裸数据只包含最后一次提交日志

git clone -b YOUR_TARGET --single-branch --depth=1 --shallow-submodules YOUR_GIT_ADDRESS

当打差异包时(以tag为文件为最小差异单位),需要至少2个以上的提交日志才能做差异内容,所以不能用–depth选项,另外利用xargs和cp命令将差异提出出来

git clone -b YOUR_BRANCH --single-branch --shallow-submodules YOUR_GIT_ADDRESS
git diff --ignore-submodules --name-only --diff-filter=AUMR $YOUR_SRC_TAG $YOUR_DST_TAG | xargs -I {} cp --parents "{}" $YOUR_OUTPUT_DIR

vimdiff&vimmerge常用命令

引用文章: https://gist.github.com/karenyyng/f19ff75c60f18b4b8149#resolving-conflict-from-a-git-pull

  • 设置vimdiff作为diff和merge工具
    git config --global core.editor vim
    git config --global diff.tool vimdiff
    git config --global merge.tool vimdiff
    git config --global --add difftool.prompt false
    
  • 设置vim在diff文件时的颜色码,加打或创建~/.vimrc文件,追加以下内容
    " 显示空白字符
    set list
    " vimdiff 高亮关闭
    if &diff
          colorscheme evening
    endif
    
  • vimdiff快捷键
快捷键 说明
]+c 跳转到下一个不同处
[+c 跳转到上一个不同处
2]c 跳转到下两个不同处
do dp do:在当前光标位置,用另一个窗口的不同处替换当前位置的内容;dp反之
:1,100 diffg 或 :1,100 diffget 等同于do,只是指定了范围1-100行
:1,100 diffp 或 :1,100 diffput 等同于dp,只是指定了范围1-100行
Ctrl-w w 跳到下一个窗口
ctrl-w h 跳到左侧窗口
ctrl-w l 跳到右侧窗口
ctrl-w j 跳到下方的窗口
ctrl-w k 跳到上方的窗口
:diffupdate 手动刷新文件的比较结果
撤销修改 u

以密钥访问git

生成密钥ssh-keygen -t ed25519 -C "YOUR_MAIL_ADDRESS" 将git客户端 ssh public key添加到git server 这一步允许客户端设备账号能访问稍后创建的git仓库。需要将git客户端.ssh/id_rsa.pub文件内容,追加到git服务器.ssh/authorized_keys。以下提供一个操作方法,使用scp传输到远程服务器(需要支持ssh远程访问),然后追加到authorized_keys

// 客户端
~/.ssh/scp id_rsa.pub root@192.168.0.206/home/git
// 服务端
cat id_rsa.pub >>.ssh/authorized_keys

git常用大杂烩

注意,某些命令&选项到某个git版本才有

子模块

  • 添加子模块
    git submodule add [子模块仓库地址] [子模块存放路径]
    git push
    
  • clone工程时,同时下载子工程
    git clone --recursive [父工程仓库地址]]
    
  • 已clone主工程,但未下载子模块,需要手动初始子模块
    git submodule update --init --recursive
    
  • 更新子模块
    git submodule update
    

分支管理

  • 创建分支
    git branch NAME
    

    删除分支

    git branch -D NAME
    git push origin :NAME
    

提交类

  • 以交互的方式添加文件到暂存区
    git add -i
    

日志类

  • 差异查看最近的2条日志
    git log -p -2
    
  • 显示短commit-id
    git log -p -10 --abbrev-commit
    
  • 以所有分支图谱视图,显示最近2条日志,
    git log -10 --all --graph
    

提交类

  • 修改最后一次提交的注释
    git commit --amend -m "对提交进行说明"
    
  • .gitignore文件常用过滤规则
    *.vscode
    *.so
    *.code-workspace
    

修改&还原类

  • 取消本地文件修改
    git restore [要对比的文件路径]
    
  • 取消暂存区文件修改
    git restore --staged [要对比的文件路径]
    
  • 删除暂存区文件
    git rm -r --cache [要对比的文件路径]
    
  • 取消本地和暂存区的修改,并还原到本地HEAD指向的版本
    git reset --hard HEAD
    
  • 取消本地和暂存区的修改,还原到远端的最新版本
    git reset --hard origin/<BRANCH_NAME>
    
  • 回退远程到某次版本
    git reset --hard <COMMITID>
    
  • 清理未跟踪的目录和文件
    git clean -f -d
    
  • 交互模式清理未跟踪的文件
    git clean -i
    
  • 暂存当前修改,TODO: –keep-index和–index的区别
    git stash
    
  • 查看暂存列表
    git stash list
    
  • 恢复暂存的修改
    git stash apply (恢复后不删除暂存)
    git stash pop (恢复后删除暂存)
    
  • 删除暂存
    git stash drop
    

标签类

  • 标签排序
    git tag -l --sort=v:refname "cn*"
    
  • 反向排序
    git tag -l --sort=-v:refname "cn*"
    
  • 强制刷新本地tags信息
    git fetch --tags -f
    

账号管理

  • 设置账号有效时间X秒
    git config --global credential.helper 'cache --timeout=秒数'
    

比较差异

  • 工作区与本地仓库区的差异
    git diff HEAD
    
  • 查看包含本地子模块的差异
    git diff --submodule
    
  • 暂存区与本地仓库区的差异
    git diff --cached HEAD
    
  • 指定文件在暂存区与本地仓库区的差异
    git diff --cached HEAD FILE_NAME
    

git lfs

  • 对使用lfs的工程安装lfs,lfs不是git内置工具,需要额外安装https://git-lfs.com/ 否则下面指令会报错
    git lfs install
    
  • 设置checkout,clone,pull等自动下载lfs文件
    git config --global lfs.auto-fetch true
    
  • 添加大文件筛选规则,如下将.psd文件以大文件看待
    git lfs track "*.psd"
    

原文:
https://lizijie.github.io/2024/08/11/git%E7%AC%94%E8%AE%B0.html
作者github:
https://github.com/lizijie

PREVIOUSgit打标签脚本
NEXTwsl2搭建centos、docker mongo