Git 版本控制系统(下):从 .git 目录结构到冲突解决机制详解

目录

  • 前言
  • [一、.git 目录结构深度剖析](#一、.git 目录结构深度剖析)
    • [1.1 .git 目录是什么](#1.1 .git 目录是什么)
    • [1.2 objects 目录------Git 存储的核心](#1.2 objects 目录——Git 存储的核心)
      • [1.2.1 提交前的 objects 目录](#1.2.1 提交前的 objects 目录)
      • [1.2.2 第一次提交后的 objects 目录](#1.2.2 第一次提交后的 objects 目录)
      • [1.2.3 再次修改提交后的 objects 目录](#1.2.3 再次修改提交后的 objects 目录)
    • [1.3 Commit ID 的本质](#1.3 Commit ID 的本质)
  • [二、查看提交历史:git log](#二、查看提交历史:git log)
    • [2.1 基本用法](#2.1 基本用法)
    • [2.2 日志中的关键字段解析](#2.2 日志中的关键字段解析)
    • [2.3 日志信息的重要性](#2.3 日志信息的重要性)
  • 三、版本恢复与底层原理
    • [3.1 Git 只记录"修改动作"](#3.1 Git 只记录"修改动作")
  • [四、Git 忽略文件机制与 .gitignore 详解](#四、Git 忽略文件机制与 .gitignore 详解)
    • [4.1 .gitignore 的核心作用与配置逻辑](#4.1 .gitignore 的核心作用与配置逻辑)
    • [4.2 规则生效验证与"已跟踪"陷阱](#4.2 规则生效验证与“已跟踪”陷阱)
  • [五、Git Push 原理与远程仓库别名机制](#五、Git Push 原理与远程仓库别名机制)
    • [5.1 git push 与 git push origin main 的区别](#5.1 git push 与 git push origin main 的区别)
  • [六、Git 多平台协同开发与冲突解决实战](#六、Git 多平台协同开发与冲突解决实战)
    • [6.1 双平台开发场景与核心痛点](#6.1 双平台开发场景与核心痛点)
    • [6.2 Windows 环境下的 Git 操作基础](#6.2 Windows 环境下的 Git 操作基础)
      • [6.2.1 Git Bash:类 Linux 的命令行体验](#6.2.1 Git Bash:类 Linux 的命令行体验)
      • [6.2.2 TortoiseGit:可视化的版本控制](#6.2.2 TortoiseGit:可视化的版本控制)
    • [6.3 协同开发的标准工作流](#6.3 协同开发的标准工作流)
      • [6.3.1 正常提交流程(无冲突)](#6.3.1 正常提交流程(无冲突))
      • [6.3.2 验证同步结果](#6.3.2 验证同步结果)
    • [6.4 深入理解代码冲突(Conflict)](#6.4 深入理解代码冲突(Conflict))
      • [6.4.1 冲突的产生](#6.4.1 冲突的产生)
      • [6.4.2 解决冲突的步骤](#6.4.2 解决冲突的步骤)
    • [6.5 常用命令速查与辅助工具](#6.5 常用命令速查与辅助工具)
      • [6.5.1 核心命令清单](#6.5.1 核心命令清单)
  • 结语

🎬 云泽Q个人主页
🔥 专栏传送入口 : 《C语言》《数据结构》《C++》《Linux》《蓝桥杯系列》《笔试算法》《AI赋能》《STM32》《Python

⛺️遇见安然遇见你,不负代码不负卿~


前言

大家好啊,我是云泽Q,欢迎阅读我的文章,一名热爱计算机技术的在校大学生,喜欢在课余时间做一些计算机技术的总结性文章,希望我的文章能为你解答困惑~

一、.git 目录结构深度剖析

本文衔接上文~

1.1 .git 目录是什么

每次 git clone 后,项目文件夹里都会隐藏着一个 .git 目录(用 ls -al 才能看到)。这个目录就是 Git 的灵魂,里面存储了所有的版本控制数据、配置信息、提交历史等。

通过 tree .git 可以查看其完整结构:

复制代码
.git
├── branches/          # 分支相关(已废弃,基本不用)
├── config             # 仓库的配置文件
├── description        # 仓库描述信息
├── HEAD               # 指向当前所在分支
├── hooks/             # Git 钩子脚本(各种事件的触发器)
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   └── ...
├── index              # 暂存区(执行 git add 后的数据存这里)
├── info/
│   └── exclude        # 排除文件(类似 .gitignore)
├── logs/              # 日志目录
│   ├── HEAD
│   └── refs/
│       ├── heads/
│       │   └── master
│       └── remotes/
│           └── origin/
│               └── HEAD
├── objects/           # 对象数据库(核心!存储所有提交、文件、目录快照)
│   ├── 13/
│   ├── 25/
│   ├── 34/
│   ├── be/
│   ├── ec/
│   ├── info/
│   └── pack/
└── refs/              # 引用目录(分支和标签的指针)
    ├── heads/
    │   └── master     # master 分支指向的 commit id
    ├── remotes/
    │   └── origin/
    │       └── HEAD
    └── tags/

1.2 objects 目录------Git 存储的核心

objects 目录是 Git 最核心的数据存储区。每执行一次 git commit,Git 就会在这个目录下生成新的对象文件。

1.2.1 提交前的 objects 目录

刚克隆下来、还没做任何修改时:

bash 复制代码
tree .git/objects/
# 输出:
# .git/objects/
# ├── 13
# │   └── 9f53c10d9e66117708c69f539629205b9e3335
# ├── 25
# │   └── 9148fa18f9fb7ef58563f4ff15fc7b172339fb
# ├── 34
# │   └── 2354e19393ccfe2e55459fac052a97f2a8d77e
# ├── be
# │   └── 615096e37bae01c3e5397c3ead42d2e03151b3
# ├── ec
# │   └── 5af8158d994d41b190fdc250c874767f40b2f4
# ├── info/
# └── pack/
# 7 directories, 5 files

此时有 5 个对象文件,对应的是初始化仓库时创建的那 3 个文件(.gitignoreREADME.en.mdREADME.md)以及相关的目录树和 commit 对象。

1.2.2 第一次提交后的 objects 目录

执行 git add .git commit -m "进度条代码" 后:

bash 复制代码
tree .git/objects/
# 输出:
# .git/objects/
# ├── 13/9f53c10d9e66117708c69f539629205b9e3335
# ├── 17/6ab6ee35e1d27c2295446062de862656426447
# ├── 25/9148fa18f9fb7ef58563f4ff15fc7b172339fb
# ├── 27/ef2e426ab3bbf778c1df47b81290b33c4c157f  ← 新增:commit 对象
# ├── 34/2354e19393ccfe2e55459fac052a97f2a8d77e
# ├── 48/c423dad011ccd16a051a587133f8bdee7c2077  ← 新增
# ├── 86/1b0919484f89a322b8319edeabfe2e309cbc1e  ← 新增
# ├── 96/58abe1609acf32bf2ca7d368894ee830b2d2c8  ← 新增
# ├── 99/4008d3fd6e9baf56c8421b26fc3f45d3f8728b  ← 新增
# ├── be/615096e37bae01c3e5397c3ead42d2e03151b3
# ├── c9/615096e37bae01c3e5397c3ead42d2e03151b3  ← 新增
# ├── ec/5af8158d994d41b190fdc250c874767f40b2f4
# ├── info/
# └── pack/
# 13 directories, 11 files

可以看到,提交后 objects 目录下新增了多个对象文件。这些新增的对象包括:

  • blob 对象:存储每个被修改文件的具体内容。
  • tree 对象:存储目录结构(哪些文件在哪些目录下)。
  • commit 对象:存储提交信息(作者、时间、日志、父 commit 引用等)。

1.2.3 再次修改提交后的 objects 目录

再修改 main.c,再次 git add .git commit -m "修改"

bash 复制代码
tree .git/objects/
# 输出:
# .git/objects/
# ├── 13/9f53c...
# ├── 17/6ab6ee...
# ├── 25/9148fa...
# ├── 27/ef2e42...
# ├── 34/2354e1...
# ├── 48/c423da...
# ├── 86/1b0919...
# ├── 96/58abe1...
# ├── 99/4008d3...
# ├── be/615096...
# ├── c9/99c8aa18970d8989490d792f8aa705b5e0258d  ← 新增:新的 commit 对象
# ├── db/849ff6e51595b0f3649a6016706a5f4bb81f34  ← 新增:新的 blob 对象(main.c 新内容)
# ├── ec/5af815...
# ├── info/
# └── pack/
# 16 directories, 14 files

每次提交都会生成新的对象文件,而旧的对象文件永远保留,这就是 Git 版本追溯的底层原理。

1.3 Commit ID 的本质

每次提交产生的 Commit ID (也叫修改 ID),比如 27ef2e4db849ff,这些看起来像乱码的短字符串,本质上就是 objects 目录下对应文件夹的名字。

比如 commit ID 是 27ef2e426ab3bbf778c1df47b81290b33c4c157f,对应的文件路径就是:

复制代码
.git/objects/27/ef2e426ab3bbf778c1df47b81290b33c4c157f

Git 把完整的 40 位 SHA-1 哈希值的前 2 位作为目录名,后 38 位作为文件名,这样做的目的是避免单个目录下文件过多导致性能下降

二、查看提交历史:git log

2.1 基本用法

bash 复制代码
git log

输出示例:

复制代码
commit db849ff6e51595b0f3649a6016706a5f4bb81f34
Author: 云泽 <2392709001@qq.com>
Date:   Mon Sep 7 14:36:08 2026 +0800

    修改

commit 27ef2e426ab3bbf778c1df47b81290b33c4c157f
Author: 云泽 <2392709001@qq.com>
Date:   Mon Sep 7 14:29:50 2026 +0800

    进度条代码

commit 342354e19393ccfe2e55459fac052a97f2a8d77e
Author: 云泽 <2392709001@qq.com>
Date:   Mon Sep 7 05:34:04 2026 +0000

    Initial commit

2.2 日志中的关键字段解析

每条提交记录包含以下信息:

  • commit:提交 ID(修改 ID),用于唯一标识这次提交。未来如果想恢复到某个历史版本,就是用这个 ID 来操作。
  • Author :作者信息,显示的是 git config 中配置的用户名和邮箱。
  • Date:提交时间。
  • 日志信息(最后一行) :就是 git commit -m 后面写的内容。

2.3 日志信息的重要性

日志信息绝对不能胡写!

好的日志应该清晰描述这次提交做了什么。比如:

  • "修复用户登录时密码校验的边界条件bug"
  • "新增进度条模块:process.c/process.h"
  • "修改"
  • "111"
  • "asdfgh"

混乱的日志会被同行鄙视。更严重的是,面试官可能会去查看你的 GitHub/Gitee 提交记录,如果满屏都是"修改""123""测试",印象分会大打折扣,且在实际业务工作中乱写日志,大概率是会被同事问候的。

三、版本恢复与底层原理

3.1 Git 只记录"修改动作"

Git 不会保存文件的完整快照,而是只记录每次修改了什么。比如:

复制代码
第五行  删除  XXXX
第六行  修改  mian -> main
第七行  增加  printf("helloworld");

Git 记录的是"第5行删除了什么、第6行把mian改成了main、第7行增加了printf",而不是把整个文件重新存一份。这种方式极大地节省了存储空间。

四、Git 忽略文件机制与 .gitignore 详解

4.1 .gitignore 的核心作用与配置逻辑

在 Git 版本控制中,.gitignore 是一个至关重要的配置文件。它的核心功能非常明确:告诉 Git 哪些文件或目录不需要被跟踪,也不需要被提交到仓库中。在实际开发中,我们的项目目录下往往充斥着大量的编译产物、临时文件、日志文件或者特定操作系统的配置缓存。如果把这些文件都提交上去,就会让仓库体积迅速膨胀

.gitignore 文件的编写遵循通配符规则。例如,使用 *.o 可以忽略所有后缀为 .o 的目标文件,*.exe 可以忽略所有可执行文件,*.log 则能过滤掉日志文件。当我们在终端执行 git add . 命令时,Git 会自动读取当前目录下的 .gitignore 规则,凡是匹配到的文件,Git 都会视而不见,不会将它们加入暂存区。

结合在 Visual Studio 等 IDE 中开发 C++ 项目的实际场景,这一点尤为重要。

当我们创建一个名为 test_09_06_01 的项目时,IDE 会自动生成一系列辅助文件。除了核心的源代码 test.cpp 外,还会产生 test_09_06_01.vcxproj(项目工程文件)、test_09_06_01.vcxproj.filters(筛选器文件)以及 test_09_06_01.vcxproj.user(用户个人配置)。更关键的是,编译过程中会生成 x64Debug/Release 这样的文件夹,里面存放着 .obj.exe.tlog 等中间产物。

对于团队协作而言,只有 test.cpp 这样的源代码是必须共享的。像 x64 文件夹里的内容,或者是 .vcxproj.user 这种包含个人路径配置的敏感文件,绝对不应该进入版本库。因此,我们需要在根目录下创建一个 .gitignore 文件,写入如下规则:

gitignore 复制代码
# 忽略 Visual Studio 编译输出目录
x64/
Debug/
Release/

# 忽略编译产生的中间文件和可执行文件
*.obj
*.exe
*.tlog
*.log

# 忽略用户个人配置文件
*.vcxproj.user

通过这样的配置,无论你在 Windows 下如何编译,执行 git status 时,那些杂乱的编译文件都不会再出现,仓库始终保持干净整洁。

4.2 规则生效验证与"已跟踪"陷阱

在使用 .gitignore 时,还有一个极易踩坑的细节需要注意:.gitignore 只能忽略那些从未被 Git 跟踪过的文件。

如果一个文件已经被 git addcommit 到了仓库中,也就是它已经处于"已跟踪"状态,那么此时你再在 .gitignore 中添加针对该文件的忽略规则,是完全无效的。Git 会继续跟踪该文件的后续修改。

例如,假设你之前不小心把 test.txt 提交到了仓库,后来觉得这个文件不该被管理,于是你在 .gitignore 里加了 *.txt。你会发现,当你修改 test.txt 后,git status 依然会提示你有修改未提交。这是因为 Git 的索引中已经记录了该文件。

要解决这个问题,必须先将文件从 Git 的缓存区(Index)中移除,但保留本地文件。正确的操作步骤如下:

bash 复制代码
# 从 Git 追踪列表中移除文件,但不删除本地物理文件
git rm --cached test.txt

# 或者批量移除所有已跟踪但现在需要忽略的文件
git rm -r --cached .

# 重新添加文件(此时 .gitignore 规则才会生效)
git add .

# 提交更改
git commit -m "Remove tracked files and apply gitignore rules"

只有执行了 git rm --cached 之后,.gitignore 中的规则才会真正对该文件生效。

五、Git Push 原理与远程仓库别名机制

5.1 git push 与 git push origin main 的区别

很多人在使用 Git 推送代码时会有疑问:为什么有时候直接敲 git push 就能成功,而有时候在别的教程里却写着 git push origin main?这两者到底有什么区别?

其实,git pushgit push origin main 的一种简写形式,但这种简写是有前提条件的。

当你通过 git clone 命令从远端克隆一个仓库到本地时,Git 会在本地自动做两件事:

  1. 记录远端地址 :它会把你克隆来源的那个 URL 地址记录下来,并默认赋予一个别名叫 origin
  2. 建立分支关联 :它会自动将本地的 main(或 master)分支与远端的 origin/main 分支建立"上游"关联。

所以,在这个标准场景下,你输入 git push,Git 实际上是在帮你补全参数,它在心里想的是:"哦,你要推送到默认的 origin 远端,推送到当前分支对应的远程分支去。"

但是,如果你的本地仓库不是克隆来的,而是自己在本地通过 git init 初始化的,或者你需要将代码推送到多个不同的远端仓库(比如同时推送到 Gitee 和 GitHub),那么 git push 这种简写可能就会失效或产生歧义。这时,你就必须显式地指定远端别名和分支名称:

bash 复制代码
# 显式指定推送到 origin 远端的 main 分支
git push origin main

这里的 origin 并不是什么关键字,它只是一个约定俗成的默认别名。你可以把它改成任何名字,比如 giteegithub 或者 my_server

六、Git 多平台协同开发与冲突解决实战

6.1 双平台开发场景与核心痛点

在真实的软件开发过程中,我们很少只在一个操作系统下工作。比如你可能习惯在 Windows 下写代码;但到了服务器部署或者某些特定开发环节,又必须在 Linux 环境下操作。更常见的情况是,团队成员之间有人用 Windows,有人用 Linux,大家需要协同开发同一个项目。

这就引出了一个非常现实的问题:当 Windows 和 Linux 两个平台同时向同一个远程仓库(如 Gitee)提交代码时,如何保证代码的一致性?

我们假设一个场景:张三在 Linux 上写代码,李四在 Windows 上写代码,他们都要把代码提交到同一个 Git 仓库。这时候,如果处理不好,就会出现"两个账号"或者"两个本地版本"打架的情况。我们需要解决的核心问题就是:如何让两个不同平台的本地仓库,与远端仓库保持同步,并在发生修改冲突时正确合并。

6.2 Windows 环境下的 Git 操作基础

对于习惯图形化界面的同学来说,Windows 下的 Git 操作其实非常友好。当你安装好 Git 后,在任意文件夹(例如桌面上的 test_code 目录)单击鼠标右键,你会看到菜单中多了 Git GUI HereGit Bash Here 等选项。

6.2.1 Git Bash:类 Linux 的命令行体验

点击 Git Bash Here,会弹出一个黑色的命令行窗口。这个工具非常强大,它模拟了 Linux 的终端环境。

  • 目录定位 :它默认打开的目录就是你右键所在的目录。比如你在 D:\programming_file\code\test_code 下右键打开,命令行提示符就会显示在这个路径下。
  • 常用命令 :你可以像在 Linux 里一样使用 pwd 查看当前路径,使用 ls 查看文件列表。
  • 避坑指南 :在克隆仓库时,千万不要在一个已经包含 .git 隐藏文件夹的目录下再次执行 git clone。这会导致仓库嵌套,引发严重的干扰和错误。建议新建一个干净的文件夹(如桌面的新目录)来进行克隆操作。

6.2.2 TortoiseGit:可视化的版本控制

除了命令行,Windows 下还有一款神器叫 TortoiseGit (小乌龟)。这在我的另一篇Git文章中有详细的安装教程。安装后,右键菜单会出现更丰富的选项,如 Pull...(拉取)、Push...(推送)、Commit -> "master"...(提交)等。

  • Push 操作细节 :当你选择 Push... 时,会弹出一个配置窗口。
    • Local(本地分支) :通常选择 master
    • Remote(远程分支) :也对应选择 master
    • Destination(目标) :选择 origin,即你克隆时绑定的远程仓库地址。
    • 点击 OK 后,如果一切顺利,进度条走完显示 Success,就说明推送成功了。
  • Pull 结果查看 :拉取成功后,TortoiseGit 会弹出一个 "Git Command Progress" 窗口,底部有一个下拉框可以选择 Pulled Diff。点击它可以直观地看到这次从远端拉取下来哪些文件发生了变化,比如新增了 code.c 文件,插入了 7 行代码。

6.3 协同开发的标准工作流

为了保证团队协作不乱套,我们必须遵守一个铁律:远端仓库永远是最新的权威版本

6.3.1 正常提交流程(无冲突)

假设张三(Linux)先提交了一个 code.c 到 Gitee。此时李四(Windows)想提交自己的 wcode.c

  1. 李四的操作 :李四在本地写好 wcode.c,执行 git add .git commit -m "Windows提交代码"
  2. 尝试推送 :李四执行 git push
  3. 关键判断
    • 如果李四在写代码期间,没有人更新过远端仓库,那么 push 会直接成功。
    • 但是 ,如果张三刚刚提交了新代码,李四的本地仓库就"落后"了。此时直接 push 会失败,报错提示 rejected(被拒绝),因为远端有李四本地没有的新内容。
  4. 正确做法 :李四必须先执行 git pull。这步操作会把张三提交的 code.c 拉取到李四的电脑上,并自动进行合并(Merge)。合并成功后,李四的本地仓库就包含了张三的代码和自己的代码,此时再执行 git push,就能成功上传了。

6.3.2 验证同步结果

当我们完成上述操作后,可以登录 Gitee 网页端查看。你会发现仓库里既有了张三提交的 code.c(提交信息显示 "code fix"),也有了李四提交的 wcode.c(提交信息显示 "Windows提交代码")。这就实现了跨平台的完美协同。

6.4 深入理解代码冲突(Conflict)

刚才说的是大家修改不同文件的情况,那如果张三和李四同时修改了同一个文件 (比如都改了 Linuxcode.c),会发生什么?这就是最头疼的代码冲突

6.4.1 冲突的产生

  1. 场景重现
    • 张三在 Linux 上修改了 Linuxcode.c,添加了一行打印 "hello Linux",并提交推送到远端。
    • 李四在 Windows 上也修改了 Linuxcode.c,添加了 "我是windows程序员",并尝试推送。
  2. 报错分析
    • 李四推送时,终端会显示 ! [rejected] master -> master (fetch first)
    • 错误信息明确指出:Updates were rejected because the remote contains work that you do not have locally.(更新被拒绝,因为远端包含你本地没有的工作)。
    • 系统提示你:You may want to first merge the remote changes (e.g., 'git pull') before pushing again.(你可能需要先合并远端更改)。

6.4.2 解决冲突的步骤

当李四执行 git pull 试图同步时,Git 会发现两个人都改了同一个文件的同一部分,它不知道该听谁的,于是报错:

CONFLICT (content): Merge conflict in Linuxcode.c

Automatic merge failed; fix conflicts and then commit the result.

这时候,你需要手动介入:

  1. 打开冲突文件 :用编辑器(如 Vim 或 VS Code)打开 Linuxcode.c。你会看到 Git 留下的特殊标记:

    c 复制代码
    <<<<<<< HEAD
    这是Linux程序员修改的代码
    =======
    我是windows程序员,下面是我写的代码
    >>>>>>> a4ef795a02c4736ee2f9de62140b99289d0fee2c
    • <<<<<<< HEAD======= 之间:是你当前本地分支(李四)的代码。
    • =======>>>>>>> 之间:是远端拉取下来(张三)的代码。
  2. 手动决策 :你需要决定保留哪部分代码,或者把两部分都保留。编辑文件,删除那些 <<<<====>>>> 标记符号,只保留合法的 C 语言代码。

  3. 重新提交

    • 保存文件。
    • 执行 git add .(告诉 Git 冲突已解决)。
    • 执行 git commit -m "merge"(生成一个合并提交记录)。
    • 最后执行 git push,将解决冲突后的最终版本推送到远端。

6.5 常用命令速查与辅助工具

为了能让大家更流畅地使用 Git,这里整理了其余几个高频命令和技巧。当然,在Linux部分的 Git 内容中只是一些基础的使用方法,只是在实际公司业务中够用的程度,Git 是一个非常强大的工具,如果想要更深入的了解 Git ,请关注博主后续更新的 Git 专栏内容~

6.5.1 核心命令清单

除了基础的 add, commit, push, pull, clone,以下命令也非常重要:

  • git log:查看提交历史记录,了解谁在什么时候改了什么。
  • git status:查看当前工作区状态,有哪些文件被修改了但还没 add。
  • git diff:查看具体修改了什么内容。
  • git --help <command>:如果你忘了某个命令怎么用,比如 git --help add,它会直接列出该命令的详细帮助文档。

结语

相关推荐
Elastic 中国社区官方博客1 小时前
教程:使用 ES|QL 进行威胁狩猎
大数据·运维·数据库·elasticsearch·搜索引擎·全文检索·安全威胁分析
weixin_505061451 小时前
原厂技术协同|世强硬创携手星坤连接解锁 IOTE 高可靠互连解决方案
大数据
乐迪信息1 小时前
如何通过AI防爆摄像机精准判断船舶超速?
大数据·人工智能·算法·安全·目标跟踪
ITxiaobing20232 小时前
广告归因场景下的IP情报工程化:提升AppsFlyer P360匹配精度的实践思路
大数据·人工智能·tcp/ip
爱吃糖醋红烧肉2 小时前
谷歌SEO与独立站建站双启动:出海品牌企业的增长引擎
大数据·谷歌seo·外贸建站·独立站seo·谷歌建站·外贸seo·白帽seo
LDR0062 小时前
USB-C接口投屏能力全辨别指南及LDR6020P芯片解决方案解析
大数据
广凌股份(广凌科技)3 小时前
2026年高校采购管理系统选型指南 | 5款软件深度测评
大数据·人工智能
张继雁4 小时前
张继雁 个人技术简介|磨削加工过滤方向
大数据·数据库·论文阅读·人工智能·机器学习·创业创新·业界资讯
AIwenIPgeolocation4 小时前
埃文科技签约中部(河南)Token产业运营平台 携手中国电信共创AI新格局
大数据·人工智能·科技