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 中的含义是样的,都是修订结构图表中的外部引用(指针)。
上图中,包含两个分支,当前的分支 Master 和另一分支 maint 。其中
34ac2
分支有一个v0.9
的标签,另一个3fb00
标签是合并提交的。
标签(tag),即
给定版本的符号名称,如 v1.3-rc3
,其永远指向相同对象,且不会变更。
这就是一个允许开发人员快速查询和浏览的信息,且该信息必须对所有人来说都是具有相同意义的。
分支(branch),即 一系列开发工作的符号名称。
本地分支的引用信息都存放在 .git/refs/heads/
路径下,如
master
分支信息存放于 .git/refs/heads/master
中,而 refs/heads/
为该 master
的命名空间。
目前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
文件中。
Branch Name
分支 | 命名 | 说明 |
---|---|---|
主分支 | master | 主分支,所有提供给用户使用的正式版本 |
开发分支 | dev | 开发分支,永远是功能最新最全的版本 |
功能分支 | feature-* | 新功能分支,某个功能点正在开发的分支 |
发布分支 | release-* | 发布定期要上线的功能 |
修复分支 | bug-* | 修复代码bug的分支 |
Master
代码库应该有且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。Git主分支的名字,默认称为
Master
。Master
主分支是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。
主分支只用来分布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做 Dev 这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在 Master 分支上,对 Dev 分支进行”合并”(merge)。
HEAD
git 中的分支 本质上是个指向 commit 对象的可变指针。
而在每一个使用 git 的 .git/
路径下,还保存着一个名为
HEAD
的特别指针,一般来说,它是指向正在工作中的本地分支的指针。
HEAD
的指向是可以改变的,比如在 commit / checkout /
branch / tag / retest / restore 等操作之后。
有的时候 HEAD
会指向一个没有分支名字的修订版本,这种情况叫 detached
HEAD。
在最底层实现中,Git 历史版本识别是通过一个 SHA-1 哈希码实现的,例如
2b953b4380
。
Git 支持多种形式的版本查询,其中就包括使用哈希码的精确匹配(最少提供4个字符)。
GIT COMMAND GRAMMER
- 使用
git <command> -h
可以获得该命令的所有参数用法帮助(简略的)。然后可以针对性地去搜索该指令和参数的用法。 - 下方会列出一些较为常见的(自己会使用得到的)命令及其参数。个别较常见的命令会贴上一张官方的参数用法图片。
git add
<file name>
使用本命令参数可以直接确认某个文件的变动记录。
如
git add readme.md
确认了readme.md
文件的修改,git add .
确认了当前路径下包含的所有子路径和子文件的修改。
git blame
<file name>
本参数可以查看文件内部每一行内容的变动,例如新增一行,文件的编辑者等。
跟
git diff
不同的是,本命令是真的打开文件然后一行一行地给你修改和提示信息,包括每一行是谁写的
git branch
1 | git branch -D origin/xxxxxx |
-a
使用本命令标志可以显示 本地 (local) 所有分支 和
远程 (remote) 所有分支。 其中,带 *
的为当前所在分支。
-d <branch name>
使用本命令标志可以删除 某个分支。
-v
使用命令标志可以展示分支的哈希函数和commit信息,-v
即
--verbose
,表示 "冗长的"。
例如: 下方,从
master
主分支刚创建出新分支SWV
时,两者的哈希函数和commit信息是一样的。但修改了
SWV
分支的内容并确认提交之后,其哈希函数和commit信息就发生了改变。
git checkout
<branch>
单独使用此参数可以用于切换分支(也有新的实验性语句
switch
可以使用,但是 checkout
更稳定)或恢复工作树文件。使用方式如:
1 | git checkout dev |
-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
操作到底修改什么信息。
git log
注意:所有的 git log
命令只会显示如下格式的 log 记录。
-<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
1 | git reset --hard HEAD^ |
git remote
本命令可以进行如下操作:
- 对远程repository进行 添加(add)、删除(remove)、重命名(rename)等操作。
git status
直接使用本命令可以查看当前路径下所有文件的修改记录。
GIT SITUATION
远程git clone一个master分支为空的仓库时
要么整个仓库都为空,要么肯定还有分支,使用 git branch -a
查看远程仓库分支,一般如下:
1 | * dev |
而正好 xxxxxxxxxx
可能就是真正存放现有文件的分支,使用
git checkout origin/xxxxxxxxxx
进行分支切换。
conflict
当文件冲突时,Git系统无法自动合并它们,因为可能这些代码块不是独立的。
git pull
也 pull 不下来。
REPO
repo 部分将分为 安装repo 、命令语法 和 使用场景 三部分。
INSTALL REPO
repo
的安装有 自动 和 手动 两种,类 UNIX
系统都可以通过对应的包管理工具进行安装,但如果没有手动修改源,使用效果可能也不尽如人意。
安装完毕之后还需要进行 版本验证,可能还需要 对python版本进行更改 。
SETUP AUTOMATICALLY
根据 google android installing repo 指示,可以从 apt 包管理库中下载和安装 repo,也可以使用 google源、清华源或其他源进行手动下载。
Debian 系 Linux 的安装:
1 | sudo apt-get update |
Mac 安装:
1 | sudo port install repo |
或
1 | sudo brew install repo |
SETUP MANUALLY
手动安装 repo
的大概思路如下:
- 在用户根目录
~
下新建bin
文件夹,即把$HOME/bin
或~/bin
当做repo
的执行文件路径。 - 将该路径加入系统变量
PATH
中,可以通过写入~/.profile
或对应的 shell 配置文件(如zsh的~/.zshrc
)中并更新该配置文件来实现。 - 然后在该路径下载 repo的执行程序,主要是将某个 url
中(如下方所示为清华源的repo)的信息写入
~/bin/repo
中。 - 为所有用户
a
附加~/bin/repo
的可执行x
权限。 - 把
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'
加入到配置文件中,使其一直生效(也可以在 terminal 中临时输入此命令,使其在设备重启前生效)。如果未写入 shell 的配置文件,则重启后该变量就会消失。
下方代码以安装了 zsh
shell 的 linux 或 debian
系系统为例,可以拷贝粘贴保存至脚本以运行。
1 | # 手动安装repo |
注意:google android用的是
https://storage.googleapis.com/git-repo-downloads/repo
的链接,但不是很方便下载,所以就更换成了清华源的。
VERIFICATION
安装完毕之后,可以对手动安装的 repo
进行版本验证,输入
repo version
和 echo $REPO_URL
即可,结果如下:
此处提示的 <repo not installed>
是正常的,因为在
~
目录下的确没有安装 repo
.
PYTHON NOT FOUND
如果出现以下提示,则说明你的系统中没有为 python
添加
软连接。
1 | /usr/bin/env: ‘python’: No such file or directory |
解决方法
- 验证是否已经安装了
python2
:输入命令python2 --version
,如果提示对应的版本信息则跳到 第3步,否则进行 第2步。 - 下载
python2
:debian 系用户输入命令sudo apt install python2
进行安装。mac系用户输入sudo port install python2
进行安装。 - 在
/usr/bin/
目录下为python2
创建软链:sudo ln -s /usr/bin/python2 /usr/bin/python
- 再次运行
repo init
即可正常。
如,在 Kali Linux 绑定 python2
为 python
之后,可以输入 whereis python
或
python --version
进行验证:
REPO COMMAND GRAMMER
- 参考 Repo 命令参考资料
- 使用
repo <command> -h
可以获取相应命令的帮助。 - 在初始化过 repo 的路径下使用
repo help
可以获得以下信息:
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 | repo sync |
但,bitbucket的网络资源极差,很多情况下都拉不下来代码。
repo help
在其他地方如果没有运行过 repo init
而直接输入
repo help
,则会出现以下信息,详见 REPO INIT 。
REPO SITUATION
REPO SWITCH BRANCH
前文有说过 “借助单个 Repo
命令,可以将文件从多个代码库下载到本地工作目录”,即使用repo拉下来的是多个
git repository,且这个repo是通过 .xml
文件进行管理的,也就是它自身包含了一些版本和分支切换相关的信息。
如果要对repo拉下来的东西进行分支切换,就要参考一下步骤:
- 到
.repo/manifest
路径中。 - 使用
git branch -a
查看分支信息。 - 使用
git checkout <branch name>
进行分支切换。 - 再次使用
repo sync
命令进行同步。
下图是通过 repo
拉下来的多个git repository
之一中的隐藏文件,可以看到这个 .git
文件被使用指向上级
.repo/
路径下的某个 .git
文件,也就是说,需要先通过 repo
去拉取信息,再使用
git
来切换分支。
REFERENCE
- Git与Repo简单入门
- git rebase 命令
- git reference
- 《Git高手之路》,Jakub Narebski,人民邮电出版社