git历险记(持续更新ing)


十年工程师:好,欢迎加入idjatsni公司,你先熟悉一下,调整调整
小灯:okok

在工作中,git已然成为一项必备技能,git虽然看起来不像其他知识那样复杂,但是想要用好git也不是一件容易的事情,当面对的团队合作项目越来越复杂时,当在职场种摸爬滚打也越来越久时,我们必然会碰到各种各样的团队协作问题。

面对各种情景,如果不知道如何处理,则会拖慢整体开发效率,甚至无法完成开发。基于此,我站在一个基础初学者的角度,结合个人工作中经常碰到的各种情形,整理出这份面向场景的git使用方案,暂且称它为“git历险记吧”


前言:先学习一些基础概念,比如git是什么,分支是什么,节点是什么等。推荐基础资料学习:

我的博客:《git入门实践(入门)》
动画网站:https://learngitbranching.js.org/?locale=zh_CN
other

首先先申请公司各种账号,这里主要是代码托管平台的账号,比如gitlab上的,根据公司步骤来即可

s1. 安装git

  • mac环境
  1. 先安装Homebrew,官网下载,或者终端执行:
    1
    /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
  2. 打开终端执行:
    1
    brew install git
  • windows环境
  1. 访问官网下载安装包:https://git-scm.com/download/win
  2. 下载后运行安装程序,默认选项一般都可以直接下一步。
  • 检查环境
    安装好后,终端运行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        git --version
    ```
    显示版本号即安装成功

    -- --
    # s2. 最小配置
    最小配置,要配置用户名和邮箱,在命令行中:
    ```sh
    git config global user.name "你的名字"
    git config global user.email "你的邮箱@example.com"
    ```
    然后执行命令查看生效:
    ```sh
    git config --global --list
    若显示出user.name和user.email,则配置成功

    PS: 这里的global参数指的是配置在当前用户生效,也就是该用户所有仓库生效,另外还有local,system

三种配置作用范围:

  • local: 仅当前仓库生效
  • global: 当前用户在所有仓库生效
  • system: 系统所有用户和仓库生效

优先级: system -> global -> local

s3. 配置公私钥

配置公私钥来让传输更快速和安全,现在应该是必备的了

  1. 生成ssh key并复制

    1
    ssh-keygen -t ed25519 -C "你的邮箱@example.com"

    执行完后会生成一个公钥和一个私钥,分别在:

    ~/.ssh/id_ed25519 (私钥,得保管好)
    ~/.ssh/id_ed25519.pub (公钥)

  2. 然后把公钥的内容复制,放到远程代码托管平台的制定公钥位置上,比如github、gitlab、gitee,看公司用的是什么,然后测试连接:

    1
    ssh -T git@github.com

    对于这里的“git@github.com”,一般公司都有自己的制定地址名称,替换即可。成功的话,会看到:Hi username!

s4. xxx permission denied

假如碰上这个提示,一般就是权限不够,找对应仓库的负责人或者有权限的管理者,帮开通权限

s5. 配置新项目环境

一般项目配置都会有文档教程,按照文档优先配置,可以去问对应的十年工程师,常规步骤

  1. 克隆项目到本地,命令输入

    1
    git clone 仓库地址

    repoAdress.png
    如上述就可以用ssh的,执行:

    1
    git clone git@github.com:bistukk/kkMusic.git

    执行完后,项目就会顺利clone到本地

  2. 如果项目是用子仓库嵌套的方式组织的话,那应该还需要执行

    1
    git submodule update --init --recursive --progress

    这将会拉取所有子模块代码并更新到最新版本(关于submodule,后面会详细讲到)

  3. 切换到指定分支

    • 查看当前分支
      1
      git branch 
      这将会列出你当前本地所在的分支, 后面可以加-a参数,-r参数,-v参数,表示所有,远程,详细信息
    • 查看当前节点
      1
      git log
    • 切换到指定分支(本地没有,远端有)
      1
      git switch -c <本地分支名> origin/<远程分支名>
    • 如果有子模块有特定分支需要切换,则手动进入子模块目录,执行切换分支操作

自此,如果合理按照文档的配置,并且已经切换到对应的分支节点了,项目一般就能编译运行了,接下来进入开发,进入之前先来了解git的几个区域

s6. 工作区,暂存区,本地仓库,远程仓库

首先要先知道git的工作流转有这四个区域

  1. 工作区(Working Directory)
    工作区其实就是项目clone下来后的文件地方内容,平时看到、编辑文件的目录
    修改的文件最开始都是在工作区。
  2. 暂存区(taging Area / Index)
    git add 会把工作区的修改放到这里,准备提交
    可以理解为“提交前的缓冲区”,将工作区的东西放入在暂存区:
    1
    git add .
  3. 本地仓库(Local Repository / HEAD 所指向的仓库)
    保存了提交历史(commit),git commit 会把暂存区内容提交到本地仓库。HEAD 指向当前分支的最新提交,通过git commit将暂存区的内容提交到本地仓库
    1
    git commit -m"本次修改内容信息"
    这将会再本地仓库生成一个提交记录,commit节点
  4. 远程仓库
    托管在服务器上的仓库,比如 GitHub、GitLab、公司自建 Git 服务器。本地仓库通过网络与远程仓库同步。保存了所有共享历史,供团队协作使用。用git push将本地仓库的内容推送到远端关联仓库
    1
    git push
    一定要牢记有这四个区域,虽然很简单,但是后续很多命令其实都需要对区域有清楚的划分。接下来进入场景篇,顺序难度不分先后,start adventure

s7. 在基线分支进行了修改,想开新的分支并包含此次内容,并在新开分支上开发

  1. 改动尚未commit(仍在工作区/暂存区)
    1
    2
    3
    4
    # 创建并切换到新分支,改动自动跟过来
    git switch -c feature/new-branch
    # 或者较旧版本 Git
    git checkout -b feature/new-branch
    然后就直接在feature/new-branch 上继续开发,baseline 保持原状。
  2. 改动已 commit 但还没 push 到远端
    先把 commit 带到新分支,再把 baseline 分支回退
    • 新开分支并保留改动
      1
      git switch -c feature/new-branch        # 新分支里已有那些 commit
    • 切回 baseline 并撤掉那些 commit
      1
      2
      git switch baseline
      git reset --hard origin/baseline
  3. 改动已 push 到远端,但希望从 baseline 分支“分离”出来
    建议保持 baseline 历史不动,直接从对应 commit 打出新分支即可,然后跟团队成员交流baseline如何处理
    1
    2
    # 从含有改动的最新提交打新分支
    git switch -c feature/new-branch

S8. 想把某个commit节点应用到另一个某个分支节点上

1.假设在分支 A 上开发了某个功能,有一个 commit 想要应用到分支 B 上。

  • 查看要 pick 的 commit:

    1
    2
    git checkout A
    git log --oneline

    找到要挑选的 commit,比如:

    1
    a1b2c3d 修复了音频播放的 bug
  • 切换到目标分支 B,然后cherry-pick

    1
    2
    git checkout B
    git cherry-pick a1b2c3d

    2.如果commit较多,可以:

  • 多个commit(不一定连续)

    1
    git cherry-pick hash1 hash2 hash3 ...
  • 多个连续 commit

    1
    git cherry-pick hash1^..hash3

    3.如果有冲突,需要解决,解决完后:

    git add .
    git cherry-pick –continue

s9. 在自己的开发分支上进行了一些开发,这时候基线分支已经有很多更新

两种情况,一个是还打算继续在个人分支上开发,一个是直接到基线分支开发(迭代快,时间紧的时候)

  1. 继续在个人分支上开发(个人长期开发,时间足够)
    • 方法一:使用 rebase

      1
      2
      3
      4
      5
      # 先切到自己的开发分支
      git switch feature/my-dev-branch
      # 把基线分支的最新提交“挪到”当前分支的底部(线性历史)
      git fetch origin # 确保是最新
      git rebase origin/baseline

      提交会像“从基线上重新开发”一样排到最新更新之后。历史干净线性,适合个人分支开发

    • 方法二:使用 merge(保留开发历史)

      1
      2
      3
      # 仍然在自己的开发分支
      git fetch origin
      git merge origin/baseline
  2. 直接到基线分支开发(迭代快,时间紧的时候)
    • 切换到基线分支并确保是最新的
      1
      2
      git switch baseline
      git pull origin baseline # 拉取远端最新的 baseline
    • 从原来的分支 cherry-pick 提交
      • 情况 A:只想 cherry-pick 最近的 某些次提交
        先用git log 找到要 pick 的 commit hash:
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
            git log --oneline feature/my-dev-branch
        ```

        然后cherry-pick想要的commit_hash:
        ```sh
        git cherry-pick <commit_hash1> <commit_hash2> ...
        ```
        - 情况 B:cherry-pick 一段连续的提交(可以用范围)
        假设想把从 A 到 B 的一段提交 pick 到 baseline 上:
        ```sh
        git cherry-pick A^..B
        注意:这是一个闭区间,包含 A 和 B。
    • 第 3 步:解决冲突(如有)

s10. commit了多次,想把多次commit合并成一个

假设想合并最近的 3 次提交,则可以:

1
git rebase -i HEAD~3

然后会进入这样的编辑界面,类似:
1
2
3
pick 1234567 commit message 1
pick 89abcde commit message 2
pick fedcba9 commit message 3

将上面的内容修改为如下形式(除了第一个,其他都改为 squash 或简写 s)
1
2
3
pick 1234567 commit message 1
squash 89abcde commit message 2
squash fedcba9 commit message 3

执行后 Git 会让你编辑 commit message,比如:
1
2
3
4
5
6
7
8
9
 # This is a combination of 3 commits.
# The first commit's message is:
fix login bug

# The 2nd commit message:
update UI layout

# The 3rd commit message:
improve performance

自己改写为一个清晰的合并提交信息,然后保存退出即可

  • 推送变更(如果之前已经推送过这些 commit,需要强推)
    1
    git push origin 分支名 --force(强推时需要force)
    PS:谨慎使用 –force,因为这会改写历史,其他人基于你这分支工作的话可能会有问题。
    建议只在 尚未合并到主分支 或 自己一个人工作的分支 上这么做。

s11. 自己在本地开发编译运行,检查git发现除了自己的修改外还有很多变更

比如经常:

change.png

  • 可能的原因
    1. 编译生成的临时文件 / 缓存文件未加入 .gitignore
      • ▪ 比如:*.o, *.class, build/, .gradle/, DerivedData/ 等。
      • ▪ 常见于 C++、Java、Android、iOS 项目。
    2. IDE 或构建工具自动生成的配置文件
      • ▪ 比如:xcuserdata/, .idea/, .vscode/, .DS_Store。
    3. pod install / gradle sync / CMake configure 等拉取依赖引起的变更
      • ▪ 例如 iOS 的 .xcworkspace 或 Pods 文件夹,Android 的 .gradle、*.iml 文件等。
    4. 代码格式化工具自动修改了代码
      • ▪ 例如 clang-format, prettier, ktlint 自动调整了自己没关注的文件。
  • 解决方案:
    1. 方式一:将无关文件添加到 .gitignore。
      比如看到的 build/, .idea/, Pods/, *.pbxuser 等变动,可以在根目录的 .gitignore 文件中加:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      # iOS
      *.xcuserdatad
      *.xcscmblueprint
      *.pbxuser
      *.mode1v3
      *.mode2v3
      DerivedData/
      Pods/
      # Android
      *.iml
      .gradle/
      build/
      .idea/
      # macOS
      .DS_Store
      然后执行:
      1
      2
      git restore --staged <文件名>     # 如果已经被 add
      git restore <文件名> # 恢复修改
    2. 方式二:只 add 实际改动的文件
      1
      2
      git add 文件1 文件2 文件3
      git commit -m "只提交你需要的内容"
      建议用 git diff / git status 仔细检查后再 git add。

s12. Git hook

&emsp; “Git Hook”这个名字里的“Hook”,来源于计算机编程中的“hook”概念,意思是“钩子”或“挂钩”。
&emsp; Git hook 是放在 .git/hooks/ 目录下的一组脚本文件(Linux/Mac 是 shell 脚本,Windows 也可以是 batch 脚本或其他可执行文件)。
&emsp; 每个 hook 脚本对应 Git 操作的某个阶段,比如提交前、提交后、推送前等。
&emsp; 默认目录下有很多示例脚本,文件名一般是钩子的名字(比如 pre-commit、commit-msg 等),但默认都是 .sample 后缀,需要去掉 .sample 并确保可执行,才能生效。
比如:
alt text

比如在pre-commit中,可以有:

1
2
3
4
5
6
 #!/bin/sh
# 简单示例:禁止提交包含 TODO 的代码
if git diff --cached | grep -q 'TODO'; then
echo "Error: Commit contains TODO."
exit 1
fi

给脚本加权限:chmod +x .git/hooks/pre-commit
测试提交时,如果暂存区的变更里包含 TODO,提交会被阻止。

实战例子
别人的一些功能别人没有适配也会影响到分支导致无法commit
应对方案,可以把git hook关了,命令行可在commit的时候手动加上 –no-verify
或者图形界面设置(比如在android studio里)

s13. submodule相关

在 Git 中,submodule(子模块)是一个 Git 仓库中的嵌套仓库。当你想在一个 Git 仓库中引用另一个独立的 Git 仓库(比如作为依赖库),就可以使用 submodule。

&emsp;子模块的特点

  • 子模块不随主仓库自动更新:它只是记录了一个特定的提交(commit)引用。
  • 更新子模块需要手动执行;
  • 删除子模块比普通文件稍复杂(因为有多个配置项需要清理);
  1. 更新子仓库

    1
    git submodule update --init --recursive --progress

    这将会把所有子仓库更新到最新的提交引用,加上–progress可以查看进度,避免卡太久不知道什么情况。

  2. 有时需要特定版本的子仓库,则在执行更新子仓库后,需要自己切进特定分支,切换对应的分支节点。

  3. 注意,子仓库有变更,主项目如果需要应该子仓库变更的节点的话,也应该提交变更的子仓库节点引用

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2022-2025 Capper
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信