repo and git

本博文并不提供全面的教学指导,详细手册请查阅 Git-Reference开发

Git 是一个开放源代码的版本控制系统,专用于处理分布在多个代码库上的大型项目。

Repo 是 google android 以 Git 为基础构建的代码库管理工具,依赖于 python2 脚本调用 Git,主要用来下载、管理android项目的软件仓库。

Repo 简化了跨多个代码库运行的流程,与 Git 相辅相成。Repo 可以在必要时 整合多个 Git 代码库,将相关内容上传到我们的修订版本控制系统,借助单个 Repo 命令,可以将文件从多个代码库下载到本地工作目录。使用 Repo 执行基本的跨网络操作可大大简化文件管理工作。

GIT

对git的使用会分为 基础概念部分(即相关概念介绍)、指令语法使用场景 三部分。

INTRODUCTION

DAG: Directed Acyclic Graph,有向无环图。

DVCS: Distributed Version Control System,分布式版本控制系统。

DVCS

节点:在DVCS中,每个节点代表项目的一个修订(一个版本),这些对象通常被称为提交。

有向边: 在DVCS中,每条边是基于两个修订之间的关系生成的。箭头是从父修订指向子修订的,代表他们之间的从属关系。 修订DAG中的箭头并不一定会形成闭环。通常修订的DAG是从左向右的结构(根节点在左,叶节点在右)或者自上而下结构(最新的版本在上面)。

BRANCH / TAG / REFS / HASH-CODE

分支(branch)和 标签 (tag)有时候也统称为 引用(refs),它们在修订 DAG 中的含义是样的,都是修订结构图表中的外部引用(指针)。

image-20220226205719300

上图中,包含两个分支,当前的分支 Master 和另一分支 maint 。其中 34ac2 分支有一个 v0.9 的标签,另一个 3fb00 标签是合并提交的。

标签(tag),即 给定版本的符号名称,如 v1.3-rc3其永远指向相同对象,且不会变更

这就是一个允许开发人员快速查询和浏览的信息,且该信息必须对所有人来说都是具有相同意义的。

分支(branch),即 一系列开发工作的符号名称

本地分支的引用信息都存放在 .git/refs/heads/ 路径下,如 master 分支信息存放于 .git/refs/heads/master 中,而 refs/heads/ 为该 master 的命名空间。

image-20220226211442066

目前Git在硬盘中采用了两种截然不同的方式来表示分支:松散格式压缩格式

例如master分支(该分支是Git采用的默认分支名,用户在创建新的版本库时默认的分支名就是它)。

  • 采用 松散格式 时,它是 .git/refs/heads/master 中的一行文本,其中指代分支的 内容是十六进制的SHA-1码
  • 采用 压缩格式 时,它是 .git/packed-refs 中的一行文本,使用 最顶部修订的SHA-1码分支全名一起 表示该分支。

Configuration

可以通过 git config --global user.email ""git config --global user.name "" 进行配置,相关信息存储在 ~/.gitconfig 文件中。

image-20220226203128964

Branch Name

分支 命名 说明
主分支 master 主分支,所有提供给用户使用的正式版本
开发分支 dev 开发分支,永远是功能最新最全的版本
功能分支 feature-* 新功能分支,某个功能点正在开发的分支
发布分支 release-* 发布定期要上线的功能
修复分支 bug-* 修复代码bug的分支

Master

代码库应该有且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。Git主分支的名字,默认称为 MasterMaster 主分支是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。

主分支只用来分布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做 Dev 这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在 Master 分支上,对 Dev 分支进行”合并”(merge)。

git 中的分支 本质上是个指向 commit 对象的可变指针。

而在每一个使用 git 的 .git/ 路径下,还保存着一个名为 HEAD 的特别指针,一般来说,它是指向正在工作中的本地分支的指针。

HEAD 的指向是可以改变的,比如在 commit / checkout / branch / tag / retest / restore 等操作之后。

有的时候 HEAD 会指向一个没有分支名字的修订版本,这种情况叫 detached HEAD

image-20220226203838866

在最底层实现中,Git 历史版本识别是通过一个 SHA-1 哈希码实现的,例如 2b953b4380

Git 支持多种形式的版本查询,其中就包括使用哈希码的精确匹配(最少提供4个字符)。

GIT COMMAND GRAMMER

  1. 使用 git <command> -h 可以获得该命令的所有参数用法帮助(简略的)。然后可以针对性地去搜索该指令和参数的用法。
  2. 下方会列出一些较为常见的(自己会使用得到的)命令及其参数。个别较常见的命令会贴上一张官方的参数用法图片。

git add

<file name>

使用本命令参数可以直接确认某个文件的变动记录。

git add readme.md 确认了 readme.md 文件的修改,git add . 确认了当前路径下包含的所有子路径和子文件的修改。

git blame

<file name>

本参数可以查看文件内部每一行内容的变动,例如新增一行,文件的编辑者等。

image-20220226202721363

git diff 不同的是,本命令是真的打开文件然后一行一行地给你修改和提示信息,包括每一行是谁写的

git branch

image-20220226213748945
1
2
git branch -D origin/xxxxxx
git branch -D xxxxxx

-a

使用本命令标志可以显示 本地 (local) 所有分支远程 (remote) 所有分支。 其中,带 * 的为当前所在分支。

image-20220226200020105

-d <branch name>

使用本命令标志可以删除 某个分支。

image-20220226211322680

-v

使用命令标志可以展示分支的哈希函数和commit信息,-v--verbose ,表示 "冗长的"。

例如: 下方,从 master 主分支刚创建出新分支 SWV 时,两者的哈希函数和commit信息是一样的。

image-20220226214736094

但修改了 SWV 分支的内容并确认提交之后,其哈希函数和commit信息就发生了改变。

image-20220226214912676

git checkout

image-20220226213843563

<branch>

单独使用此参数可以用于切换分支(也有新的实验性语句 switch 可以使用,但是 checkout 更稳定)或恢复工作树文件。使用方式如:

1
2
3
git checkout dev
git checkout origin/dev
git checkout xxxxxx

-b

使用本标志 + <new branch name> 可以创建一个新分支并切换至该分支,等同于 先使用 git branch <new branch name>git checkout <new branch name>

--orphan <branch name>

使用本命令标志可以创建一个与主分支没有联系的孤儿分支。

git commit

-a/-all

使用本标志指 接受被追踪文件的所有变更,可以在暂存区创建一个注释来实现操作隔离。

-m ""

本标志和参数可以支持在 "" 内实现对合并的快速注释。

git clone

使用本命令可以直接从某个库中拷贝某个路径下的 repository,如:

1
git clone git@git.islet.space:srv/led_blink.git

当然,拷贝不公开的repository时,需要确保已经将自己的 SSH-key 加入到该网站的 /home/git/.ssh/authorized_keys 文件中。公开的 repository 可以随便拷贝。

git diff

本命令可以简要地查看距离上次 commit 操作到底修改什么信息。

image-20220226202625977

git log

注意:所有的 git log 命令只会显示如下格式的 log 记录。

image-20220226202114879

-<num>

如输入 git log -2,可以查看当前项目最近两条的提交记录。

--author 或 --committer

本标志支持查看提交者信息,使用方法如:

1
git log --author=Linus

--merges 或 --no-merges

本标志支持查看已合并和提交或非合并提交的信息。

<path / file name>

本参数可以

git merge

默认情况下,Git执行 快进式合并(fast-forward merge),会直接将 Master 分支指向 Dev 分支。 使用 –no–ff 参数后,会执行正常合并,在 Master 分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法。即:

1
git merge -no-ff dev

git pull

执行本命令后,Git系统会将服务器版本库中的变更下载到本机。自动将 remote 版本和 local 版本进行合并,然后把合并后的变更提交到本机 版本库中。

git push

--set-upstream

设置本地(新)分支的远程上游分支,一些在网上可以查找到的信息如下:

  • set upstream branches to work properly
  • closely associated with remote branches
  • define the branch tracked on the remote branches (also called as remote tracking branch)
  • --set-upstream is the same as -u

在本地创建的新分支如果希望在远程版本库中添加对应的上游分支,使用此命令和标志可以使得此分支推送至远程分支的目标更加明确。

1
git push --set-upstream origin better-random

git rebase

rebase是将自己的分支重新基于别人的分支之上,即,在别人的基础上再添加什么东西。

git reset

image-20220226223239807
1
git reset --hard HEAD^

git remote

本命令可以进行如下操作:

  1. 对远程repository进行 添加(add)、删除(remove)、重命名(rename)等操作。
image-20220226223823738

git status

直接使用本命令可以查看当前路径下所有文件的修改记录。

image-20220226223119337

GIT SITUATION

远程git clone一个master分支为空的仓库时

要么整个仓库都为空,要么肯定还有分支,使用 git branch -a 查看远程仓库分支,一般如下:

1
2
3
4
5
* dev
remotes/origin/xxxxxxxxxx
remotes/origin/HEAD -> origin/dev
remotes/origin/dev
remotes/origin/master

而正好 xxxxxxxxxx 可能就是真正存放现有文件的分支,使用 git checkout origin/xxxxxxxxxx 进行分支切换。

conflict

当文件冲突时,Git系统无法自动合并它们,因为可能这些代码块不是独立的。

image-20220225214016189
image-20220225211850394

git pull 也 pull 不下来。

image-20220225212102034

REPO

repo 部分将分为 安装repo命令语法使用场景 三部分。

INSTALL REPO

repo 的安装有 自动手动 两种,类 UNIX 系统都可以通过对应的包管理工具进行安装,但如果没有手动修改源,使用效果可能也不尽如人意。

安装完毕之后还需要进行 版本验证,可能还需要 对python版本进行更改

SETUP AUTOMATICALLY

根据 google android installing repo 指示,可以从 apt 包管理库中下载和安装 repo,也可以使用 google源、清华源或其他源进行手动下载。

Debian 系 Linux 的安装:

1
2
sudo apt-get update
sudo apt-get install repo

Mac 安装:

1
sudo port install repo

1
sudo brew install repo

SETUP MANUALLY

手动安装 repo 的大概思路如下:

  1. 在用户根目录 ~ 下新建 bin 文件夹,即把 $HOME/bin~/bin 当做 repo 的执行文件路径。
  2. 将该路径加入系统变量 PATH 中,可以通过写入 ~/.profile 或对应的 shell 配置文件(如zsh的 ~/.zshrc )中并更新该配置文件来实现。
  3. 然后在该路径下载 repo的执行程序,主要是将某个 url 中(如下方所示为清华源的repo)的信息写入 ~/bin/repo 中。
  4. 为所有用户 a 附加 ~/bin/repo 的可执行 x 权限。
  5. export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo' 加入到配置文件中,使其一直生效(也可以在 terminal 中临时输入此命令,使其在设备重启前生效)。如果未写入 shell 的配置文件,则重启后该变量就会消失。

下方代码以安装了 zsh shell 的 linux 或 debian 系系统为例,可以拷贝粘贴保存至脚本以运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 手动安装repo
mkdir ~/bin
PATH=~/bin:$PATH
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo > ~/bin/repo
chmod a+x ~/bin/repo

# 将源添加到shell配置文件并使其生效
echo "export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'" >> ~/.zshrc
source ~/.zshrc

# 验证信息
repo version
echo $REPO

注意:google android用的是 https://storage.googleapis.com/git-repo-downloads/repo 的链接,但不是很方便下载,所以就更换成了清华源的。

VERIFICATION

安装完毕之后,可以对手动安装的 repo 进行版本验证,输入 repo versionecho $REPO_URL 即可,结果如下:

此处提示的 <repo not installed> 是正常的,因为在 ~ 目录下的确没有安装 repo .

image-20220226193048479

PYTHON NOT FOUND

如果出现以下提示,则说明你的系统中没有为 python 添加 软连接

1
/usr/bin/env: ‘python’: No such file or directory

解决方法

  1. 验证是否已经安装了 python2 :输入命令 python2 --version,如果提示对应的版本信息则跳到 第3步,否则进行 第2步。
  2. 下载 python2:debian 系用户输入命令 sudo apt install python2 进行安装。mac系用户输入 sudo port install python2 进行安装。
  3. /usr/bin/ 目录下为 python2 创建软链:sudo ln -s /usr/bin/python2 /usr/bin/python
  4. 再次运行 repo init 即可正常。

如,在 Kali Linux 绑定 python2python 之后,可以输入 whereis pythonpython --version 进行验证:

image-20220125143434000

REPO COMMAND GRAMMER

  1. 参考 Repo 命令参考资料
  2. 使用 repo <command> -h 可以获取相应命令的帮助。
  3. 在初始化过 repo 的路径下使用 repo help 可以获得以下信息:
image-20220226220906805

repo init

执行下方的命令会在当前目录中创建一个 .repo/ 目录并初始化 Repo 的版本控制,其中包含存放 Repo 源代码,以及从 URL(对应下方 -u 参数) 中同步下来的 Git 代码库 的某个分支 (对应下方 -b 参数)。

1
repo init -u <git@xxxx.com:xxxx/manifest> -b <oneOfTheBranch>
  • -u:即URL,指定从中检索清单代码库的网址。
  • -m:选择代码库中的清单文件。如果未选择清单名称,则默认为 default.xml
  • -b:指定修订版本,即特定的 manifest-branch,可以是 dev 分支,也可以是特定分支,拉下来之后参照 REPO SWITCH BRANCH 进行分支切换。

注意:对于所有剩余的 Repo 命令,当前的工作目录必须是 .repo/ 的父目录或该父目录的子目录。

repo branch

使用本命令可以查看所有的 目前可用的主题分支(currently available topic branches)。

repo sync

manifest 源中往外拉取分支。下方的 -j<number> 是指创建多线程进行数据资源获取,如 -j4 是指创建四线程进行数据资源获取。

1
2
3
repo sync
repo sync -j4
repo sync -j8

但,bitbucket的网络资源极差,很多情况下都拉不下来代码。

repo help

在其他地方如果没有运行过 repo init 而直接输入 repo help ,则会出现以下信息,详见 REPO INIT

image-20220226220753429

REPO SITUATION

REPO SWITCH BRANCH

前文有说过 “借助单个 Repo 命令,可以将文件从多个代码库下载到本地工作目录”,即使用repo拉下来的是多个 git repository,且这个repo是通过 .xml 文件进行管理的,也就是它自身包含了一些版本和分支切换相关的信息。

如果要对repo拉下来的东西进行分支切换,就要参考一下步骤:

  1. .repo/manifest 路径中。
  2. 使用 git branch -a 查看分支信息。
  3. 使用 git checkout <branch name> 进行分支切换。
  4. 再次使用 repo sync 命令进行同步。
image-20220225211308567

下图是通过 repo 拉下来的多个git repository 之一中的隐藏文件,可以看到这个 .git 文件被使用指向上级 .repo/ 路径下的某个 .git 文件,也就是说,需要先通过 repo 去拉取信息,再使用 git 来切换分支。

image-20220225214801501

REFERENCE

  1. Git与Repo简单入门
  2. git rebase 命令
  3. git reference
  4. 《Git高手之路》,Jakub Narebski,人民邮电出版社