1 Star 0 Fork 11

coder_lw / wiki

forked from deepinwiki / wiki 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
git 手册.md 30.85 KB
一键复制 编辑 原始数据 按行查看 历史
htqx 提交于 2023-05-14 18:12 . 文件就是结构

git 手册

前言

git 是现在最常用的同步和版本管理工具。同步就是将两地的文档版本同步,一个中心(git 仓库网站/服务器),多地编辑。版本管理是每一次修改,提交,都会形成一个新的版本,新旧版本直接可以跳转回溯,甚至生成分支。

新建仓库(Repository)

  1. git init 目录 :普通仓库
  2. git init --bare 目录 :裸仓库
  3. git clone 源 目录 :克隆创建

所谓的仓库,实际上是一个配置了 .git 子目录的普通目录,里面的内容是任意的,仓库的信息存在 .git 子文件夹。普通仓库会显示文档内容,可以对文档进行编辑。

而所谓的裸仓库,就是只有 .git 信息内容的目录(即目录内容就是普通仓库的 .git 子目录),即只有信息,而没有文件视图,不能对存储的文档进行编辑。

仓库服务器,一般是创建裸仓库(bare repo),因为它的作用是给异地的普通仓库同步用的。

git 的管理逻辑是:

  1. 目录中的普通文档和 .git 子文件夹(即本地仓库)同步(本地操作)
  2. 普通仓库和裸仓库同步 (和本地另一个目录自建的裸仓库同步)
  3. 普通仓库和服务器同步 (或者和异地服务器同步)

仓库和仓库之间,严格来说内容是一样的(在同步之后),这种架构叫分布式版本管理。

流程:

  1. 一个裸仓库可以对应多个普通仓库(客户端)
  2. 普通仓库会显示文档文件,用户编辑文档之后执行同步命令,就和 .git 子目录的本地仓库信息进行交互和同步
  3. 用户执行远程同步命令,就会将本地仓库存在的新版本提交到服务器上的裸仓库。
  4. 另一个普通仓库的用户执行同步命令,就会从服务器上的裸仓库下载新版本,然后和本地个 .git 子文件夹(本地仓库)对比,合并新版本信息。
  5. 目录存在的文件切换到新版本的内容,即替换现有的旧文档,用户就能及时的看到新的文档内容。

所以裸仓库的作用就是同步和保存信息,而不是编辑文档内容。而普通仓库的作用是提取当前本地仓库需要修改的版本到目录中,用户可以直接编辑和更改文件内容。

例子:

# 普通仓库,可以编辑文档
git init proj-a
# 裸仓库,用于同步共享多个普通仓库的版本信息
git init --bare shared-proj-a

# 查看服务器上的远程仓库
# -h 只列出分支信息
git ls-remote -h https://github.com/ratfactor/ziglings

# 将远程仓库克隆到本地 proj-b 目录下
# proj-b 成为一个普通仓库
# proj-b/.git 是本地仓库的所有信息,它内容等同于远程的仓库
# --depth=1 只要目标版本,而不下载其他关联版本,提高下载速度
# -b 分支
git clone --depth=1 -b main https://github.com/ratfactor/ziglings proj-b

配置本地仓库(工作目录)

  1. git config 字段 值:配置仓库信息(当前仓库)
    1. ./.git/config :配置文件
  2. git config --global 字段 值:用户级配置(本用户的所有仓库)
    1. ~/.gitconfig:用户主配置文件
  3. git config --system 系统级(所有用户)的配置。
# ./.git/config 的一个例子
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[user]
        name = htqx

        email = xxx@sina.com
[remote "deepin.wiki"]
        url = git@github.com:deepinwiki/wiki.wiki.git
        fetch = +refs/heads/*:refs/remotes/deepin.wiki/*
[branch "master"]
        remote = deepin.wiki
        merge = refs/heads/master
[remote "gitee"]
        url = git@gitee.com:deepinwiki/wiki.git
        fetch = +refs/heads/*:refs/remotes/gitee/*
# ~/.gitconfig 的例子
[user]
        email = xxx@163.com
        name = rufeng
[core]
        quotepath = false
[http]
        proxy = socks5h://127.0.0.1:1080
# 使用命令来设置配置
git config --global user.name "htqx"
git config --global user.email "xxx@qq.com"

# 取消某个设置
git config --unset http.proxy

# 编辑配置文件
git config --edit

关联远程仓库

  1. git remote -v :显示当前关联的远程仓库地址
  2. git remote add 标识 地址:关联一个远程仓库
    1. origin : 使用 clone 克隆的仓库,会自动生成一个 origin 标识的关联源仓库地址的名称

工作模式:git 关联远程仓库并不会自动处理同步,一切都要靠用户自己主动提交信息和同步信息。

例子:

cd proj-a 
git remote -v
# 和本地文件夹的裸仓库建立联系
git remote add shared ../share

# 查看远程源设置的相关命令
git remote -h
# 配置文件发生变化
# .git/config
[remote "shared"]
        url = ../shared-proj-a/
        fetch = +refs/heads/*:refs/remotes/shared/*

关联远程分支

以上说明只是建立 url 关联,它可以获取共享目录的信息。但现在,本地目录,和共享目录还是空的。所以我们实际上要创建分支,然后关联分支,才能同步版本。

  1. git branch -a : 查看当前分支,默认会创建一个 master 名称的主分支。
  2. git branch master -u origin/master : 当远程仓库 origin 存在master 分支时,可以使用这个命令绑定
  3. git push -u origin/master : 当远程仓库不存在,将推送(新建)远程分支 master,并绑定当前的分支 master

我们需要做的事情是将本地仓库的 master 绑定到远程仓库,即 origin 远程仓库上的 master 分支。

分支的名字是可以随意命名的,也不要求本地仓库的分支名字和远程仓库的分支名字一致。但建议使用 master 来表示当前的主要分支。

# 默认工作目录会创建一个 master 分支,只需要添加文件即可
touch a # 创建 a 文件
git add a # 添加到 git 仓库,否则 git 并不会跟踪这个文件的变化
git commit -m "create a" a # 提交文件 a 的当前版本(状态)到本地仓库,否则 git 不会认为发生了变化

# -u 关联远程分支。
# 也就是将本地 master --> shared/master 推送,并建立默认关联
# 这样下次在 master 分支下,只需要 git push 就推送最新版本到共享目录
# git pull 反向操作
git push -u shared master 

也可以直接修改配置文件:

# .git/config
# 本地 master 分支的配置
[branch "master"]
        remote = shared # 远程仓库
        merge = refs/heads/master # 关联的远程仓库上的分支

基本工作流

  1. git add 文档:标记文档,准备将其内容同步到 .git 本地仓库
  2. git commit :正式提交到 .git 本地仓库
  3. git status :当前文档状态,这是一条需要经常使用的命令,建议在任何操作之前使用,以免发生预想不到的情况

注意。工作目录(文档视图)和 .git 仓库子目录是两个地方。用户编辑的文件首先要提交给 .git 子目录,它相当于在本地的一个版本管理系统。和异地同步是后面的事。

当你添加一个新的文件到目录中,.git 是还没有该文件的信息,这种状态称之为”未跟踪“。.git 有一个该文件的版本,这种情况为已跟踪。

git add 标注一下这次要更新的文件(新建文件或发生变化的文件),一次更新即 git commit 可以是多个文件。

git 是不会自动将文档的变化提交到 .git ,每次修改后,都需要用户主动 git add 标记和主动提交 git commit。

因此,这就造成了目录下的文档存在这几种情况:

  1. 未跟踪(Untrack):不会被同步,也没有版本的概念。
  2. 已跟踪,但没有变化(Unmodified)。版本和仓库中记录的内容一致。
  3. 发生变化(Modified),需要用户人工提交成新的版本(还没提交)。
  4. 已暂存(Staged),使用 git add 添加到暂存区后的状态。
  5. 主动提交:文档的变化会被同步,创建一个新的版本,.git 和主目录,暂存区三者的版本一致,即变成第二步。

事实上,git 的正式提交是分阶段的:

  1. 修改文档 a,产生内容差异
  2. git add 保存到临时暂存区(staged)
  3. git commit : 提交暂存区(staged)的内容到 .git 本地仓库

主目录(工作区 Working Directory) --> 暂存区(Staging Area/索引 Index) --> .git(Local Repository) --> 远程仓库(Remote Repository)

可能现在还是对这些术语了解不深,因为还没有掌握相关的操作命令,但是最终会形成这个模型,而命令就是将数据从这些对象中搬来搬去的作用。因为是翻译文档,中文版的说法可能不是很统一,所以也要关注英文的写法是什么,方便以后在互联网上搜索信息。

当然现在只需要记住需要使用这两个命令来更新也可以了。

为了查看目录处于那种状态,git status 命令将会给出信息,并且还会给出相关命令的提示。

# 追加内容到 a
echo "aaa" >> a
git status # 查看本地仓库状态,这是个好习惯
# 这时会提示让你 git add 修改后的 a
git add a
# -m 记录在当前版本的简短描述
git commit -m "add aaa"

# 将上述两步合并到一起的命令
git commit -m "add aaa" - a 

这里可以直接用 git commit 的参数来提交,省去 add 这一步:

  1. -a : 提交所有变化的文档(只限被跟踪的)
  2. git commit a : 提交指定文档

可以针对一个文件提交,也可以针对几个文件一起进行提交。git 能够分别记录每一个文件的版本变化。一次提交就会生成一个版本。git 接下来可以回溯到旧的版本,这就是 git 的主要目的。

echo "bbb" >> a
git add a
echo "ccc" >> a
git status # 这时,工作区和暂存区(staged)不一致,工作区比暂存区(staged)更新

这时,git status 会提示你:

  1. git restore --staged a : 取消暂存。这里意思比较隐晦。restore 是恢复的意思, --staged 指暂存区,联合起来就是取消暂存。实质清空暂存区。它等于 git add 的逆操作。
  2. git restore : 用本地仓库替换掉当前工作区,也就是当前修改的内容会丢弃(危险操作)
  3. git add a : 提交当前变化到暂存区

注意: 在 git 中,什么操作是危险的,什么操作是安全的?当你修改后提交(commit),形成了一个版本记录后,这个修改的内容就会永久(几乎)的被记录下来,你再怎么修改,删除,都能通过某些手段恢复当前的版本,这种操作就是安全的。

反之,当还没有提交前的修改,变了就是变了,删了就是删了,就无法再次恢复。

git restore a
cat a # a 没了 ccc
git commit -m "add bbb" # 新的版本,添加了 bbb

相关命令:

  1. 工作区 --> 暂存区(staged)
    1. git add 文档/目录
    2. git add -u :添加当前修改的所有文档
  2. 暂存区(staged) --> 工作区
    1. git restore 文档(危险)
  3. 暂存区(staged) --> 本地仓库
    1. git commit -m 提交说明
  4. 本地仓库 --> 暂存区(staged)
    1. git restore --staged 文档
  5. 本地仓库 --> 暂存区和工作区
    1. git restore -WS 文档(危险)
  6. 删除文件
    1. git rm 文档
    2. git rm --cached 文档:删除暂存区(staged)的文档,并取消跟踪,但保留工作区的文档
  7. 移动和改名:git mv 文档 新名字
  8. 查看文件状态: git status
  9. 查看文件修改的内容:git diff

当一个文件被跟踪后,应该是用 git rm 删除,而不应该使用 rm 删除。因为 rm 只是从目录中删除, .git 本地仓库中是缺乏这种信息(那么你就无法提交到远程仓库中,让其他用户知道你删除了文件)。

重命名实质是 git rm 旧文件,然后 git add 新文件。

版本创建和跳转

版本的概念:

  1. 每一次正式提交都是一个新版本。
  2. git 管理的文件可以在不同版本间随意跳动

没创建版本的本地修改可能会丢失,而一旦提交,创建了版本,就不会丢失数据。

版本跳转会影响:

  1. 目录(工作区)
  2. 暂存区(staged)(索引 index)
  3. 仓库版本指针HEAD:默认指向当前分支
    1. master:指向当前分支最终的提交(版本)
      1. commit:即某个版本,一个提交对应一个版本,两者是一个意思
    2. commit:可以直接让 HEAD 指向任一提交,这时处于”头指针分离“状态
  4. 远程仓库分支 shared/master。

意思是,如果改变版本,就等于仓库头指针 HEAD 指向某个版本,获得这个版本下所有文件的状态和内容。改变仓库版本很容易理解,但如果当前工作区,暂存区(staged)和 .git 的版本不一致(说明用户进行了修改),那么就要结合暂存区(staged)和目录就需要仔细思考其中的差异:

  1. 改变仓库版本(即改变 HEAD):git reset --soft 提交,其中“提交”是 git commit 之后得到的一个散列值,用来标识一个版本。
    1. 仓库变
    2. 目录不变
    3. 暂存区(staged)不变。
  2. 改变仓库、暂存区(staged):git reset 提交
    1. 仓库变
    2. 目录不变
    3. 暂存区(staged)变(即需要git add,因为仓库和暂存区(staged)一致,也就是和工作区不一致)。
  3. 改变仓库、暂存区(staged)、目录:git reset --hard 提交
    1. 三者统一,跳转到指定版本,但之前对目录中文档的修改都丢失(危险,--keep 保持当前修改)。

知道工作原理,还要知道应用场景。

当我们想切换版本,最好先保证三者统一(意思就是先提交修改 git commit,创建新版本,三者自然统一在这个新版本之下,然后再跳转版本),这样就不需要考虑工作区修改,或者暂存区(staged)修改的情况了。

任何时候:跳转安全与否,在于当前的修改有没有被提交。每一次提交都有一个代表该版本的哈希值,只要你能记住,永远可以依赖这个哈希值,回到那个版本。这也是版本管理的基本抓手。

相关命令:

  1. 跳转版本:git reset --hard 提交(危险)
  2. 头指针分离:git checkout -d 提交
  3. 查看提交历史: git log
    1. HEAD : 头指针开始
    2. HEAD~2 : 前两个版本
    3. shared/master..master : 同步到最新的历史
    4. ^HEAD : 不包含 HEAD 的历史
    5. -- a : 针对特定文件的历史
    6. -L 1,3:a : 文件 a 的几个版本的变化历史
    7. --oneline : 一行内显示
    8. --pretty="%an %ar:[ %s ]-- %h" : 格式化输出
  4. gitk: 图形方式显示历史

git 向前回溯历史是很简单的,但是向后移动版本就比较难,如果用 git reset 回到老版本,他会同时将分支指向那个老版本,那么怎么回去当前版本是个问题。

如果你在当前分支下进行版本操作,最好用 git checkout -b 提交 这种方式,强制头指针 HEAD 单独指向某一个版本,而不是关联某个分支,它不会导致分支指针的变化。

git status # 做跳转前,最好先观察文件状态
cat a
git checkout shared/master # 回到上一次同步的位置
cat a # 文件已经清空
git checkout master # 回到当前分支顶端
cat a # 内容又回来了,这就是版本跳转的神奇之处
git checkout HEAD~1 # 表示头指针指向的上一个版本,
cat a # aaa
git log --oneline # 查看历史,可以看到每个提交对应一个 5e8daa6... 之类的一串数字
git checkout 5e8daa6 # 提供若干位即可跳转到指定提交版本
git checkout master # 恢复到当前分支顶端

如果我们跳到某个历史版本中,又修改了文件,将会发生什么?这就是接下来要说的分支。

分支

仓库头指针 HEAD 指向当前的分支,而分支指向当前分支最近的版本,这是一般状态。

  1. 仓库指针
    1. HEAD
      1. 分支 master
        1. 版本(最近版本)
      2. 分支 test
        1. 版本
      3. 版本(头指针分离)
    2. FETCH_HEAD(远程源指针)
      1. 分支 master
      2. 分支 release

分支本质就是一个指针,它指向一个提交(commit),当有新的提交,就更新这个分支指针,指向新的提交。一个提交就是一个版本,提交记录了上一个提交的位置信息,所以可以回溯历史(用上一节的 checkout),分支只需要记录终点版本。

为何需要分支?比如一些大型项目,它都是同时开发几个版本的,一些版本比较激进,一些版本比较稳定,面对不同的消费者。

另一个例子,如果历史中的某个版本,发现了错误,那么就需要添加修补补丁。这些版本的消费者不愿意升级到最新版本,但是开发者还是需要提供修补错误的补丁的。

再一个例子,对当前版本的前景不明朗,可能会尝试一些新的方法,但是又想保留当前分支,可以作为后退的立足点。这时候就可以新建一个分支作为尝试。

诸如此类的例子,说明我们业务上确实需要分支。

首先需要理解分支的状态。分支就相当于版本的一组序列,有起点和终点。分支的集合,就类似一颗多个枝条的大树,在某个地方分叉出一个新的分支。

具体形态有:

  1. 起点是别的分支的一部分:同根分支
    1. 否:异根分支
  2. 终点是别的分支的一部分:已合并分支
    1. 否:未合并分支
  3. 起点和终点与别的分支无关:独立分支(异根且未合并)
    1. 否:相关分支
git status # 做操作前先了解当前文件状态
# 创建 test 分支(始于当前位置)
git branch test
git switch test
# 设置上游(远程仓库)
# shared 是远程仓库的标识
# test 是想推送的本地分支
# :release 是想对送到远程仓库的哪个分支
# -u 设置绑定关系,即 test <--> shared/release
git push -u shared test:release 
# 现在就有了一个新的分支
echo "ccc" >> a
git commit -m "add ccc" a # 做一些修改工作,生成新的版本
# 后悔了,切换回主分支
git status 
git switch master
echo "ddd" >> a
git commit -m "add ddd" a # 在主分支上创作,延展一些新的版本
# 从而两个分支出现了独立发展的趋势

用户可以随意在两个分支中切换(前提是先保存好当前状态,避免丢失数据)。

相关命令:

  1. 创建分支:git branch 分支名 起点
  2. 删除分支:git branch -d 分支名
    1. git branch -D 分支名:强制删除,哪怕该分支还没合并(注意)
  3. 移动分支:git branch -m 旧名 新名
  4. 拷贝分支:git branch -c 老分支 新分支
  5. 和远程分支关联:git branch -u 上游分支 本地分支名: 修改关联
    1. git fetch 远程仓库 : 下载远程仓库的分支信息(远程已有时)
      1. git checkout -b 本地分支 远程仓库/分支 : 快捷创建对应的本地分支
    2. git push -u 远程仓库 本地分支:远程分支 : 设置关联(先在本地创建时)
  6. 查看分支:git branch -avv
  7. 切换分支:git switch 分支
    1. git checkout 分支

合并分支

创建分支很简单,但是要合并分支就要处理相关的合并切入点和内容冲突:

  1. 快进合并(fast-worward):当前分支是需要合并的分支的祖先,即都老于需要合并的版本,就可以简单的把当前分支的指针指向合并版本。快进合并不会产生新的提交(版本)。
    1. git mergegit pull 会开启合并流程,不能快速合并会提示用户人工干预。git merge --ff-only 只尝试快速合并。
    2. 中止合并:git merge --abort,如果出错,可以用这个取消整个合并
  2. 三方合并:公共祖先和两个目标版本
  3. 变基(rebase):将当前版本的修改合并到与目标分支(基底)的共同祖先,然后逐级合并,直到合并目标版本得到最终的版本。比三方合并麻烦,但是它等于完全消除了当前分支的历史,可以得到一条没有分支的主干。
    1. git rebase master :当前分支基于 master 主分支变基
  4. 内容冲突:同一个文件的同一部分进行修改,需要人工确认。git 将有冲突的文件做了标记,编辑内容,删除标记,选择要合并(保留)的内容,保存文件即可。
    1. vscode 编辑合并冲突的文件即可
    2. git mergetool 图形工具(需要配置)
    3. git merge --continuegit commit 提交结果

分支图

以上文字说明,可能比较迷糊。以图片说明对照一下:

  1. c2 到 c4 是快速合并
  2. c2 到 c5 也是快速合并
  3. c4 到 c5 是三方合并或变基合并
    1. 三方是基于 c2,c4,c5 三部分来合并
    2. 变基是将 c4 合并到 c5
      1. 或者将 c3,c5 依次合并到 c4 后

可见变基会将共同祖先之后的每个当前分支版本,应用到另一个分支顶点上,因此需要依次处理合并冲突。因为等于在基底分支上新增了整个当前分支,那么自然就可以去除当前分支了。

而三方合并虽然只需要考虑 c2,c4,c5 的冲突,但是c4 或c5 是怎么来的?这个信息也必须保留起来(否则没有路径)。好处是哪怕分支的路径再复杂,也只需要处理三个点。

# 三方合并
git status
git switch master
git merge test # 产生合并冲突,因为不能快速合并
# a 文件会变成代解决冲突的内容
# 类似:
aaa
bbb
<<<<...
ddd
====...
ccc
>>>>...
# 可以用任何文本编辑器来将冲突部分改成你需要的内容
# 如:
aaa
bbb
ccc
ddd
# 即可
# 产生冲突时,会产生 MERGE_HEAD 指针,所以 git 能判断当前是合并状态
git add a # 表示解决冲突了
git merge --continue # 继续合并,会产生一个新的版本,是两个当前分支的直接后续

git log --oneline --graph # 查看分支图
gitk # 图形界面看得更清晰

三方合并 可以配置 pull 默认采取的策略:

git config pull.rebase false # 三方合并(默认)
git config pull.rebase true # 变基
git config pull.ff only # 只支持快进合并

变基

变基合并是为了解决分支图过于繁琐,删除一些过时的分支。

git switch master
git rebase # 清理多余分支,产生冲突,按上一节处理即可
nano a
git add a
git rebase --continue # 解决冲突后继续
git log --oneline --graph 
git switch test # 将分支定位到主分支上
git reset --hard master # 这样都在一条分支线上了

变基

rebase 还能指定另一个分支和基底进行变基: git rebase --onto 当前分支 修改的分支

注意:变基实际上在改变历史记录,因此在多人协作时会有一点麻烦(要求其他人也应该用 rebase 合并本地仓库的分支)。

reset 命令一般很少用,因为我们的分支应该自然而然的变化(随着提交),reset 区操控它,会让这种历史的变化变得很跳跃。我们要窥探历史,只需要 checkout,它并不改变分支(只改变 HEAD)。但,这里 reset 是有用的,因为变基实际上将 test 从 master 分支树中去除了。我不想维护两个不相干的分支树,所以将 test 重设为当前 master。

.git 文件介绍

.git 目录下:

  1. branches :
  2. config: 配置
  3. description: 描述
  4. FETCH_HEAD:远程头指针
  5. HEAD:头指针
  6. ORIG_HEAD:原始指针
  7. hooks
  8. index:暂存区
  9. info
  10. logs:日志
  11. modules:子模块(子项目)的 .git
  12. objects:本地仓库的对象

工作目录:

  1. .gitignore:应该忽略的文件或目录(git 不管理)
  2. .gitmodules:子模块
  3. .gitattributes:配置文档的存储细节

其他

保存到暂存区

git 分三个点管理版本:目录、暂存区(staged)、.git。其中.git 子目录保存了所有版本,而目录保存当前工作的版本,暂存区(staged)就是中间过度的版本。

当我们要切换版本的时候,经常面临的窘迫是怎么保存现在正在工作,但却还不想提交的工作版本。

git 给出了两个方案:

  1. git stash push -m 注释:把工作目录的修改保存在暂存区栈
    1. git stash pop:应用上一个暂存区的该更
  2. git worktree add ../tmp:创建一个新分支 tmp,并将工作目录设置在../tmp 目录
    1. git worktree remove ../tmp: 删除多余的工作目录

远程仓库

git 主要目的是为了共享一个远程的同步仓库,方便协作或备份。和本地 .git 目录相比,主要是设置和远程仓库的上游关系,包括拉取信息和提交信息。

首先需要设置拉取和提交的配置:

git config push.default=upstream # 表示默认提交到上游
git coinfg pull.ff=only # 拉取合并的模式,只支持快速合并

远程仓库基本配置:

  1. 设置地址:git remote add 仓库名字 ../test.git ,设置地址../test.git,支持多种地址格式。默认的名字:origin
  2. 设置远程的跟踪分支:git remote set-branches origin test2
    1. git remote set-branches --add origin test,添加另一个远程分支
  3. 设置拉取上游的分支:git branch --set-upstream-to=origin/test,将当前分支和上游的 origin/test 分支关联。如果上游分支不存在:
    1. git push -u origin HEAD:test2:提交到 origin 仓库,HEAD 指向当前分支,test2 是希望对应的远程分支,意思就是将 HEAD 指向的分支上传到远端test2 分支。 -u 是同时设置默认上游关系,如果没有默认值,上传的时候就需要指定上游。
  4. 同步信息:git fetch
  5. 拉取内容:git pull, pull 相当于 fetch + merge(合并分支)
  6. 提交内容:git push
  7. 查看远程仓库信息:git ls-remote

git 中的远程仓库,和本地仓库其实分别并不大,因为它的设计是任意一个仓库,都保存所有的信息(在同步的基础上)。因此,有时候远程仓库没有的东西,就用 git push 推送,git push 相当于操作远程仓库,你可以推送一些编辑信息给远程仓库,比如删除,添加分支等。

远程仓库是需要配置的,它并不会默认同步所有信息,你需要添加关联的分支,命令指定同步的内容等等,这对于同步效率是有意义的。

创建仓库时会自动生成默认的主分支 master,在远程同步中会经常被假定使用的名字,如果要改名字或删除,这会和其他工具产生冲突,建议保留。

和远程仓库有关的概念:

  1. 本地仓库:HEAD 头指针,指向各个本地分支
    1. master:本地分支
    2. 特定版本
  2. 远程仓库:FETCH_HEAD 远程头指针
    1. remotes/origin/test:远程跟踪分支
    2. 其他临时下载的东西

从远程下载东西,它默认并不会直接合并到当前分支,而是存放在 FETCH_HEAD,后续才对这个远程头的内容进行合并。

子仓库

实际工作中,经常会出现项目之间相互依赖的关系。这时候不能将两个项目合并在一个仓库,因为有些人不关心其中一部分,如果放在一个仓库,那么两个项目的任意改变,都会影响到所有人。

git 使用一下方案解决这类问题:

  1. git submodule:git 子模块

意思是在远程建立两个独立的仓库,然后在本地设置其中一个仓库的子模块是另一个仓库。

对于本地来说,主仓库和子仓库并不是平等的,子仓库就相当于主仓库的一个模块,一个功能组件。

然后要注意的是主仓库和子模块之间的依赖关系,它被认为是存在以下关系的:

  1. 主仓库依赖子模块的版本
  2. 主仓库不管理子模块的内容,但会把子模块版本变化视为一个应提交内容(条件1 的实现机制)

比如主仓库是版本 1 依赖子仓库版本 2,但是另一个人本地有主仓库版本1,子仓库版本1,这样就是无效的。进而可以发现实际上,是有几种应用情况:

  1. 子模块更新较慢,主仓库只是把它作为静态依赖,不积极更新其版本
  2. 子模块和主仓库同时被修改,迭代更新

情况1 比较常见于依赖外部项目,情况2 主要是内部自己的管理的项目。根据这两个情况,可能有不同的更新策略。主仓库默认是不会主动提交子模块的更新的。

子模块配置:

# 设置 test1 子模块跟踪远程 stable 分支
# -f .gitmodules 在模块配置中设置(可远程共享),而非在本地 .git
git config -f .gitmodules submodule.test1.branch stable
# 显示子模块的更改详情
git config status.submodulesummary 1
# 自动推送子模块更新(可能存在问题)
git config push.recurseSubmodules on-demand

相关功能:

  1. 创建子模块:git submodule add ./test1 test1
    1. 会在主仓库添加子模块配置文件: .gitmodules
    2. 创建子模块目录:./test1
    3. 创建指向实际配置目录的配置文件:./test1/.git
  2. 初始化模块:git submodule init,如果是克隆主仓库,子模块需要单独初始化
    1. git submodule sync:模块路径发生变化时
  3. 更新子模块:git submodule update --remote --merge 或进入子模块目录执行git pull
  4. 推送更新:git push,但配置自动更新子模块时
    1. git submodule foreach 'git push' 在子模块执行更新命令

日志/标签

标签和分支差不多,它也是一个指向特定版本的指针,只是用途不一样,它只是一个标记。

标签种类:

  1. 轻量标签:类似不会变化的分支
  2. 附注标签:带签名等信息的标签对象

git log 日志记录了每一次的版本更新,是查看历史记录的强大工具:

# 单行模式 图形模式 显示最近20条记录
git log --oneline --graph -20

图形工具:

# 需要先安装 tk 组件,这个工具才能正常使用
# 推荐使用
gitk

配置 vscode 作为 difftool

vscode 或者开源版本的 vscodium 可以作为 diff 工具使用。difftool 是将修改前和修改后的文件进行对比,让用户选择那些最终修改的版本,这在 git 中是一个比较关键的编辑环节。

git config --global diff.tool vscodium
git config --global difftool.prompt false
git config --global difftool.vscodium.cmd 'codium --wait --diff "$LOCAL" "$REMOTE" '

# 然后可以使用(如果文档修改了,就会出现对比图)
git difftool 文档

对应的配置内容:

# ~/.gitconfig
[diff]
        tool = vscodium
[difftool "vscodium"]
        cmd = codium  --diff --wait \"$LOCAL\" \"$REMOTE\"
[difftool]
        prompt = false

参考

  1. git 教程:https://github.com/geeeeeeeeek/git-recipes/wiki
  2. git 官方文档:https://git-scm.com/book/zh/v2
  3. git 视频教程:https://www.bilibili.com/video/BV1HM411377j
  4. 如何将 VSCodium 设置为 git difftool:https://stackoverflow.com/questions/68340820/how-to-set-vscodium-as-git-difftool
1
https://gitee.com/coder_lw/wiki.git
git@gitee.com:coder_lw/wiki.git
coder_lw
wiki
wiki
master

搜索帮助