静态交叉编译教程
前言
静态交叉编译教程,本文以SS为例。
配置编译环境
安装需要软件
1 | apt-get install -y build-essential autoconf autopoint automake git pkg-config |
交叉编译工具
下载编译工具
以群晖为例,所有的交叉编译工具下载地址:
https://sourceforge.net/projects/dsgpl/files/
选择对应机型下载。
1 | cd /mnt |
自制编译工具
未完成。。。。。。
调整编译环境
自定义参数:MLIB
编译的目标处理器架构;TOOLCHAINDIR
交叉编译工具所在目录;TOOLCHAIN
交叉编译工具bin
目录所在目录;HOST
交叉编译工具名称。
1 | MLIB=x86_64 |
完成后运行$HOST-gcc -v
;返回版本信息说明交叉编译工具配置正常。
创建编译目录
1 | rm -rf /mnt/build/shadowsocks-$MLIB |
编译依赖库文件
- mbedTLS
- pcre
- libsodium
- libev
- libudns
- c-ares
注意:软件所需要的依赖库,请查看软件的文档得知。
mbedTLS
1 | mkdir $SRC/mbedTLS && cd $SRC/mbedTLS |
pcre
1 | mkdir $SRC/pcre && cd $SRC/pcre |
libsodium
1 | mkdir $SRC/libsodium && cd $SRC/libsodium |
libev
1 | mkdir $SRC/libev && cd $SRC/libev |
libudns
1 | mkdir $SRC/libudns && cd $SRC/libudns |
c-ares
1 | mkdir $SRC/c-ares && cd $SRC/c-ares |
编译软件
- shadowsocks-libev
- simple-obfs
shadowsocks-libev
1 | mkdir $SRC/shadowsocks-libev && cd $SRC/shadowsocks-libev |
simple-obfs
1 | mkdir $SRC/simple-obfs && cd $SRC/simple-obfs |
去除符号表
1 | cp $PREFIX/bin/ss-* $BASE |
还原环境变量
1 | PATH=$PATH_A |
Gogs使用教程
什么是 Gogs
Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自助 Git 服务。使用 Go 语言开发使得 Gogs 能够通过独立的二进制分发,并且支持 Go 语言支持的 所有平台,包括 Linux、Mac OS X、Windows 以及 ARM 平台。
新建用户
Gogs 默认以 git 用户运行(你应该也不会想一个能修改 ssh 配置的程序以 root 用户运行吧?)。
运行 sudo adduser git
新建好 git 用户。
su git 以 git 用户登录,到 git 用户的主目录中新建好 .ssh 文件夹。
下载解包
我使用的是预编译的二进制包。需要从源码编译的话,请参考一般 Go 语言项目的编译。下载后解包到你喜欢的地方,例如 /usr/share/gogs/
或者 /home/git/gogs/
。文件夹的内容如下。
1 | $ ls /home/git/gogs/ |
运行安装
首先建立好数据库。
1 | 在安装完mysql之后,我们还需要创建一个数据库,将之取名为gogs, |
在 Gogs 目录的 scripts/mysql.sql
文件是数据库初始化文件。执行 mysql -u root -p < scripts/mysql.sql
(需要输入密码)即可初始化好数据库。
然后登录 MySQL 创建一个新用户 gogs,并将数据库 gogs 的所有权限都赋予该用户。
1 | $ mysql -u root -p |
运行 gogs web 把 Gogs 运行起来,然后访问 http://服务器IP:3000/
来进行安装,填写好表单之后提交就可以了。
需要注意的是, 0.6.9.0903 Beta 版本有个 bug,允许在关闭注册的情况下不添加管理员,这样安装完成之后将没有任何用户可以登录。所以请务必在安装界面指定一个管理员帐号。
配置调整
配置文件位于 Gogs 目录的 custom/conf/app.ini
,是 INI 格式的文本文件。详细的配置解释和默认值请参考 官方文档 ,其中关键的配置大概是下面这些。
1 | RUN_USER 默认是 git ,指定 Gogs 以哪个用户运行 |
nginx 反代
在 /etc/nginx/sites-available
中新建一个文件,把以下内容写入文件中。
1 | server { |
然后进入 /etc/nginx/sites-enabled
中,执行 ln -s ../sites-available/配置文件名
启用这个配置文件。
最后重启 nginx 就好了,Ubuntu 下是 sudo service nginx restart
。
服务脚本
此处的服务脚本是针对 Debian 系的 init 脚本。
其他地方处理办法:
gogs提供多重方式进行安装,比较常用的有两种。一个是二进制安装,另一个是通过源代码安装。
然后切换到解压得到的目录之后,直接运行 ./gogs web 即可在服务器上运行gogs服务了。
./gogs -h
得到更多的命令行参数说明。
nohup ~/gogs/gogs web > /dev/null 2>&1 &
启动gogs服务之后,首次运行会让你进行相关的配置。
主要分为3个配置项,
一是数据库的配置,包括数据库地址及密码;
二是服务的应用配置,包括域名,路径等等;
三是可选邮件服务和管理员配置。
后记
h5ai使用教程
什么是 h5ai
H5ai是一款功能强大 php 文件目录列表程序,由德国开发者 Lars Jung 主导开发,它提供多种文件目录列表呈现方式,支持多种主流 Web 服务器,例如 Nginx、Apache、Cherokee、Lighttpd 等,支持多国语言,可以使用本程序在线预览文本、图片、音频、视频等。
安装
搭建Web服务
本文使用LNMP环境,使用教程到LNMP查看。
并创建一个新虚拟机。
修改配置文件
编辑Nginx主机文件和PHP配置文件,分别在/usr/local/nginx/conf/vhost/
下和/usr/local/php/etc/php.ini
。
1 | vim /usr/local/nginx/conf/vhost/your_domain.conf |
配置虚拟机空间
去到官网下载h5ai
,解压到虚拟机空间目录。
1 | . |
重启Web服务
LNMP重启命令lnmp restart
。
1 | +-------------------------------------------+ |
参考
BBR魔改版安装教程(转载)
最近大佬 @Yankee 更新了一份 BBR魔改方案,使得BBR加速效果更明显了。
CentOS安装Docker-CE
警告:切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker.
准备工作
系统要求
Docker 支持 64 位版本 CentOS 7/8,并且要求内核版本不低于 3.10。 CentOS 7 满足最低内核的要求,但由于内核版本比较低,部分功能(如 overlay2
存储层驱动)无法使用,并且部分功能可能不太稳定。
卸载旧版本
旧版本的 Docker 称为 docker
或者 docker-engine
,使用以下命令卸载旧版本:
1 | $ sudo yum remove docker \ |
使用 yum 安装
执行以下命令安装依赖包:
1 | $ sudo yum install -y yum-utils |
鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
执行下面的命令添加 yum
软件源:
1 | $ sudo yum-config-manager \ |
如果需要测试版本的 Docker 请执行以下命令:
1 | $ sudo yum-config-manager --enable docker-ce-test |
安装 Docker
更新 yum
软件源缓存,并安装 docker-ce
。
1 | $ sudo yum install docker-ce docker-ce-cli containerd.io |
CentOS8 额外设置
由于 CentOS8 防火墙使用了 nftables
,但 Docker 尚未支持 nftables
, 我们可以使用如下设置使用 iptables
:
更改 /etc/firewalld/firewalld.conf
1 | \# FirewallBackend=nftables |
或者执行如下命令:
1 | $ firewall-cmd --permanent --zone=trusted --add-interface=docker0 |
使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS 系统上可以使用这套脚本安装,另外可以通过 --mirror
选项使用国内源进行安装:
若你想安装测试版的 Docker, 请从 test.docker.com 获取脚本
1 | \# $ curl -fsSL test.docker.com -o get-docker.sh |
执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 的稳定(stable)版本安装在系统中。
启动 Docker
1 | $ sudo systemctl enable docker |
建立 docker 用户组
默认情况下,docker
命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root
用户和 docker
组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root
用户。因此,更好地做法是将需要使用 docker
的用户加入 docker
用户组。
建立 docker
组:
1 | $ sudo groupadd docker |
将当前用户加入 docker
组:
1 | $ sudo usermod -aG docker $USER |
退出当前终端并重新登录,进行如下测试。
测试 Docker 是否安装正确
1 | $ docker run --rm hello-world |
若能正常输出以上信息,则说明安装成功。
镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以配置 Docker 国内镜像加速。
添加内核参数
如果在 CentOS 使用 Docker 看到下面的这些警告信息:
1 | WARNING: bridge-nf-call-iptables is disabled |
请添加内核配置参数以启用这些功能。
1 | $ sudo tee -a /etc/sysctl.conf <<-EOF |
然后重新加载 sysctl.conf
即可
1 | $ sudo sysctl -p |
[转载]深入浅出 Git
本文转载自Coding
这篇文章解释了 Git 是如何工作的。(如果相关内容的谈话更吸引你,你可以观看链接中的视频。)
本文假设你已经对 Git 理解到了可以对你的项目进行版本控制的程度。本文专注于支撑 Git 的图结构以及这些图的性质影响 Git 行为的方式。通过了解底层,你可以将你心中对 Git 的模型建立在事实之上,而不是基于通过积累使用经验而建立的假设上。这个更真实的模型可以让你更好的理解 Git 做了什么,正在做什么以及将要做什么。
本文由一系列针对单个项目的 Git 命令构成。时不时的,还将有一些对于 Git 所建立的图数据结构的观察。这些观察阐述了图的性质和相应性质所产生的影响。
读完本文后,如果你希望更深入的了解 Git,可以阅读我关于 Git 的 JavaScript 实现gitlet.js(heavily annotated source code)。
创建项目
1 | ~ $ mkdir alpha |
用户为项目建立一个名为 alpha
的目录。
1 | ~/alpha $ mkdir data |
进入目录 alpha
,并在下面建立名为 data
的目录。在这个目录中建立一个名为 letter.txt
的文件,其中包含一个字符 a
。此时目录结构看起来像这样:
1 | alpha |
初始化版本库
1 | ~/alpha $ git init |
git init
命令使得当前目录成为一个 Git 版本库。为此这条命令建立了一个名为的 .git
目录并且向其中写入了一些文件。这些文件定义和记录了关于Git配置和项目历史的所有相关内容。它们只是普通的文件,其中并没有什么类似魔法的神奇之处。用户可以使用文本编辑器和命令行阅读或编辑这些文件。也就是说:用户可以像获取和修改项目文件一样简单地获取和修改项目历史。
这时候目录 alpha
的结构看起来像这样:
1 | alpha |
.git
目录和其中的内容属于 Git。所有其他的文件一起被称为工作副本,属于用户。
添加一些文件
1 | ~/alpha $ git add data/letter.txt |
用户对文件 data/letter.txt
运行命令 git add
。这一操作产生两个效果。
首先,这一操作在目录 .git/objects/
新建一个BLOB(binary large object 二进制大对象)文件。
这个BLOB文件包含了文件 data/letter.txt
压缩过的内容。文件名由内容的散列值得到。散列一个文本片段,意味着对其内容运行一个程序将其转变为一段更短小[^1]并且唯一地[^2]代表原先文本的文本片段。例如,Git 将 a
散列为 2e65efe2a145dda7ee51d1741299f848e5bf752e
。最前面的两个字符被用于对象数据库中目录的命名:.git/objects/2e/
。散列值的其余部分被用于包含添加文件内容的BLOB文件的命名:.git/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e
。
注意到将一个文件添加到 Git 中时 Git 是如何将文件的内容保存到 objects
目录中的。即便用户从工作副本中删除文件 data/letter.txt
,它的内容在 Git 库中也是安全的。
其次,git add
命令将文件添加到索引中。索引是一个包含所有 Git 所要跟踪文件的列表。它以文件 .git/index
保存。这个文件每一行建立起一个被跟踪文件与这个文件被添加时散列值的对应关系。这是在 git add
命令被执行之后的索引文件:
1 | data/letter.txt 2e65efe2a145dda7ee51d1741299f848e5bf752e |
用户建立一个叫做 data/number.txt
的文件,内容为 1234
。
1 | ~/alpha $ printf '1234' > data/number.txt |
工作副本看起来像这样:
1 | alpha |
用户将这个文件添加到 Git 中。
1 | ~/alpha $ git add data |
命令 git add
创建一个包含 data/number.txt
内容的BLOB文件。同时添加一条文件 data/number.txt
的索引项,指向对应的BLOB文件。在 git add
命令第二次被执行之后索引文件如下:
1 | data/letter.txt 2e65efe2a145dda7ee51d1741299f848e5bf752e |
注意到即便用户运行 git add data
,也只有 data
目录中的文件在索引文件中被列出。文件夹 data
没有被单独列出。
1 | ~/alpha $ printf '1' > data/number.txt |
当用户最初建立 data/number.txt
时,他想要写入 1
,而不是 1234
。用户做了更改并且将文件添加到索引中。这次的命令创建了一个新的包含了新内容的BLOB文件。并且更新了文件 data/number.txt
的索引项指向新的BLOB文件。
进行一次提交
1 | ~/alpha $ git commit -m 'a1' |
用户做提交 a1
。Git 显示出一些有关此次提交的数据。很快我们将看懂这些信息。
提交命令分三步执行。首先,命令建立了一个树图来表示被提交的项目版本的内容。其次,建立一个提交对象。最后,将当前分支指向新的提交对象。
创建一个树图
Git 通过从索引建立一张树图来记录项目的当前状态。这张树图记录了项目中每一个文件的位置和内容。
图由两种对象组成:BLOB 文件和树。
BLOB 文件由 git add
存储。它们表示了文件的内容。
树是在提交被进行时被存储的。树表示了工作副本中的目录。
下面就是记录新提交中 data
目录内容的树对象:
1 | 100664 blob 2e65efe2a145dda7ee51d1741299f848e5bf752e letter.txt |
第一行记录了再现 data/letter.txt
文件所需的一切内容。第一部分表明文件权限。第二部分表明此项内容由BLOB文件表示而不是一个树对象。第三个部分表明对应BLOB的散列值。第四部分表明文件名。
第二行记录了关于 data/number.txt
文件的相同内容。
下面是代表 alpha
的树对象,即项目的根目录:
1 | 040000 tree 0eed1217a2947f4930583229987d90fe5e8e0b74 data |
树中只有一行并指向 data
树。
a1
提交的树图
在上面的图中,root
树指向 data
树。data
树指向 data/letter.txt
和 data/number.txt
对应的BLOB文件。
创建一个提交对象
git commit
在创建树图之后创建一个提交对象。提交对象是 .git/objects/
目录中另一个文本文件:
1 | tree ffe298c3ce8bb07326f888907996eaa48d266db4 |
第一行指向树图。散列值对应于代表工作副本根目录的树图,这里是 alpha
目录。最后一行是提交信息。
a1
提交对象指向它的树图
将当前分支指向新提交
最后,提交命令将当前分支指向新的提交对象。
哪一个分支是当前分支?Git 在 .git/HEAD
目录中的 HEAD
文件中寻找相关信息:
1 | ref: refs/heads/master |
这代表 HEAD
指向 master
。master
分支是当前分支。
HEAD
和 master
都是引用。引用是 Git 或用户用来标识特定分支的标签。
代表 master
分支的引用并不存在,因为这是版本库的第一次提交。Git 在路径 .git/refs/heads/master
创建文件并且将内容写为提交对象的散列值:
1 | 74ac3ad9cde0b265d2b4f1c778b283a6e2ffbafd |
(如果你在 Git 中输入你所读到的命令,a1
提交的散列值将与我这里的不同。内容对象例如 BLOB 文件和树总是散列到与本文相同的值上。提交对象并不如此,因为其中包含日期和创建者的名字。)
让我们将 HEAD
和 master
添加到 Git 图中:
HEAD
指向 master
并且 master
指向 a1
提交
HEAD
指向 master
,如提交之前一样。但是现在 master
开始存在并且指向新的提交对象。
创建一个非首次提交
下面是 a1
提交之后的 Git 图。其中包括了工作拷贝和索引。
包含工作拷贝和索引的 a1
提交
注意,工作拷贝,索引以及 a1
提交中的 data/letter.txt
和 data/number.txt
文件内容是一样的。索引和 HEAD
提交的散列值都指向的都是 BLOB 对象,但是工作拷贝的内容是作文文本文件存放在不同的地方的。
1 | ~/alpha $ printf '2' > data/number.txt |
用户将 data/number.txt
的内容设置为 2
。该操作更新了工作拷贝,但是没有改变 HEAD
提交以及索引。
工作拷贝中的 data/number.txt
设置为 2
1 | ~/alpha $ git add data/number.txt |
用户将文件添加到 Git。该操作在 object
目录中创建了一个内容为 2
的 BLOB 文件。在新的 BLOB 文件中添加了一条指向 data/number.txt
的索引项。
工作拷贝和索引中的 data/number.txt
设置为 2
1 | ~/alpha $ git commit -m 'a2' |
用户提交。步骤同上。
第一步,创建了一个代表索引内容的树图。
data/number
的索引项发生了改变。旧的 data
树不能再反映当前的 data
目录的索引状态。所以必须创建一个一个新的 data
树:
1 | 100664 blob 2e65efe2a145dda7ee51d1741299f848e5bf752e letter.txt |
新的 data
树与旧的 data
树散列值不同。必须创建一个新的 root
树来记录当前散列值:
1 | 040000 tree 40b0318811470aaacc577485777d7a6780e51f0b data |
第二步,创建一个新的提交对象。
1 | tree ce72afb5ff229a39f6cce47b00d1b0ed60fe3556 |
提交对象的第一行指向了新的 tree
对象。第二行指向 a1
提交:当前提交的父提交。要找到父提交,Git 首先找到头指针 HEAD
,然后顺着找到 master
然后获得 a1
提交的散列值。
第三步,将 master
分支文件的内容设置为新提交的散列值。
a2
提交
不包括工作拷贝和索引的 Git 图
图属性: 内容被储存为一个树对象。这表明只有差异被储存在对象数据库中。从上图可以看出。a2
提交重复使用了在 a1
提交之前创建的 a
BLOB 文件。类似的,如果整个工作目录的内容在一次次提交中没有发生改变,树对象以及所有的 BLOB 文件都能够被重复使用。通常来说,各次提交之间只有很小的改动。这也就意味着 Git 能够使用很小的空间来储存大量的提交历史。
图属性:每个提交都有一个父提交,也就是说仓库可以储存项目的历史改动。
图属性:引用(refrences 或者 refs,译者注)是指向一部分提交历史或者其他的条目。也就是说可以给提交取一个有意义的名字。用户将它们的工作组织成单行的固定短语,比如 fix-for-bug-376
。Git 使用了一些像 HEAD
,MERGE_HEAD
和 FETCH_HEAD
的符号引用来支持一些用来处理提交历史的命令。
图属性:objects/
目录中的节点都是不可变的。也就是说其中的内容能编辑但是不能删除。你添加到仓库中的所有内容以及你所做的每一个提交都存放在 objects
目录[^3]中的某个地方.
图属性:引用是可变的。因此,可以改变一个引用的意义。master
所指向的提交可能是当前项目的最好的版本,但是过段时间,它将会被一个更新的或者更好的提交所取代。
图属性:由引用所指向的工作拷贝以及提交很容易获取,但是获取其他的引用就不那么简单了。也就是说调出最近的提交历史更加容易,但是那也会时常会改变。
工作拷贝是最容易在历史提交中调出的,因为它是仓库的根节点。调出它甚至不需要执行 Git 命令。同时它也是提交历史中的最早的永久节点。用户可以创建一个文件的许多版本,但是如果没有对它们执行 add
操作的话,Git 将不会记录它们。
头指针 HEAD
所指向的提交很容易被调出。它在检出分支的顶端。要查看其中的内容,用户只需执行 stash[^4] 然后检出工作拷贝。同时,HEAD
改变频率最高的引用。
有固定引用指向的提交很容易被调出。用户可以轻易的检出那个分支。分支的顶端通常没有 HEAD
的改变频率高,but often enough for the meaning of a branch name to be changeable.
要调出没有被任何引用所指向的提交很困难。用户在某个引用上提交得越多,操作之前的提交就越不容易。但我们通常很少操作很久之前的提交[^5]。
检出一个提交
1 | ~/alpha $ git checkout 37888c2 |
用户通过对应的散列值来检出 a2
提交。(你过你照搬了以上的 Git 命令,在你的电脑上不会起作用。请使用 git log
命令来查找 a2
提交的散列值。)
检出有以下 4 个步骤。
第一步,Git 获得 a2
提交以及其指向的树图。
工作区的内容已经和树图保持一致了,因为我们的HEAD之前就已经通过master指向a2提交了。
第二步,将树图中的文件写入工作拷贝中。这不会产生什么变化。工作拷贝的内容已经和树图中的保持一致了,因为头指针 HEAD
早已经通过 master
指向了 a2
提交。
第三步,Git 将树图中的文件写入索引。同样,这也不会产生什么变化,index 早就有 a2
提交的内容了。
第四步,头指针 HEAD
的内容被设置为 a2
提交的散列值:
1 | f0af7e62679e144bb28c627ee3e8f7bdb235eee9 |
对 HEAD
写入一个散列值会导致仓库进入头指针分离状态。注意下图中的 HEAD
直接指向了 a2
提交,而不是指向 master
。
分离头指针到 a2
提交
1 | ~/alpha $ printf '3' > data/number.txt |
用户将 data/number.txt
的值设置为 3
然后提交更改。Git 找到 HEAD
然后找到 a3
提交的父提交。这回返回 a2
提交的散列值而不是查找一个分支引用。
Git 将 HEAD
更新,使其直接指向 a3
提交的哈希值。此时仓库仍然处于头指针分离状态,而没有在一个分支上,因为没有引用指向 a3
提交亦或是它之后的提交。这意味着它很容易丢失。
从现在起,图示中大多的树和 BLOB 都会省略。
没有在分支上的 a3
提交
创建一个分支
1 | ~/alpha $ git branch deputy |
用户创建了一个新的叫做 deputy
的分支。这个操作在 .git/refs/heads/deputy
目录下创建了新文件,其包含了 HEAD
指向的散列值:a3
提交的散列值。
图属性:分支其实就是引用,而引用其实就是文件。这也就是说 Git 的分支是很轻量的。
创建 deputy
分支的操作实际上将新的 a3
提交安全地放在了一个新的分支上。HEAD
指针目前仍处于分离状态,因为它现在仍是直接指向了一个提交。
处于 deputy
分支的 a3
提交
检出分支
1 | ~/alpha $ git checkout master |
用户检出了’master’分支
首先, Git 找到’master’分支所指向的a2
提交对象并获取该提交对象所指向的树对象.
接下来 Git 会将树对象储存的文件写到当前工作副本中, 该操作将覆写data/number.txt
为2
.
第三步, Git 将树对象中的文件入口写入 index, data/number.txt
的文件入口将会被更新为2
blob 的 hash 值
最后 Git 通过将HEAD
中的 hash 值替换为如下内容来使HEAD
指向master
分支:
1 | ref: refs/heads/master |
master
分支被检出, 指向’a2’提交
检出与当前工作副本相冲突的分支
1 | ~/alpha $ printf '789' > data/number.txt |
用户无意中将data/number.txt
的内容更改为789
, 此时他尝试检出deputy
分支, Git 没有执行这次检出.
当前HEAD
指向 master
分支, 其所指向提交a2
中data/number.txt
的内容为2
. deputy
分支指向的提交a3
中data/number.txt
的内容为3
. 当前工作副本中data/number.txt
的内容为789
. 这个文件的各个版本各不相同, 必须通过一种方法来消除这些差异.
如果 Git 将当前版本的data/number.txt
替换成将要检出的分支中的版本则会造成数据遗失, Git 不允许这种情况发生.
如果 Git 将要检出的分支与当前工作副本合并则会导致冲突
因此 Git 中断了此次检出.
1 | ~/alpha $ printf '2' > data/number.txt |
用户注意到了对data/number.txt
的意外操作, 在把它的内容修改回2
后成功检出了deputy
分支.
deputy
分支被检出
合并一个祖先分支
1 | ~/alpha $ git merge master |
用户将master
分支合并到deputy
中. 合并两个分支其本质为合并两个提交对象. 其一-作为接受者(receiver)-是deputy
所指向的提交对象, 其二-作为给予者(giver)-是master
所指向的提交对象. 在这次合并中 Git 不会执行任何操作, 而仅仅打印出Already up-to-date.
.
[图属性]: 图示中的一系列提交对象可以被认为是对仓库内容进行的一些列更改. 因此在一次合并当中, 若想要并入的分支是当前分支的祖先分支, Git不会执行任何操作, 因为想要并入分支中的改动已经被包含在了当前分支中.
合并后代提交
1 | ~/alpha $ git checkout master |
用户检出了 master
分支.
master
被检出并指向了 a2
提交
1 | ~/alpha $ git merge deputy |
他们合并 deputy
分支到 master
。Git 发现赠予提交的 a2
是源提交 a3
的祖先提交。这里可以执行 fast-forward 合并。
Git 获取赠予提交和它指向的树图,将树图中的文件写入工作区和 index。这将 master
分支 fast-forward
到了 a3
提交。
deputy
分支的 a3
提交 fast-forward 合并到了 master
分支
图属性:图中的提交系列被视为对仓库内容的一系列更改。这意味着,如果赠予提交是接收提交的后代提交,提交历史不会变。已经存在一序列提交来描述赠予提交和接收提交之间的变化。但是尽管 Git 历史不变,Git 的状态图是会改变的。HEAD
指向的具体引用会更新以指向赠予提交。
合并不同提交线的两个提交
1 | ~/alpha $ printf '4' > data/number.txt |
用户将文件 number.txt
的内容改为 4
然后提交到 master
.
1 | ~/alpha $ git checkout deputy |
用户检出 deputy
分支,将文件 data/letter.txt
内容改为 b
然后提交到 deputy
分支。
a4
提交到 master
, b3
提交到 deputy
, deputy
被检出
图属性: 多个提交可以共用一个父提交,这意味着新提交线可以在提交历史里创建出来。
图属性: 某提交可以有多个父提交,这意味着两个不同的提交线可以被一个合并提交来合并。
1 | ~/alpha $ git merge master -m 'b4' |
用户合并 master
到 deputy
.
Git 发现接收提交 b3
和赠予提交 a4
在不同的提交线上。它创建了一个合并提交。这个过程总共分八步。
第一步,Git 将接收提交的哈希值写入文件 alpha/.git/MERGE_HEAD
。此文件的存在说明 Git 正在做合并操作。
第二步,Git 查找基提交:即接收提交和赠予提交共有的一个最近父提交。
a3
是 a4
和 b3
的基提交
图属性:每个提交都有一个父提交。这意味着我们可以发现两个提交线分开自哪个提交。Git 查找 b3
和 a4
的所有祖先提交,发现了最近的公共父提交 a3
,即为他们的基提交。
第三步,Git 为基提交、接收提交和赠予提交创建索引。
第四步,Git 产生接收提交和赠予提交相对于基提交的 diff,此处的 diff 是一个文件路径列表,指向一个变化:包括添加、移除、修改或冲突。
Git 获取基提交、接收提交和赠予提交的文件列表,针对每一个文件,通过对比 index 来判断它的状态与写入变更。它将对应条目写入 diff。在这个例子中,diff 包含两个条目。
第一项记录 data/letter.txt
的状态。该文件内容分别是基提交中的 a
、接收提交中的b
和赠予提交中的a
。文件内容在基提交和接收提交不同,但在基提交和赠予提交相同。Git 发现文件内容被接收提交修改了,而不是在赠予提交中。data/letter.txt
的状态是修改,而不是冲突。
第二项记录 data/number.txt
的变更。在这个例子中,该文件内容在基提交和接收提交中是相同的,但在基提交和赠予提交是不同的。 data/number.txt
条目的状态也是修改。
图属性:查找一个合并操作的基提交是可行的。这意味着,如果基提交中的一个文件只在接收提交或赠予提交做了修改,Git 可以自动除了合并文件,这样就减少了用户的工作量。
第五步,Git 将差异中的项被应用到工作区。data/letter.txt
内容被修改为b
,data/number.txt
内容被修改为 4
。
第六步,Git 将差异中的项应用到 index。data/letter.txt
会指向内容为 b
的 blob,data/number.txt
会指向内容为 4
的 blob。
第七步,更新后的 index 被提交:
1 | tree 20294508aea3fb6f05fcc49adaecc2e6d60f7e7d |
注意,这个提交有两个父提交。
第八步,Git 将当前分支 deputy
指向新提交。
a4
递归合并入 b3
产生 b4
合并不同提交线且有相同修改文件的两个提交
1 | ~/alpha $ git checkout master |
用户检出 master
,他们将 deputy
合并到 master
。此操作将master
fast-forwards 指向 b4
。master
和 deputy
现指向了相同的提交。
deputy
合并到 master
将 master
更新到新提交 b4
1 | ~/alpha $ git checkout deputy |
用户检出 deputy
。将 data/number.txt
内容修改为 5
并提交到 deputy
分支。
1 | ~/alpha $ git checkout master |
用户检出 master
。将 data/number.txt
内容修改为 6
并提交到 master
分支。
b5
提交在 deputy
b6
提交在 master
1 | ~/alpha $ git merge deputy |
用户将 deputy
合并到 master
。这里存在冲突故合并中止。对于有冲突的合并操作,执行步骤和没有冲突的合并的前六步是相同的:设置 .git/MERGE_HEAD
,查找基提交,创建基提交、接收提交和赠予提交的索引,生成 diff,更新工作区,更新 index。由于冲突,第七步提交和第八步更新 ref 不再执行。让我们再来看看这些步骤,观察到底发生了什么。
第一步,Git 将赠予提交的哈希值写入 .git/MERGE_HEAD
.
MERGE_HEAD
写入在 b5
合并入 b6
第二步,Git 查找到基提交, b4
.
第三步,Git 创建基提交、接收提交和赠予提交的索引。
第四步,Git 生成集合了接收提交和赠予提交相对于基提交的差异列表,这个 diff 是一份指向变更的文件路径:添加、删除、修改或冲突。
在本例中,差异列表仅包含一项: data/number.txt
。它的状态被标为冲突因为其内容在接收提交,赠予提交和基提交中都是变化的。
第五步,差异列表中的文件被写入工作区。对于冲突的部分,Git 将两个版本都写入工作区的文件中。data/number.txt
的内容被变更为:
1 | <<<<<<< HEAD |
第六步,差异列表中的文件被写入 index。index 中的项被文件路径和 stage 的组合唯一标识。没有冲突的项 stage 为 0
。在该合并前,index 看起来像下面的样子,标有 0
的是 stage 值:
1 | 0 data/letter.txt 63d8dbd40c23542e740659a7168a0ce3138ea748 |
在合并 diff 写入 index 后,index 变成:
1 | 0 data/letter.txt 63d8dbd40c23542e740659a7168a0ce3138ea748 |
stage 0
的 data/letter.txt
项和合并前是一样的。stage 0
的 data/number.txt
项已经不存在,取代的是三个新项。stage 1
的项包含该文件在基提交中内容的哈希值,stage 2
包含接收提交的哈希值,stage 3
包含赠予提交的哈希值。这三项表明文件 data/number.txt
存在冲突。
合并中止了。
1 | ~/alpha $ printf '11' > data/number.txt |
用户通过将 data/number.txt
的内容修改为 11
将两个有冲突的文件合并,将文件添加到 index,Git 创建一个包含11
的 blob,创建一个冲突文件以告诉 Git 冲突已经解决了。Git 移除 index 中的 1
, 2
和 3
,并添加 stage 为 0
的 data/number.txt
项,该项指向新创建 blob 的哈希值。现在 index 变为:
1 | 0 data/letter.txt 63d8dbd40c23542e740659a7168a0ce3138ea748 |
1 | ~/alpha $ git commit -m 'b11' |
第七步,用户进行提交。Git 发现存在 .git/MERGE_HEAD
,意味着合并还在进行中。通过检查 index 发现没有冲突。它创建了一个新提交 b11
,用来记录合并后的内容。然后删除 .git/MERGE_HEAD
。合并完成。
第八步,Git 将当前分支 master 指向新提交。
b5
和 b6
递归合并为b11
删除文件
这幅 Git 示意图包含对于最后一次提交的历史提交、树对象、储存对象、工作副本以及索引。
工作副本、索引, b11
提交以及它的树对象。
1 | ~/alpha $ git rm data/letter.txt |
该用户告诉 Git 删除 data/letter.txt
。 文件将从工作副本中删除,该文件的入口也将从索引中消失。
After data/letter.txt
rm
ed from working copy and index
data/letter.txt
从工作副本以及索引中 rm
过后
1 | ~/alpha $ git commit -m '11' |
该用户进行提交了。 一般说来,Git 会在提交时构建一张树图代表索引内容,data/letter.txt
并不会出现在树中,因为它不在索引里。
data/letter.txt
删除后进行 11
提交
复制一个库
1 | ~/alpha $ cd .. |
用户复制 alpha/
库中的内容到 bravo/
目录中,使得目录结构如下:
1 | ~ |
bravo
目录即生成另一张 Git 图:
当 alpha
cp
至 bravo
后产生的新图
链接至另一个库
1 | ~ $ cd alpha |
用户回到 alpha
库中,设置 bravo
为一个 alpha
的远程库,这会使 alpha/.git/config
多了这几行:
1 | [remote "bravo"] |
这几行说明有个叫 bravo
的远程库在 ../bravo
中。
获取远程分支
1 | ~/alpha $ cd ../bravo |
进入 bravo
目录. 覆写 文件 data/number.txt
为 12
,将改动提交到bravo
仓库的分支 master
.
bravo
仓库中的一个提交12
1 | ~/bravo $ cd ../alpha |
进入 alpha
目录,从 bravo
仓库获取 master
分支到 alpha
仓库到 master
分支。这个过程包含了四个步骤。
第一步,Git 获取 bravo
仓库中 master
分支所指向提交的 hash 值,即 提交 12
所对应的 hash 值。
第二步,Git 会给做出 12
这个提交依赖的所有对象组成的一个列表:包含了提交对象自身,树图中的对象,提交 12
所对应的父提交,父提交树图对象。然后从列表中移除所有在 alpha 仓库对象数据库中已有的对象。复制剩下的对象到目录 alpha/.git/objects/
。
第三步,将 12
这个提交的 hash 值 写入文件 alpha/.git/refs/remotes/bravo/master
。
第四步,文件 alpha/.git/FETCH_HEAD
内容被置为:
1 | 94cd04d93ae88a1f53a4646532b1e8cdfbc0977f branch 'master' of ../bravo |
文件内容表明从 bravo
仓库 master
分支获取了 12
这个提交。
从 bravo/master
获取后的alpha
仓库状态
图属性: 对象可以被复制。以为址不同仓库间可以共享对象。
图属性:一个仓库可以存储远程分支的引用,例如 alpha/.git/refs/remotes/bravo/master
。意味着,仓库可以在本地记录远程分支的状态。如果远程分支没有改变,这个状态就一直是正确的。
合并分支 FETCH_HEAD
1 | ~/alpha $ git merge FETCH_HEAD |
用户合并了分支FETCH_HEAD
,这个分支也是一个指向某个提交的一个引用,被解析为指向 12
这个提交的一个引用。
合并前,HEAD
指向此次被合并的引用, 11
这个提交。 完成 fast-forward
合并后, HEAD
指向提交 12
。
alpha
仓库合并FETCH_HEAD
后的状态
拉取远程仓库
1 | ~/alpha $ git pull bravo master |
拉取远程仓库 bravo
的 master
分支到本地仓库 alpha
。pull
操作是 “获取然后合并FETCH_HEAD
“ 这两个命令的一个快捷方式。执行了这个命令后,反馈 Already up-to-date
, 说明本地和远程内容一样。
克隆一个仓库
1 | ~/alpha $ cd .. |
切换到 alpha
的上级目录,克隆仓库alpha
到目录 charlie
,这个操作的结果,如同通过复制到到bravo
仓库。
克隆仓库做的事情包括:创建新目录charlie
; 在目录charlie
下初始化仓库;将 alpha
作为远程仓库的 origin
分支; 获取 origin
分支到本地;合并分支 FETCH_HEAD
。
推送本地分支到远程仓库
1 | ~ $ cd alpha |
切换回 alpha
仓库, 覆写文件 data/number.txt
为 13
,将改动提交到alpha
仓库的分支 master
。
1 | ~/alpha $ git remote add charlie ../charlie |
将本地目录charlie
作为本地仓库的alpha
的一个远程仓库。
1 | ~/alpha $ git push charlie master |
推送本地仓库的master
分支到远程仓库 charlie
。
13
这个提交所需要的所有对象,被复制到到远程仓库charlie
中。
至此,推送操作完成。和之前一样,如果操作出错,Git 会提示出错内容。例如,Git 会拒绝推送到一个在远程切出的分支。听起来是不是很有道理 ?由于一次推送操作会更新远程索引和 HEAD
的指向,如果这个时候有人正在编辑远程副本的时候,就会导致冲突,出现不一致。
现在,可以创建一个新分支,合并 13
这个内容到新分支 ,推送到远程仓库 charlie
。但是,希望达到的是可以推送任何想推送的内容到仓库,希望又有一个可以推送和拉取到中心仓库,但是没人能直接推送到此中心仓库。有点像 GitHub 远程操作,最后的解决方案就是,裸库。
克隆一个裸库
1 | ~/alpha $ cd .. |
切换到 alpha
上级目录,将裸库到目录 delta
。这个克隆有两点不同。一是config
文件表明这是一个裸库;二是,通常位于 .git
目录下的文件被现在存放在仓库的根目录下:
1 | delta |
克隆alpha
到 delta
后的仓库图
推送分支到裸库
1 | ~ $ cd alpha |
切换到 alpha
目录。使用 ../delta
目录 创建远程仓库delta
。
1 | ~/alpha $ printf '14' > data/number.txt |
覆写文件 data/number.txt
为 14
,将改动提交到alpha
仓库的分支 master
。
alpha
仓库中的14
提交
1 | ~/alpha $ git push delta master |
推送master
到仓库delat
。推送过程包含三步:
第一步,14
提交需要的所有对象,从alpha/.git/objects/
目录,复制到目录delta/objects/
。
第二步,更新文件delta/refs/heads/master
内容,指向提交 14
。
第三步,更新文件alpha/.git/refs/remotes/delta/master
内容,指向提交 14
。本地仓库alpha
就有了远程仓库delta
的一份最新状态记录。
推送alpha
仓库到提交14
到仓库delta
总结
Git 是在基于图的思想上构建的,几乎所有的 Git 命令都在维护这个图。想要深入理解 Git,就需要把精力集中在这个图的属性上,而不是在 Git 操作流程 或者 Git 命令。
想要更多的理解 Git,就去详细剖析 .git
目录,看里面都有些什么文件。通过改变文件内容,观察里面这些文件的变化。手动创建提交,看看可以把这个仓库搞什么鬼样子,然后尝试修复这些问题。
- 通过这个案例,hash 值比原始文件内容要长。但是,所有文件的内容比 hash 值要长,这样的效果就是,表达的意思比原始文件要更加简洁明了。
- 有可能会出现两个不同的文件内容的 hash 值一样,但是,这个 机率很小.。
git prune
会删除一个引用不能获取的所有对象。如果使用此命令,可能导致文件内容丢失。git stash
会在一个安全的地方存储HEAD
与当前工作区的差异。因此稍后可以恢复工作区。rebase
可以对历史命令进行操作,达到新增,修改,删除提交的目的。
利用GitLab搭建私有代码库
GitLab 简介
GitLab 是利用 Ruby On Rails 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面进行访问公开的或者私人项目。它拥有与 GitHub 类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。团队成员可以利用内置的简单聊天程序( Wall )进行交流。它还提供一个代码片段收集功能可以轻松实现代码复用,便于日后有需要的时候进行查找。
GitHub 它是一个开源的社区, 如果你想把你们公司的项目放在上面就得交钱, 如果不交钱就得开源, 而 GitLab 是一个本地的项目管理仓库, 既然是本地那么想怎么玩就怎么玩儿
优点
- 社区版基于 MIT License 开源完全免费
- 无用户,协作和仓库限制
- 保护项目源码安全
- 管理仓库,用户与访问权限
搭建环境
- 服务器:微软Azure主机
- 操作系统:Ubuntu 16.04 X64
- 软件包:LNMP组件
安装GitLab
选择版本
GitLab分为社区版(GitLab Community Edition)和企业版(GitLab Enterprise Edition)。社区版免费,企业版收费,但是功能比社区版多。根据目前的需求,选择安装社区版(GitLab-CE)。
安装配置依赖项
如想使用Postfix来发送邮件,在安装期间请选择’Internet Site’. 您也可以用sendmai或者 配置SMTP服务 并 使用SMTP发送邮件.
1 | apt install curl openssh-server ca-certificates postfix |
添加GitLab仓库,并安装到服务器上
1 | curl -sS http://packages.gitlab.cc/install/gitlab-ce/script.deb.sh | sudo bash |
启动GitLab
1 | gitlab-ctl reconfigure |
使用浏览器访问GitLab
首次访问GitLab,系统会让你重新设置管理员的密码,设置成功后会返回登录界面.
默认的管理员账号是root,如果你想更改默认管理员账号,请输入上面设置的新密码登录系统后修改帐号名.
未完待续。。。。。。
LEDE编译教程
简介
LEDE-project是 OpenWrt 的一个分支,是为了解决OpenWrt遗留的一些问题而设立,拥有更多的支持。
配置环境
开发环境
LEDE-project开发环境及依赖:
- 系统:ubuntu 16.04 x64
- 依赖:build-essential subversion git-core libncurses5-dev zlib1g-dev gawk flex quilt libssl-dev xsltproc libxml-parser-perl mercurial bzr ecj cvs unzip
安装依赖
1 | apt update && apt upgrade -y |
下载源码
1 | git clone https://github.com/lede-project/source.git LEDE-project |
编译
编辑固件设置
1 | make menuconfig |
执行后出现配置界面,根据需要选择自己的配置选项。
备注:记得勾选SDK
开始编译
1 | make V=s |
注意:
- 编译时间较长;
- 编译过程中,需要爬墙下载相关软件包;
完成编译
编译完成后,便可以在/bin/$target
目录下找到刷机的固件和SDK.这里最好做一个备份,方便以后使用。
参考
钢铁雄心IV控制台命令
使用方法
按“~”键打开控制台,输入指令。
以下代码经测试对AI无效,仅对玩家有效
1 | tdebug 输入后显示鼠标停留地的国家代码以及省份代码 |
下面的虽然对AI有效,但经测试在暂停时使用对AI无效,故暂停时使用完立刻关闭可避免AI使用,关闭方法为再输一遍代码
1 | Focus.AutoComplete 所点击的国家焦点立即完成,注意如果使用时已经选择了国家焦点则无效,必须等当前国家焦点完成后使用 |
具体国家代码:
1 | AFG--阿富汗 |