Git 的分支功能可以支持同时进行多个功能的开发和版本管理,多分支用于多人同时开发不同功能,在各自的平行空间写代码,互不干扰,最终合并。标签用于给特定提交打上不可变的版本标记(如 v1.0.0),记录项目的里程碑。子仓库用于在当前项目中嵌入另一个独立的 Git 仓库,实现公共代码的隔离与复用。

分支

查看分支情况

1
2
3
4
5
6
7
8
# 查看当前分支和本地分支
git branch

# 查看远程分支
git branch -r

# 查看所有分支,包括本地和远程
git branch -a

创建本地分支

1
2
3
4
5
6
7
8
# 创建本地分支
git branch production

# 切换分支
git checkout production

# 创建并切换本地分支
git checkout -b production

合并分支

使用如下命令合并分支,如果目前是在 main 分支,首先需要切换到 production 分支,然后再合并 main 分支到 production 分支

1
2
git checkout production
git merge main

除了 merge 命令外,还有一种命令可以合并分支,那就是 rebase,更多内容可以查看:分支的合并

删除分支

1
2
3
4
5
6
7
# 删除本地分支
git branch -d production

# 删除远程分支
git push origin --delete production
# or 下面方式删除。即推送时本地分支为空
git push -u origin :production

注意:删除远程分支不使用 git branch -d,而是使用 git push origin --delete 命令

但是,当远程仓库没有该分支时,或者在本地使用 git branch -r 能够看到远程仓库名,但远程仓库却已经不存在时,使用上面删除远程分支,会出现如下错误:

1
error: unable to delete 'xxx': remote ref does not exist 

这时候需要我们更新本地仓库的远程仓库列表信息,命令如下:

1
2
3
4
5
6
git fetch --prune

# 更新后,会列出远程仓库 origin 已经不存在的分支
// From github.com:xujinzh/JBlog
// - [deleted] (none) -> origin/production
// - [deleted] (none) -> origin/dependabot/npm_and_yarn/acorn-7.1.1

推送分支

1
2
3
4
# production 表示本地分支
# production_remote 表示远程分支
# 如果远程分支不存在,则自动创建一个分支
git push -u origin production:production_remote

远程仓库信息查看

当从远程仓库克隆后,git 默认远程仓库名为 origin,并将远程的 main 分支与本地的 main 分支对应。查看远程仓库名称使用如下命令:

1
2
3
git remote
# or
git remote -v

git remote -v 将显示更多信息,不仅有远程仓库名称,还有 fetch 和 push 的远程仓库地址。当没有 push 权限时,将不显示 push 地址。

多分支管理的一个例子

假设维护两个分支,一个是 main 分支,进行迭代开发,一个是production 分支,当 main 分支测试稳定后合并到 production 分支进行发布部署生产。

已知 main 分支已经进行了一轮迭代开发,测试稳定,需要投入到生产发布。接下来就是要进行 production 生产分支创建与推送。

首次创建并推送 production 分支(仅需做一次)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 确保当前在 main 分支,且代码是最新的
git checkout main
git pull origin main

# 2. 基于当前的 main 分支,在本地创建一个名叫 production 的新分支
git checkout -b production

# 3. 将本地的 production 分支推送到远程仓库
# (-u 参数会在远程也建立同名分支,并建立本地与远程的追踪关系)
git push -u origin production
# 上面命令等同于 git push -u origin production:production
# 其中第一个 production 表示本地 production
# 第二个 production 表示远程仓库的 production

日常迭代:代码稳定后同步到 production

以你一直留在本地 main 分支上开发。当 main 分支的代码再次开发测试稳定、准备上生产环境时,执行下面的四部日常发布命令:

1
2
3
4
5
6
7
8
9
10
11
# 1. 切换到本地 production 分支
git checkout production

# 2. 确保本地 production 分支和远程同步至最新
git pull origin production

# 3. 将 main 分支沉淀的稳定代码,合并(merge)到当前的 production 分支
git merge main

# 4. 将合并后的生产代码推送到远程仓库,生产环境就可以从远程仓库下载最新最稳定的 production 代码
git push origin production

多人协作开发提交的例子

当克隆远程仓库到本地,一般是 main 分支或产品版本分支,该分支是主分支,时刻与远程仓库同步;除此之外,开发人员在本地还会创建 dev 分支、bug 分支和 feature 分支。其中 dev 分支是开发分支,团队开发人员开发分支,重大版本迭代时 merge 到主分支,一般也需要时刻与远程 dev 分支同步;bug 分支是本地修复 bug 的分支,一般不进行远程同步;feature 分支同步取决于团队需要。该小节可参考:多人协作

  1. leader 或首位开发者在本地 main 分支外使用如下命令创建了 dev分支,并提交到远程 origin 仓库

    1
    2
    3
    4
    5
    # 在本地创建 dev 分支,并切换到 dev 分支,进行开发
    git checkout -b dev

    # 推送本地 dev 分支到远程仓库 origin,并同时在远程仓库origin 创建 dev 分支
    git push origin dev
  2. 第二位开发人员首先克隆远程开发仓库到本地,然后在本地增加远程 origin/dev 分支到本地 dev 分支

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    # 克隆远程 origin 仓库到本地机器
    git clone jinzhongxu@1.14.1.14:/home/jinzhongxu/monkey.git

    # 查看分支情况,特别是远程,发现远程仓库 origin 除了有 main 分支,还有分支 origin/dev
    git branch -a
    //* main
    // remotes/origin/HEAD -> origin/main
    // remotes/origin/dev
    // remotes/origin/main

    # 将远程仓库 origin 的 dev 分支增加到本地,并切换到本地 dev 分支
    git checkout -b dev origin/dev

    # 查看本地分支信息
    git branch
    //* dev
    // main

    # 该开发人员进行了本地开发,并在 dev 分支增加部分代码,然后提交到远程仓库 origin 的 dev 分支
    git status
    git add .
    git commit -m "repair bug"
    git status
    git push origin dev:dev
  3. 第三位开发人员也同样和第二位开发人员一样,下载了远程仓库 origin 的 main 分支和 dev 分支,并同时进行开发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    # 克隆远程 origin 仓库到本地机器
    git clone jinzhongxu@1.14.1.14:/home/jinzhongxu/monkey.git

    # 查看分支情况,特别是远程,发现远程仓库 origin 除了有 main 分支,还有分支 origin/dev
    git branch -a
    //* main
    // remotes/origin/HEAD -> origin/main
    // remotes/origin/dev
    // remotes/origin/main

    # 将远程仓库 origin 的 dev 分支增加到本地,并切换到本地 dev分支
    git checkout -b dev origin/dev

    # 查看本地分支信息
    git branch
    //* dev
    // main

    # 该开发人员进行了本地开发,并在 dev 分支增加部分代码,然后提交到远程仓库 origin 的 dev 分支
    git status
    git add .
    git commit -m "repair bug"
    git status

    # 但是,使用如下命令在提交时出现问题
    git push origin dev:dev
    // To 1.14.1.14:/home/jinzhongxu/monkey.git
    // ! [rejected] dev -> dev (fetch first)
    // error: failed to push some refs to 'jinzhongxu@1.14.1.14:/home/jinzhongxu/monkey.git'
    // hint: Updates were rejected because the remote contains work that you do
    // hint: not have locally. This is usually caused by another repository pushing
    // hint: to the same ref. You may want to first integrate the remote changes
    // hint: (e.g., 'git pull ...') before pushing again.
    // hint: See the 'Note about fast-forwards' in 'git push --help' for details.

    # 上面的错误提示很明白,那就是团队中已经有人提交的最新更新到远程 dev 分支,需要先 git pull,然后再 git push,使用如下命令解决
    git pull # 如果失败,可能的原因是没有指定本地dev分支与远程origin/dev分支的链接,可使用右边命令设置dev和origin/dev的链接:git branch --set-upstream-to=origin/dev dev
    # 成功后,出现如下提示,就是需要手动修改冲突,然后,再 git push
    // remote: Counting objects: 5, done.
    // remote: Compressing objects: 100% (3/3), done.
    // remote: Total 3 (delta 1), reused 0 (delta 0)
    // Unpacking objects: 100% (3/3), done.
    // From 1.14.1.14:/home/jinzhongxu/monkey
    // 4461cc8..7056c16 dev -> origin/dev
    // Auto-merging main.py
    // CONFLICT (content): Merge conflict in main.py
    // Automatic merge failed; fix conflicts and then commit the result.

    git status
    git add main.py
    git commit -m "repair bugs for xlabel and ylabel"
    git push origin dev
  4. 前面三个过程,特别是 2 和 3,是公司开发时常遇到,并且经常这样的迭代进行。特别是 3,我们要经常使用,要熟练掌握。

推送时忽略某些文件或文件夹

在仓库主目录下,编写

1
vim .gitignore 

添加忽略的(或包含的)文件、文件夹。这里以 hexo 部署博客为例:

1
2
3
4
5
6
7
.DS_Store
Thumbs.db
db.json
*.log
node_modules/
public/
.deploy*/

如果出现 !db.json 即表示提交时包含 db.json.

标签

添加标签

首先运行如下命令,查看commit id,

1
git log 

运行如下命令添加标签

1
git tag v1.0.0 c584a15bd2

其中,v1.0.0 为标签,版本号,c584a15bd2 为 commit id,只要唯一即可。

查看本地标签

1
git tag -n

推送本地标签

1
2
3
4
5
# 推送单个指定标签
git push origin v1.0.0

# 推送本地所有标签
git push origin --tags

删除本地标签

1
git tag -d <tagname>

删除远程仓库标签

1
2
3
4
5
# 1. 先删除本地标签
git tag -d v1.0.0

# 2. 再删除远程标签
git push origin --delete v1.0.0

自建 git 托管服务器

这里以 Ubuntu 为例。

首先,需要有一个 vps 服务器,假设 IP 地址是 1.14.1.14

其次,在服务器上增加用户和组,比如添加 jinzhongxu 用户

1
adduser jinzhongxu

然后,安装 git 命令

1
sudo apt update && sudo apt install git

最后,建立远程仓库

1
git init --bare monkey.git

把远程仓库克隆到本地

1
git clone jinzhongxu@1.14.1.14:/home/jinzhongxu/monkey.git

更改远程仓库的 URL

当远程仓库 IP 地址更改、SSH 和 HTTPS 切换等其他导致远程仓库的 URL 地址发生改变时,需要切换地址,才能够正确提交本地修改的代码等内容上传到远程仓库。

更改远程仓库的 URL 需要使用命令:git remote set-url

git remote set-url 命令使用两个参数:

  • 现有远程仓库的名称。 例如 origin。一般有源仓库上游仓库是两种常见选择。

  • 远程仓库的新 URL。 例如:

    • 如果您要更新为使用 HTTPS,您的 URL 可能如下所示:

      1
      https://github.com/USERNAME/REPOSITORY.git
    • 如果您要更新为使用 SSH,您的 URL 可能如下所示:

      1
      git@github.com:USERNAME/REPOSITORY.git

将远程 URL 从 SSH 切换到 HTTPS

  1. 打开 Terminal(终端)。

  2. 将当前工作目录更改为您的本地仓库。

  3. 列出现有远程仓库以获取要更改的远程仓库的名称。

    1
    2
    3
    $ git remote -v
    > origin git@github.com:USERNAME/REPOSITORY.git (fetch)
    > origin git@github.com:USERNAME/REPOSITORY.git (push)
  4. 使用git remote set-url

    命令将远程的 URL 从 SSH 更改为 HTTPS。

    1
    $ git remote set-url origin https://github.com/USERNAME/REPOSITORY.git
  5. 验证远程 URL 是否已更改。

    1
    2
    3
    4
    $ git remote -v
    # Verify new remote URL
    > origin https://github.com/USERNAME/REPOSITORY.git (fetch)
    > origin https://github.com/USERNAME/REPOSITORY.git (push)

下次对远程仓库执行 git fetchgit pullgit push 操作时,您需要提供 GitHub 用户名和密码。 When Git prompts you for your password, enter your personal access token (PAT) instead. Password-based authentication for Git is deprecated, and using a PAT is more secure. For more information, see “Creating a personal access token.”

You can use a credential helper so Git will remember your GitHub username and personal access token every time it talks to GitHub.

将远程 URL 从 HTTPS 切换到 SSH

  1. 打开 Terminal(终端)。

  2. 将当前工作目录更改为您的本地仓库。

  3. 列出现有远程仓库以获取要更改的远程仓库的名称。

    1
    2
    3
    $ git remote -v
    > origin https://github.com/USERNAME/REPOSITORY.git (fetch)
    > origin https://github.com/USERNAME/REPOSITORY.git (push)
  4. 使用git remote set-url

    命令将远程的 URL 从 HTTPS 更改为 SSH。

    1
    $ git remote set-url origin git@github.com:USERNAME/REPOSITORY.git
  5. 验证远程 URL 是否已更改。

    1
    2
    3
    4
    $ git remote -v
    # Verify new remote URL
    > origin git@github.com:USERNAME/REPOSITORY.git (fetch)
    > origin git@github.com:USERNAME/REPOSITORY.git (push)

更改远程仓库地址的方法还有另一种方法,那就是先删除已有远程仓库地址,然后再添加新的远程仓库地址:

1
2
3
4
5
# 1. 删除旧的 origin 关联
git remote remove origin

# 2. 添加新的 origin 关联
git remote add origin <新的仓库URL地址>

为仓库添加子模块(子仓库)

当项目开发中遇到需要引用其他子仓库作为一个包时,可以将其作为子模块添加到现有项目中。

添加子模块

在该项目中添加子模块依赖,如 vendor、common 包。这里也 vendor 为例。

1
git submodule add  https://gitlab.mathscv.com/jinzhongxu/sub-repo.git vendor

此时,主项目根目录下会自动生成一个 .gitmodules 文件,用来记录子模块的路径与仓库映射关系。其实,在当前项目的 .git/config 中也会有子模块信息。

提交并推送到主项目远程仓库:

1
2
3
git add .gitmodules vendor
git commit -m "feat: 以子模块形式引入 vendor"
git push origin main

子模块的修改单独进行推送。

克隆主项目

当添加子模块后,主项目的推送和克隆中子模块都是一个地址引用。对于新克隆,如果相应同时克隆子模块代码,那么需要执行下面命令:

1
2
3
4
git clone https://gitlab.mathscv.com/jinzhongxu/main-repo.git
cd main-repo

git submodule update --init --recursive

更新子模块地址

当子模块的地址更改(导入内网 gogs 托管平台,或者使用其他更强大的同名子模块)后,可以采用如下的方法更新子模块地址。

修改配置文件

打开主项目根目录下的 .gitmodules 文件,找到对应子模块的 url 属性,将其修改为全新的远程仓库地址。

1
2
3
[submodule "vendor"]
path = vendor
url = <新的子项目远程仓库地址>

同步配置到 Git 内部

运行以下命令,将 .gitmodules 中的新地址同步到 Git 的本地全局配置(.git/config)中:

1
git submodule sync

更新子模块的缓存(可选但推荐)

如果子模块已经被克隆到了本地,为了确保其内部的 .git 目录也指向新地址,请进入子模块目录并更新它的远程源:

1
git submodule update --init --recursive --remote

提交变更

将配置文件的修改提交并推送到主项目的远程仓库,以便团队其他成员可以同步:

1
2
3
git add .gitmodules
git commit -m "chore: 更新子模块 vendor 的远程仓库地址"
git push origin main

团队协同注意事项

当团队其他成员拉取(git pull)主项目的最新代码后,他们本地的子模块地址不会自动生效。他们需要在主项目根目录执行以下两行命令来激活新地址:

1
2
git submodule sync
git submodule update --init --recursive

参考链接

更改远程仓库的 URL