0%

前言

静态交叉编译教程,本文以SS为例。


配置编译环境

安装需要软件

1
apt-get install -y build-essential autoconf autopoint automake git pkg-config

交叉编译工具

下载编译工具

以群晖为例,所有的交叉编译工具下载地址:
https://sourceforge.net/projects/dsgpl/files/
选择对应机型下载。

1
2
3
cd /mnt
wget https://ncu.dl.sourceforge.net/project/dsgpl/DSM%206.1%20Tool%20Chains/Intel%20x86%20Linux%203.10.102%20%28Broadwell%29/broadwell-gcc493_glibc220_linaro_x86_64-GPL.txz
tar Jxvf broadwell-gcc493_glibc220_linaro_x86_64-GPL.txz

自制编译工具

未完成。。。。。。

调整编译环境

自定义参数:
MLIB编译的目标处理器架构;
TOOLCHAINDIR交叉编译工具所在目录;
TOOLCHAIN交叉编译工具bin目录所在目录;
HOST交叉编译工具名称。

1
2
3
4
5
6
7
8
MLIB=x86_64
TOOLCHAINDIR=/mnt
TOOLCHAIN=$TOOLCHAINDIR/x86_64-pc-linux-gnu
HOST=${MLIB}-pc-linux-gnu

PATH_A=$PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
PATH=$TOOLCHAIN/bin:$PATH

完成后运行$HOST-gcc -v;返回版本信息说明交叉编译工具配置正常。

创建编译目录

1
2
3
4
5
6
7
8
9
10
11
12
rm -rf /mnt/build/shadowsocks-$MLIB
mkdir -p /mnt/build/shadowsocks-$MLIB && cd /mnt/build/shadowsocks-$MLIB

BASE=`pwd`
SRC=$BASE/src
PREFIX=$BASE/local
PRE=$BASE/lib

mkdir -p $SRC $PRE $PREFIX

PKG_CONFIG_PATH=$PRE/lib/pkgconfig/
LD_LIBRARY_PATH=$PRE/lib/

编译依赖库文件

  1. mbedTLS
  1. pcre
  1. libsodium
  1. libev
  1. libudns
  1. c-ares

注意:软件所需要的依赖库,请查看软件的文档得知。

mbedTLS

1
2
3
4
5
6
7
8
mkdir $SRC/mbedTLS && cd $SRC/mbedTLS
ver=2.5.1
wget --no-check-certificate https://tls.mbed.org/download/mbedtls-$ver-gpl.tgz

tar zxf mbedtls-$ver-gpl.tgz
cd mbedtls-$ver

CC=$HOST-gcc AR=$HOST-ar LD=$HOST-ld LDFLAGS=-static make DESTDIR=$PREFIX install

pcre

1
2
3
4
5
6
7
8
9
mkdir $SRC/pcre && cd $SRC/pcre
ver=8.41
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-$ver.tar.gz

tar zxf pcre-$ver.tar.gz
cd pcre-$ver

./configure --host=$HOST --prefix=$PREFIX --disable-shared --enable-utf8 --enable-unicode-properties
make -j`nproc` && make install

libsodium

1
2
3
4
5
6
7
8
9
10
mkdir $SRC/libsodium && cd $SRC/libsodium
ver=1.0.13
wget --no-check-certificate https://download.libsodium.org/libsodium/releases/libsodium-$ver.tar.gz

tar zxf libsodium-$ver.tar.gz
cd libsodium-$ver

./configure --host=$HOST --prefix=$PREFIX --disable-ssp --disable-shared

make -j`nproc` && make install

libev

1
2
3
4
5
6
7
8
9
mkdir $SRC/libev && cd $SRC/libev
ver=4.24
wget http://dist.schmorp.de/libev/libev-$ver.tar.gz

tar zxf libev-$ver.tar.gz
cd libev-$ver

./configure --host=$HOST --prefix=$PREFIX --disable-shared
make -j`nproc` && make install

libudns

1
2
3
4
5
6
7
8
mkdir $SRC/libudns && cd $SRC/libudns
git clone git://github.com/shadowsocks/libudns
cd libudns

./autogen.sh
./configure --host=$HOST --prefix=$PREFIX

make -j`nproc` && make install

c-ares

1
2
3
4
5
6
7
8
9
10
11
12
mkdir $SRC/c-ares && cd $SRC/c-ares
ver=1.13.0
wget https://c-ares.haxx.se/download/c-ares-$ver.tar.gz

tar zxvf c-ares-$ver.tar.gz
cd c-ares-$ver

sed -i 's#\[-\]#[1.13.0]#' configure.ac
./buildconf
./configure --prefix=$PREFIX --host=$HOST CC=$HOST-gcc --enable-shared=no --enable-static=yes

make -j`nproc` && make install

编译软件

  1. shadowsocks-libev
  1. simple-obfs

shadowsocks-libev

1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir $SRC/shadowsocks-libev && cd $SRC/shadowsocks-libev
ver=3.1.0
git clone git://github.com/shadowsocks/shadowsocks-libev
cd shadowsocks-libev
git checkout v$ver -b v$ver
git submodule init && git submodule update

./autogen.sh
LIBS="-lpthread -lm" LDFLAGS="-Wl,-static -static -static-libgcc -L$PREFIX/lib" CFLAGS="-I$PREFIX/include" \
./configure --host=$HOST --prefix=$PREFIX --disable-ssp --disable-documentation \
--with-mbedtls=$PREFIX --with-pcre=$PREFIX --with-sodium=$PREFIX

make -j`nproc` && make install

simple-obfs

1
2
3
4
5
6
7
8
9
10
11
12
mkdir $SRC/simple-obfs && cd $SRC/simple-obfs
ver=0.0.3
git clone https://github.com/shadowsocks/simple-obfs
cd simple-obfs
git checkout v$ver -b v$ver
git submodule init && git submodule update

./autogen.sh
LIBS="-lpthread -lm" LDFLAGS="-Wl,-static -static -static-libgcc -L$PREFIX/lib" CFLAGS="-I$PREFIX/include" \
./configure --host=$HOST --prefix=$PREFIX --disable-ssp --disable-documentation

make -j`nproc` && make install

去除符号表

1
2
3
4
5
cp $PREFIX/bin/ss-* $BASE
cp $PREFIX/bin/obfs-* $BASE
$HOST-strip $BASE/obfs-*
$HOST-strip $BASE/ss-*
cd $BASE

还原环境变量

1
2
3
4
PATH=$PATH_A

echo
echo "Done!"

什么是 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
2
$ ls /home/git/gogs/
custom data gogs LICENSE log public README.md README_ZH.md scripts templates

运行安装

首先建立好数据库。

1
2
3
4
5
在安装完mysql之后,我们还需要创建一个数据库,将之取名为gogs,

CREATE DATABASE gogs CHARACTER SET utf8 COLLATE utf8_bin;
GRANT ALL PRIVILEGES ON gogs.* TO ‘root’@‘localhost’;
FLUSH PRIVILEGES;

在 Gogs 目录的 scripts/mysql.sql 文件是数据库初始化文件。执行 mysql -u root -p < scripts/mysql.sql (需要输入密码)即可初始化好数据库。

然后登录 MySQL 创建一个新用户 gogs,并将数据库 gogs 的所有权限都赋予该用户。

1
2
3
4
5
6
$ mysql -u root -p
> # (输入密码)
> create user 'gogs'@'localhost' identified by '密码';
> grant all privileges on gogs.* to 'gogs'@'localhost';
> flush privileges;
> exit;

运行 gogs web 把 Gogs 运行起来,然后访问 http://服务器IP:3000/ 来进行安装,填写好表单之后提交就可以了。

需要注意的是, 0.6.9.0903 Beta 版本有个 bug,允许在关闭注册的情况下不添加管理员,这样安装完成之后将没有任何用户可以登录。所以请务必在安装界面指定一个管理员帐号。

配置调整

配置文件位于 Gogs 目录的 custom/conf/app.ini ,是 INI 格式的文本文件。详细的配置解释和默认值请参考 官方文档 ,其中关键的配置大概是下面这些。

1
2
3
4
5
6
7
8
9
10
RUN_USER 默认是 git ,指定 Gogs 以哪个用户运行
ROOT 所有仓库的存储根路径
PROTOCOL 如果你使用 nginx 反代的话请使用 http ,如果直接裸跑对外服务的话随意
DOMAIN 域名。会影响 SSH clone 地址
ROOT_URL 完整的根路径,会影响访问时页面上链接的指向,以及 HTTP clone 的地址
HTTP_ADDR 监听地址,使用 nginx 的话建议 127.0.0.1 ,否则 0.0.0.0 也可以
HTTP_PORT 监听端口,默认 3000
INSTALL_LOCK 锁定安装页面
Mailer 相关的选项
其中,Mailer 可以使用 Mailgun 的免费邮件发送服务,将 Mailgun 的 SMTP 配置填入到配置中就好。

nginx 反代

/etc/nginx/sites-available 中新建一个文件,把以下内容写入文件中。

1
2
3
4
5
6
7
8
9
server {
server_name 域名或IP;
listen 80; # 或者 443,如果你使用 HTTPS 的话
# ssl on; 是否启用加密连接
# 如果你使用 HTTPS,还需要填写 ssl_certificate 和 ssl_certificate_key
location / { # 如果你希望通过子路径访问,此处修改为子路径,注意以 / 开头并以 / 结束
proxy_pass http://127.0.0.1:3000/;
}
}

然后进入 /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是一款功能强大 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
2
3
4
5
6
7
vim /usr/local/nginx/conf/vhost/your_domain.conf

将index值修改为
index _h5ai/public/index.php;

vim /usr/local/php/etc/php.ini
搜索 scandir、exec、passthru,将其从被禁用的函数中删除。

配置虚拟机空间

去到官网下载h5ai,解压到虚拟机空间目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── _h5ai
│ ├── CHANGELOG.md
│ ├── private
│ ├── public
│ └── README.md
├── 您要显示的文件夹
│ ├── 子文件夹1
│ ├── 文件1
│ └── 文件2
└── 您要显示的文件夹
├── 文件1
└── 文件2

重启Web服务

LNMP重启命令lnmp restart

1
2
3
4
5
6
7
8
9
10
11
12
13
+-------------------------------------------+
| Manager for LNMP, Written by Licess |
+-------------------------------------------+
| http://lnmp.org |
+-------------------------------------------+
Stoping LNMP...
Stoping nginx... done
[ ok ] Stopping mariadb (via systemctl): mariadb.service.
Gracefully shutting down php-fpm . done
Starting LNMP...
Starting nginx... done
[ ok ] Starting mariadb (via systemctl): mariadb.service.
Starting php-fpm done

参考

云梦小站

警告:切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker.

准备工作

系统要求

Docker 支持 64 位版本 CentOS 7/8,并且要求内核版本不低于 3.10。 CentOS 7 满足最低内核的要求,但由于内核版本比较低,部分功能(如 overlay2 存储层驱动)无法使用,并且部分功能可能不太稳定。

卸载旧版本

旧版本的 Docker 称为 docker 或者 docker-engine,使用以下命令卸载旧版本:

1
2
3
4
5
6
7
8
9
10
$ sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

使用 yum 安装

执行以下命令安装依赖包:

1
$ sudo yum install -y yum-utils

鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
执行下面的命令添加 yum 软件源:

1
2
3
4
5
6
7
8
$ sudo yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
$ sudo sed -i 's/download.docker.com/mirrors.aliyun.com\\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
\# 官方源
\# $ sudo yum-config-manager \
\# --add-repo \
\# https://download.docker.com/linux/centos/docker-ce.repo

如果需要测试版本的 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
2
\# FirewallBackend=nftables
FirewallBackend=iptables

或者执行如下命令:

1
2
$ firewall-cmd --permanent --zone=trusted --add-interface=docker0
$ firewall-cmd --reload

使用脚本自动安装

在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS 系统上可以使用这套脚本安装,另外可以通过 --mirror 选项使用国内源进行安装:

若你想安装测试版的 Docker, 请从 test.docker.com 获取脚本

1
2
3
4
\# $ curl -fsSL test.docker.com -o get-docker.sh
$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
\# $ sudo sh get-docker.sh --mirror AzureChinaCloud

执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 的稳定(stable)版本安装在系统中。

启动 Docker

1
2
$ sudo systemctl enable docker
$ sudo systemctl start 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
2
3
4
5
6
7
8
9
10
11
12
$ docker run --rm hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
...

若能正常输出以上信息,则说明安装成功。

镜像加速

如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以配置 Docker 国内镜像加速

添加内核参数

如果在 CentOS 使用 Docker 看到下面的这些警告信息:

1
2
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

请添加内核配置参数以启用这些功能。

1
2
3
4
$ sudo  tee -a /etc/sysctl.conf <<-EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

然后重新加载 sysctl.conf 即可

1
$ sudo sysctl -p

本文转载自Coding

这篇文章解释了 Git 是如何工作的。(如果相关内容的谈话更吸引你,你可以观看链接中的视频。)

本文假设你已经对 Git 理解到了可以对你的项目进行版本控制的程度。本文专注于支撑 Git 的图结构以及这些图的性质影响 Git 行为的方式。通过了解底层,你可以将你心中对 Git 的模型建立在事实之上,而不是基于通过积累使用经验而建立的假设上。这个更真实的模型可以让你更好的理解 Git 做了什么,正在做什么以及将要做什么。

本文由一系列针对单个项目的 Git 命令构成。时不时的,还将有一些对于 Git 所建立的图数据结构的观察。这些观察阐述了图的性质和相应性质所产生的影响。

读完本文后,如果你希望更深入的了解 Git,可以阅读我关于 Git 的 JavaScript 实现gitlet.js(heavily annotated source code)

创建项目

1
2
~ $ mkdir alpha
~ $ cd alpha

用户为项目建立一个名为 alpha 的目录。

1
2
~/alpha $ mkdir data
~/alpha $ printf 'a' > data/letter.txt

进入目录 alpha,并在下面建立名为 data 的目录。在这个目录中建立一个名为 letter.txt 的文件,其中包含一个字符 a。此时目录结构看起来像这样:

1
2
3
alpha
└── data
└── letter.txt

初始化版本库

1
2
~/alpha $ git init
Initialized empty Git repository

git init 命令使得当前目录成为一个 Git 版本库。为此这条命令建立了一个名为的 .git 目录并且向其中写入了一些文件。这些文件定义和记录了关于Git配置和项目历史的所有相关内容。它们只是普通的文件,其中并没有什么类似魔法的神奇之处。用户可以使用文本编辑器和命令行阅读或编辑这些文件。也就是说:用户可以像获取和修改项目文件一样简单地获取和修改项目历史。

这时候目录 alpha 的结构看起来像这样:

1
2
3
4
5
6
alpha
├── data
| └── letter.txt
└── .git
├── objects
etc...

.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
2
3
4
alpha
└── data
└── letter.txt
└── number.txt

用户将这个文件添加到 Git 中。

1
~/alpha $ git add data

命令 git add 创建一个包含 data/number.txt 内容的BLOB文件。同时添加一条文件 data/number.txt 的索引项,指向对应的BLOB文件。在 git add 命令第二次被执行之后索引文件如下:

1
2
data/letter.txt 2e65efe2a145dda7ee51d1741299f848e5bf752e
data/number.txt 274c0052dd5408f8ae2bc8440029ff67d79bc5c3

注意到即便用户运行 git add data,也只有 data 目录中的文件在索引文件中被列出。文件夹 data 没有被单独列出。

1
2
~/alpha $ printf '1' > data/number.txt
~/alpha $ git add data

当用户最初建立 data/number.txt 时,他想要写入 1,而不是 1234。用户做了更改并且将文件添加到索引中。这次的命令创建了一个新的包含了新内容的BLOB文件。并且更新了文件 data/number.txt 的索引项指向新的BLOB文件。

进行一次提交

1
2
~/alpha $ git commit -m 'a1'
[master (root-commit) 774b54a] a1

用户做提交 a1。Git 显示出一些有关此次提交的数据。很快我们将看懂这些信息。

提交命令分三步执行。首先,命令建立了一个树图来表示被提交的项目版本的内容。其次,建立一个提交对象。最后,将当前分支指向新的提交对象。

创建一个树图

Git 通过从索引建立一张树图来记录项目的当前状态。这张树图记录了项目中每一个文件的位置和内容。

图由两种对象组成:BLOB 文件和树。

BLOB 文件由 git add 存储。它们表示了文件的内容。

树是在提交被进行时被存储的。树表示了工作副本中的目录。

下面就是记录新提交中 data 目录内容的树对象:

1
2
100664 blob 2e65efe2a145dda7ee51d1741299f848e5bf752e letter.txt
100664 blob 56a6051ca2b02b04ef92d5150c9ef600403cb1de number.txt

第一行记录了再现 data/letter.txt 文件所需的一切内容。第一部分表明文件权限。第二部分表明此项内容由BLOB文件表示而不是一个树对象。第三个部分表明对应BLOB的散列值。第四部分表明文件名。

第二行记录了关于 data/number.txt 文件的相同内容。

下面是代表 alpha 的树对象,即项目的根目录:

1
040000 tree 0eed1217a2947f4930583229987d90fe5e8e0b74 data

树中只有一行并指向 data 树。

Tree graph for the `a1` commit
a1 提交的树图

在上面的图中,root 树指向 data 树。data 树指向 data/letter.txtdata/number.txt 对应的BLOB文件。

创建一个提交对象

git commit 在创建树图之后创建一个提交对象。提交对象是 .git/objects/ 目录中另一个文本文件:

1
2
3
4
5
tree ffe298c3ce8bb07326f888907996eaa48d266db4
author Mary Rose Cook <mary@maryrosecook.com> 1424798436 -0500
committer Mary Rose Cook <mary@maryrosecook.com> 1424798436 -0500

a1

第一行指向树图。散列值对应于代表工作副本根目录的树图,这里是 alpha 目录。最后一行是提交信息。

`a1` commit object pointing at its tree graph
a1 提交对象指向它的树图

将当前分支指向新提交

最后,提交命令将当前分支指向新的提交对象。

哪一个分支是当前分支?Git 在 .git/HEAD 目录中的 HEAD 文件中寻找相关信息:

1
ref: refs/heads/master

这代表 HEAD 指向 mastermaster 分支是当前分支。

HEADmaster 都是引用。引用是 Git 或用户用来标识特定分支的标签。

代表 master 分支的引用并不存在,因为这是版本库的第一次提交。Git 在路径 .git/refs/heads/master 创建文件并且将内容写为提交对象的散列值:

1
74ac3ad9cde0b265d2b4f1c778b283a6e2ffbafd

(如果你在 Git 中输入你所读到的命令,a1 提交的散列值将与我这里的不同。内容对象例如 BLOB 文件和树总是散列到与本文相同的值上。提交对象并不如此,因为其中包含日期和创建者的名字。)

让我们将 HEADmaster 添加到 Git 图中:

`master` pointing at the `a1` commit
HEAD 指向 master 并且 master 指向 a1 提交

HEAD 指向 master,如提交之前一样。但是现在 master 开始存在并且指向新的提交对象。

创建一个非首次提交

下面是 a1 提交之后的 Git 图。其中包括了工作拷贝和索引。

包含工作拷贝和索引的 `a1` 提交
包含工作拷贝和索引的 a1 提交

注意,工作拷贝,索引以及 a1 提交中的 data/letter.txtdata/number.txt 文件内容是一样的。索引和 HEAD 提交的散列值都指向的都是 BLOB 对象,但是工作拷贝的内容是作文文本文件存放在不同的地方的。

1
~/alpha $ printf '2' > data/number.txt

用户将 data/number.txt 的内容设置为 2。该操作更新了工作拷贝,但是没有改变 HEAD 提交以及索引。

工作拷贝中的 `data/number.txt` 设置为 2
工作拷贝中的 data/number.txt 设置为 2

1
~/alpha $ git add data/number.txt

用户将文件添加到 Git。该操作在 object 目录中创建了一个内容为 2 的 BLOB 文件。在新的 BLOB 文件中添加了一条指向 data/number.txt 的索引项。

工作拷贝和索引中的 `data/number.txt` 设置为 2
工作拷贝和索引中的 data/number.txt 设置为 2

1
2
~/alpha $ git commit -m 'a2'
[master f0af7e6] a2

用户提交。步骤同上。

第一步,创建了一个代表索引内容的树图。

data/number 的索引项发生了改变。旧的 data 树不能再反映当前的 data 目录的索引状态。所以必须创建一个一个新的 data 树:

1
2
100664 blob 2e65efe2a145dda7ee51d1741299f848e5bf752e letter.txt
100664 blob d8263ee9860594d2806b0dfd1bfd17528b0ba2a4 number.txt

新的 data 树与旧的 data 树散列值不同。必须创建一个新的 root 树来记录当前散列值:

1
040000 tree 40b0318811470aaacc577485777d7a6780e51f0b data

第二步,创建一个新的提交对象。

1
2
3
4
5
6
tree ce72afb5ff229a39f6cce47b00d1b0ed60fe3556
parent 774b54a193d6cfdd081e581a007d2e11f784b9fe
author Mary Rose Cook <mary@maryrosecook.com> 1424813101 -0500
committer Mary Rose Cook <mary@maryrosecook.com> 1424813101 -0500

a2

提交对象的第一行指向了新的 tree 对象。第二行指向 a1 提交:当前提交的父提交。要找到父提交,Git 首先找到头指针 HEAD,然后顺着找到 master 然后获得 a1 提交的散列值。

第三步,将 master 分支文件的内容设置为新提交的散列值。

`a2` 提交
a2 提交

不包括工作拷贝和索引的 Git 图
不包括工作拷贝和索引的 Git 图

图属性: 内容被储存为一个树对象。这表明只有差异被储存在对象数据库中。从上图可以看出。a2 提交重复使用了在 a1 提交之前创建的 a BLOB 文件。类似的,如果整个工作目录的内容在一次次提交中没有发生改变,树对象以及所有的 BLOB 文件都能够被重复使用。通常来说,各次提交之间只有很小的改动。这也就意味着 Git 能够使用很小的空间来储存大量的提交历史。

图属性:每个提交都有一个父提交,也就是说仓库可以储存项目的历史改动。

图属性:引用(refrences 或者 refs,译者注)是指向一部分提交历史或者其他的条目。也就是说可以给提交取一个有意义的名字。用户将它们的工作组织成单行的固定短语,比如 fix-for-bug-376。Git 使用了一些像 HEADMERGE_HEADFETCH_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
2
~/alpha $ git checkout 37888c2
You are in 'detached HEAD' state...

用户通过对应的散列值来检出 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` 提交
分离头指针到 a2 提交

1
2
3
4
~/alpha $ printf '3' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m 'a3'
[detached HEAD 3645a0e] a3

用户将 data/number.txt 的值设置为 3 然后提交更改。Git 找到 HEAD 然后找到 a3 提交的父提交。这回返回 a2 提交的散列值而不是查找一个分支引用。

Git 将 HEAD 更新,使其直接指向 a3 提交的哈希值。此时仓库仍然处于头指针分离状态,而没有在一个分支上,因为没有引用指向 a3 提交亦或是它之后的提交。这意味着它很容易丢失。

从现在起,图示中大多的树和 BLOB 都会省略。

没有在分支上的 `a3` 提交
没有在分支上的 a3 提交

创建一个分支

1
~/alpha $ git branch deputy

用户创建了一个新的叫做 deputy 的分支。这个操作在 .git/refs/heads/deputy 目录下创建了新文件,其包含了 HEAD 指向的散列值:a3 提交的散列值。

图属性:分支其实就是引用,而引用其实就是文件。这也就是说 Git 的分支是很轻量的。

创建 deputy 分支的操作实际上将新的 a3 提交安全地放在了一个新的分支上。HEAD 指针目前仍处于分离状态,因为它现在仍是直接指向了一个提交。

处于 `deputy` 分支的 `a3` 提交
处于 deputy 分支的 a3 提交

检出分支

1
2
3
4
~/alpha $ git checkout master
Switched to branch 'master'
---
检出'master'分支

用户检出了’master’分支

首先, Git 找到’master’分支所指向的a2提交对象并获取该提交对象所指向的树对象.

接下来 Git 会将树对象储存的文件写到当前工作副本中, 该操作将覆写data/number.txt2.

第三步, Git 将树对象中的文件入口写入 index, data/number.txt的文件入口将会被更新为2 blob 的 hash 值

最后 Git 通过将HEAD中的 hash 值替换为如下内容来使HEAD指向master分支:

1
ref: refs/heads/master

`master`分支被检出, 指向'a2'提交
master分支被检出, 指向’a2’提交

检出与当前工作副本相冲突的分支

1
2
3
4
5
6
7
8
9
10
11
~/alpha $ printf '789' > data/number.txt
~/alpha $ git checkout deputy
Your changes to these files would be overwritten
by checkout:
data/number.txt
Commit your changes or stash them before you
switch branches.
---
对以下文件做出的更改将在检出中被覆盖:
data/number.txt
请在检出前将这些更改提交或储藏.

用户无意中将data/number.txt的内容更改为789, 此时他尝试检出deputy分支, Git 没有执行这次检出.

当前HEAD指向 master分支, 其所指向提交a2data/number.txt的内容为2. deputy分支指向的提交a3data/number.txt的内容为3. 当前工作副本中data/number.txt的内容为789. 这个文件的各个版本各不相同, 必须通过一种方法来消除这些差异.

如果 Git 将当前版本的data/number.txt替换成将要检出的分支中的版本则会造成数据遗失, Git 不允许这种情况发生.

如果 Git 将要检出的分支与当前工作副本合并则会导致冲突

因此 Git 中断了此次检出.

1
2
3
~/alpha $ printf '2' > data/number.txt
~/alpha $ git checkout deputy
Switched to branch 'deputy'

用户注意到了对data/number.txt的意外操作, 在把它的内容修改回2后成功检出了deputy分支.

`deputy` 分支被检出
deputy 分支被检出

合并一个祖先分支

1
2
~/alpha $ git merge master
Already up-to-date.

用户将master分支合并到deputy中. 合并两个分支其本质为合并两个提交对象. 其一-作为接受者(receiver)-是deputy所指向的提交对象, 其二-作为给予者(giver)-是master所指向的提交对象. 在这次合并中 Git 不会执行任何操作, 而仅仅打印出Already up-to-date..

[图属性]: 图示中的一系列提交对象可以被认为是对仓库内容进行的一些列更改. 因此在一次合并当中, 若想要并入的分支是当前分支的祖先分支, Git不会执行任何操作, 因为想要并入分支中的改动已经被包含在了当前分支中.

合并后代提交

1
2
~/alpha $ git checkout master
Switched to branch 'master'

用户检出了 master 分支.

`master` checked out and pointing at the `a2` commit
master 被检出并指向了 a2 提交

1
2
~/alpha $ git merge deputy
Fast-forward

他们合并 deputy 分支到 master。Git 发现赠予提交的 a2 是源提交 a3 的祖先提交。这里可以执行 fast-forward 合并。

Git 获取赠予提交和它指向的树图,将树图中的文件写入工作区和 index。这将 master 分支 fast-forward 到了 a3 提交。

`a3` commit from `deputy` fast-forward merged into `master`
deputy 分支的 a3 提交 fast-forward 合并到了 master 分支

图属性:图中的提交系列被视为对仓库内容的一系列更改。这意味着,如果赠予提交是接收提交的后代提交,提交历史不会变。已经存在一序列提交来描述赠予提交和接收提交之间的变化。但是尽管 Git 历史不变,Git 的状态图是会改变的。HEAD 指向的具体引用会更新以指向赠予提交。

合并不同提交线的两个提交

1
2
3
4
~/alpha $ printf '4' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m 'a4'
[master 7b7bd9a] a4

用户将文件 number.txt 的内容改为 4 然后提交到 master.

1
2
3
4
5
6
~/alpha $ git checkout deputy
Switched to branch 'deputy'
~/alpha $ printf 'b' > data/letter.txt
~/alpha $ git add data/letter.txt
~/alpha $ git commit -m 'b3'
[deputy 982dffb] b3

用户检出 deputy 分支,将文件 data/letter.txt 内容改为 b 然后提交到 deputy 分支。

`a4` committed to `master`, `b3` committed to `deputy` and `deputy` checked out
a4 提交到 master, b3 提交到 deputydeputy 被检出

图属性: 多个提交可以共用一个父提交,这意味着新提交线可以在提交历史里创建出来。

图属性: 某提交可以有多个父提交,这意味着两个不同的提交线可以被一个合并提交来合并。

1
2
~/alpha $ git merge master -m 'b4'
Merge made by the 'recursive' strategy.

用户合并 masterdeputy.

Git 发现接收提交 b3和赠予提交 a4在不同的提交线上。它创建了一个合并提交。这个过程总共分八步。

第一步,Git 将接收提交的哈希值写入文件 alpha/.git/MERGE_HEAD。此文件的存在说明 Git 正在做合并操作。

第二步,Git 查找基提交:即接收提交和赠予提交共有的一个最近父提交。

`a3`, the base commit of `a4` and `b3`
a3a4b3 的基提交

图属性:每个提交都有一个父提交。这意味着我们可以发现两个提交线分开自哪个提交。Git 查找 b3a4 的所有祖先提交,发现了最近的公共父提交 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 内容被修改为bdata/number.txt 内容被修改为 4

第六步,Git 将差异中的项应用到 index。data/letter.txt 会指向内容为 b 的 blob,data/number.txt 会指向内容为 4 的 blob。

第七步,更新后的 index 被提交:

1
2
3
4
5
6
7
tree 20294508aea3fb6f05fcc49adaecc2e6d60f7e7d
parent 982dffb20f8d6a25a8554cc8d765fb9f3ff1333b
parent 7b7bd9a5253f47360d5787095afc5ba56591bfe7
author Mary Rose Cook <mary@maryrosecook.com> 1425596551 -0500
committer Mary Rose Cook <mary@maryrosecook.com> 1425596551 -0500

b4

注意,这个提交有两个父提交。

第八步,Git 将当前分支 deputy 指向新提交。

`b4`, the merge commit resulting from the recursive merge of `a4` into `b3`
a4递归合并入 b3 产生 b4

合并不同提交线且有相同修改文件的两个提交

1
2
3
4
~/alpha $ git checkout master
Switched to branch 'master'
~/alpha $ git merge deputy
Fast-forward

用户检出 master,他们将 deputy 合并到 master。此操作将master fast-forwards 指向 b4masterdeputy 现指向了相同的提交。

`deputy` merged into `master` to bring `master` up to the latest commit, `b4`
deputy 合并到 mastermaster 更新到新提交 b4

1
2
3
4
5
6
~/alpha $ git checkout deputy
Switched to branch 'deputy'
~/alpha $ printf '5' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m 'b5'
[deputy bd797c2] b5

用户检出 deputy。将 data/number.txt 内容修改为 5并提交到 deputy 分支。

1
2
3
4
5
6
~/alpha $ git checkout master
Switched to branch 'master'
~/alpha $ printf '6' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m 'b6'
[master 4c3ce18] b6

用户检出 master。将 data/number.txt 内容修改为 6 并提交到 master 分支。

`b5` commit on `deputy` and `b6` commit on `master`
b5 提交在 deputy b6 提交在 master

1
2
3
4
~/alpha $ git merge deputy
CONFLICT in data/number.txt
Automatic merge failed; fix conflicts and
commit the result.

用户将 deputy 合并到 master。这里存在冲突故合并中止。对于有冲突的合并操作,执行步骤和没有冲突的合并的前六步是相同的:设置 .git/MERGE_HEAD,查找基提交,创建基提交、接收提交和赠予提交的索引,生成 diff,更新工作区,更新 index。由于冲突,第七步提交和第八步更新 ref 不再执行。让我们再来看看这些步骤,观察到底发生了什么。

第一步,Git 将赠予提交的哈希值写入 .git/MERGE_HEAD.

`MERGE_HEAD` written during merge of `b5` into `b6`
MERGE_HEAD 写入在 b5 合并入 b6

第二步,Git 查找到基提交, b4.

第三步,Git 创建基提交、接收提交和赠予提交的索引。

第四步,Git 生成集合了接收提交和赠予提交相对于基提交的差异列表,这个 diff 是一份指向变更的文件路径:添加、删除、修改或冲突。

在本例中,差异列表仅包含一项: data/number.txt。它的状态被标为冲突因为其内容在接收提交,赠予提交和基提交中都是变化的。

第五步,差异列表中的文件被写入工作区。对于冲突的部分,Git 将两个版本都写入工作区的文件中。data/number.txt 的内容被变更为:

1
2
3
4
5
<<<<<<< HEAD
6
=======
5
>>>>>>> deputy

第六步,差异列表中的文件被写入 index。index 中的项被文件路径和 stage 的组合唯一标识。没有冲突的项 stage 为 0。在该合并前,index 看起来像下面的样子,标有 0 的是 stage 值:

1
2
0 data/letter.txt 63d8dbd40c23542e740659a7168a0ce3138ea748
0 data/number.txt 62f9457511f879886bb7728c986fe10b0ece6bcb

在合并 diff 写入 index 后,index 变成:

1
2
3
4
0 data/letter.txt 63d8dbd40c23542e740659a7168a0ce3138ea748
1 data/number.txt bf0d87ab1b2b0ec1a11a3973d2845b42413d9767
2 data/number.txt 62f9457511f879886bb7728c986fe10b0ece6bcb
3 data/number.txt 7813681f5b41c028345ca62a2be376bae70b7f61

stage 0data/letter.txt 项和合并前是一样的。stage 0data/number.txt 项已经不存在,取代的是三个新项。stage 1 的项包含该文件在基提交中内容的哈希值,stage 2 包含接收提交的哈希值,stage 3包含赠予提交的哈希值。这三项表明文件 data/number.txt 存在冲突。

合并中止了。

1
2
~/alpha $ printf '11' > data/number.txt
~/alpha $ git add data/number.txt

用户通过将 data/number.txt 的内容修改为 11将两个有冲突的文件合并,将文件添加到 index,Git 创建一个包含11 的 blob,创建一个冲突文件以告诉 Git 冲突已经解决了。Git 移除 index 中的 1, 23,并添加 stage 为 0data/number.txt 项,该项指向新创建 blob 的哈希值。现在 index 变为:

1
2
0 data/letter.txt 63d8dbd40c23542e740659a7168a0ce3138ea748
0 data/number.txt 9d607966b721abde8931ddd052181fae905db503
1
2
~/alpha $ git commit -m 'b11'
[master 251a513] b11

第七步,用户进行提交。Git 发现存在 .git/MERGE_HEAD,意味着合并还在进行中。通过检查 index 发现没有冲突。它创建了一个新提交 b11,用来记录合并后的内容。然后删除 .git/MERGE_HEAD。合并完成。

第八步,Git 将当前分支 master 指向新提交。

`b11`, the merge commit resulting from the conflicted, recursive merge of `b5` into `b6`
b5b6递归合并为b11

删除文件

这幅 Git 示意图包含对于最后一次提交的历史提交、树对象、储存对象、工作副本以及索引。

The working copy, index, `b11` commit and its tree graph
工作副本、索引, b11 提交以及它的树对象。

1
2
~/alpha $ git rm data/letter.txt
rm 'data/letter.txt'

该用户告诉 Git 删除 data/letter.txt。 文件将从工作副本中删除,该文件的入口也将从索引中消失。

After `data/letter.txt` `rm`ed from working copy and index
After data/letter.txt rmed from working copy and index
data/letter.txt 从工作副本以及索引中 rm 过后

1
2
~/alpha $ git commit -m '11'
[master d14c7d2] 11

该用户进行提交了。 一般说来,Git 会在提交时构建一张树图代表索引内容,data/letter.txt 并不会出现在树中,因为它不在索引里。

`11` commit made after `data/letter.txt` `rm`ed
data/letter.txt 删除后进行 11 提交

复制一个库

1
2
~/alpha $ cd ..
~ $ cp -R alpha bravo

用户复制 alpha/ 库中的内容到 bravo/ 目录中,使得目录结构如下:

1
2
3
4
5
6
7
~
├── alpha
| └── data
| └── number.txt
└── bravo
└── data
└── number.txt

bravo 目录即生成另一张 Git 图:

New graph created when `alpha` `cp`ed to `bravo`
alpha cpbravo 后产生的新图

链接至另一个库

1
2
      ~ $ cd alpha
~/alpha $ git remote add bravo ../bravo

用户回到 alpha 库中,设置 bravo 为一个 alpha 的远程库,这会使 alpha/.git/config 多了这几行:

1
2
[remote "bravo"]
url = ../bravo/

这几行说明有个叫 bravo 的远程库在 ../bravo 中。

获取远程分支

1
2
3
4
5
~/alpha $ cd ../bravo
~/bravo $ printf '12' > data/number.txt
~/bravo $ git add data/number.txt
~/bravo $ git commit -m '12'
[master 94cd04d] 12

进入 bravo 目录. 覆写 文件 data/number.txt12 ,将改动提交到bravo仓库的分支 master.

`12` commit on `bravo` repository

bravo 仓库中的一个提交12

1
2
3
4
5
~/bravo $ cd ../alpha
~/alpha $ git fetch bravo master
Unpacking objects: 100%
From ../bravo
* branch master -> FETCH_HEAD

进入 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 这个提交。

`alpha` after `bravo/master` fetched

bravo/master 获取后的alpha仓库状态

图属性: 对象可以被复制。以为址不同仓库间可以共享对象。

图属性:一个仓库可以存储远程分支的引用,例如 alpha/.git/refs/remotes/bravo/master 。意味着,仓库可以在本地记录远程分支的状态。如果远程分支没有改变,这个状态就一直是正确的。

合并分支 FETCH_HEAD

1
2
3
~/alpha $ git merge FETCH_HEAD
Updating d14c7d2..94cd04d
Fast-forward

用户合并了分支FETCH_HEAD,这个分支也是一个指向某个提交的一个引用,被解析为指向 12 这个提交的一个引用。

合并前,HEAD 指向此次被合并的引用, 11 这个提交。 完成 fast-forward 合并后, HEAD 指向提交 12

`alpha` after `FETCH_HEAD` merged

alpha仓库合并FETCH_HEAD后的状态

拉取远程仓库

1
2
~/alpha $ git pull bravo master
Already up-to-date.

拉取远程仓库 bravomaster 分支到本地仓库 alphapull 操作是 “获取然后合并FETCH_HEAD “ 这两个命令的一个快捷方式。执行了这个命令后,反馈 Already up-to-date, 说明本地和远程内容一样。

克隆一个仓库

1
2
3
~/alpha $ cd ..
~ $ git clone alpha charlie
Cloning into 'charlie'

切换到 alpha 的上级目录,克隆仓库alpha 到目录 charlie,这个操作的结果,如同通过复制到到bravo仓库。

克隆仓库做的事情包括:创建新目录charlie; 在目录charlie下初始化仓库;将 alpha 作为远程仓库的 origin分支; 获取 origin 分支到本地;合并分支 FETCH_HEAD

推送本地分支到远程仓库

1
2
3
4
5
      ~ $ cd alpha
~/alpha $ printf '13' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m '13'
[master 3238468] 13

切换回 alpha仓库, 覆写文件 data/number.txt13 ,将改动提交到alpha仓库的分支 master

1
~/alpha $ git remote add charlie ../charlie

将本地目录charlie 作为本地仓库的alpha 的一个远程仓库。

1
2
3
4
5
~/alpha $ git push charlie master
Writing objects: 100%
remote error: refusing to update checked out
branch: refs/heads/master because it will make
the index and work tree inconsistent

推送本地仓库的master 分支到远程仓库 charlie

13 这个提交所需要的所有对象,被复制到到远程仓库charlie中。

至此,推送操作完成。和之前一样,如果操作出错,Git 会提示出错内容。例如,Git 会拒绝推送到一个在远程切出的分支。听起来是不是很有道理 ?由于一次推送操作会更新远程索引和 HEAD的指向,如果这个时候有人正在编辑远程副本的时候,就会导致冲突,出现不一致。

现在,可以创建一个新分支,合并 13这个内容到新分支 ,推送到远程仓库 charlie。但是,希望达到的是可以推送任何想推送的内容到仓库,希望又有一个可以推送和拉取到中心仓库,但是没人能直接推送到此中心仓库。有点像 GitHub 远程操作,最后的解决方案就是,裸库。

克隆一个裸库

1
2
3
~/alpha $ cd ..
~ $ git clone alpha delta --bare
Cloning into bare repository 'delta'

切换到 alpha上级目录,将裸库到目录 delta 。这个克隆有两点不同。一是config文件表明这是一个裸库;二是,通常位于 .git目录下的文件被现在存放在仓库的根目录下:

1
2
3
4
5
delta
├── HEAD
├── config
├── objects
└── refs

`alpha` and `delta` graphs after `alpha` cloned to `delta`

克隆alphadelta后的仓库图

推送分支到裸库

1
2
      ~ $ cd alpha
~/alpha $ git remote add delta ../delta

切换到 alpha目录。使用 ../delta 目录 创建远程仓库delta

1
2
3
4
~/alpha $ printf '14' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m '14'
[master cb51da8] 14

覆写文件 data/number.txt14 ,将改动提交到alpha仓库的分支 master

`14` commit on `alpha`

alpha 仓库中的14 提交

1
2
3
4
~/alpha $ git push delta master
Writing objects: 100%
To ../delta
3238468..cb51da8 master -> master

推送master到仓库delat。推送过程包含三步:

第一步,14提交需要的所有对象,从alpha/.git/objects/目录,复制到目录delta/objects/

第二步,更新文件delta/refs/heads/master内容,指向提交 14

第三步,更新文件alpha/.git/refs/remotes/delta/master内容,指向提交 14。本地仓库alpha就有了远程仓库delta的一份最新状态记录。

`14` commit pushed from `alpha` to `delta`

推送alpha仓库到提交14到仓库delta

总结

Git 是在基于图的思想上构建的,几乎所有的 Git 命令都在维护这个图。想要深入理解 Git,就需要把精力集中在这个图的属性上,而不是在 Git 操作流程 或者 Git 命令。

想要更多的理解 Git,就去详细剖析 .git 目录,看里面都有些什么文件。通过改变文件内容,观察里面这些文件的变化。手动创建提交,看看可以把这个仓库搞什么鬼样子,然后尝试修复这些问题。

  1. 通过这个案例,hash 值比原始文件内容要长。但是,所有文件的内容比 hash 值要长,这样的效果就是,表达的意思比原始文件要更加简洁明了。
  2. 有可能会出现两个不同的文件内容的 hash 值一样,但是,这个 机率很小.。
  3. git prune会删除一个引用不能获取的所有对象。如果使用此命令,可能导致文件内容丢失。
  4. git stash会在一个安全的地方存储HEAD与当前工作区的差异。因此稍后可以恢复工作区。
  5. rebase可以对历史命令进行操作,达到新增,修改,删除提交的目的。

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
2
curl -sS http://packages.gitlab.cc/install/gitlab-ce/script.deb.sh | sudo bash
apt install gitlab-ce

启动GitLab

1
gitlab-ctl reconfigure

使用浏览器访问GitLab

首次访问GitLab,系统会让你重新设置管理员的密码,设置成功后会返回登录界面.
默认的管理员账号是root,如果你想更改默认管理员账号,请输入上面设置的新密码登录系统后修改帐号名.

未完待续。。。。。。

简介

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
2
3
apt update && apt upgrade -y

apt install build-essential subversion git-core libncurses5-dev zlib1g-dev gawk flex quilt libssl-dev xsltproc libxml-parser-perl mercurial bzr ecj cvs unzip

下载源码

1
2
3
4
5
6
7
git clone https://github.com/lede-project/source.git LEDE-project

cd LEDE-project

./scripts/feeds update -a

./scripts/feeds install -a

编译

编辑固件设置

1
make menuconfig

执行后出现配置界面,根据需要选择自己的配置选项。

备注:记得勾选SDK

开始编译

1
make V=s

注意:

  • 编译时间较长;
  • 编译过程中,需要爬墙下载相关软件包;

完成编译

编译完成后,便可以在/bin/$target目录下找到刷机的固件和SDK.这里最好做一个备份,方便以后使用。

参考

lede-project

使用方法

按“~”键打开控制台,输入指令。

以下代码经测试对AI无效,仅对玩家有效

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
  tdebug 输入后显示鼠标停留地的国家代码以及省份代码

  annex(空格)(国家代码) 直接吞并该国家 例:annex jap 吞并日本

  tag(空格)(国家代码) 切换所控制的国家,如果玩到一半想换一个国家玩可以使用此代码,如想玩法国输入tag fra

  delall(空格)(国家代码) 删除指定国家所有的军队和舰队

  add_core(省份代码) 添加该省份为我国核心,可作为宣战理由

  remove_core(省份代码)去除省份核心

  research complete 第一个研发槽的科技研发完成

  research all 所有科技研发完成

  manpower 增加10M人力

  XP 三军经验各加500

  PP 加999政治力量

  nu(空格)(数字)加国家凝聚力,不输数字则无效果

  threat(空格)(数字)世界紧张度增加(玩家导致),最好别用,会被所有国家仇视

  fow 去除战争迷雾

  tp 开启后部队瞬间移动,选择部队然后右键点击即可瞬间移动,可以将部队瞬移到敌国,免去了登陆和空降

下面的虽然对AI有效,但经测试在暂停时使用对AI无效,故暂停时使用完立刻关闭可避免AI使用,关闭方法为再输一遍代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  Focus.AutoComplete 所点击的国家焦点立即完成,注意如果使用时已经选择了国家焦点则无效,必须等当前国家焦点完成后使用

  instantconstruction 建筑瞬间建造,建造界面点击地点即可建造

  allowdiplo 外交无条件,可无条件宣战

  yesman 对方答应任何外交要求,如签条约加派系等

以下代码用处不大,仅供娱乐

  debug_nuking 可在任意省份丢核弹,无论你有没有核弹对方是否交战,不过这种方法的核弹丢了和没丢一样,炸不死军队

  winwars 给你所参加的战争增加最大进度,然而没啥用,对方死不投降,还是要继续打

  time 查看系统时间

  nextsong 换下一首配乐

  event(空格)(事件代码)执行该事件,由于事件代码不清楚所以用处不大

  resign 退出游戏

  fullscreen 全屏

具体国家代码:

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
AFG--阿富汗
ALB--阿尔巴尼亚
ARG--阿根廷
AST--澳大利亚
AUS--奥地利
BEL--比利时
BHU--不丹
BOL--玻利维亚
BRA--巴西
BUL--保加利亚
CAN--加拿大
CGX--桂系
CHC--中共
CHI--中华民国
CHL--智利
COL--哥伦比亚
COS--哥斯达黎加
CRO--克罗地亚
CSX--晋系
CUB--古巴
CXB--西北三马
CYN--滇系
CZE--捷克斯洛伐克
DDR--德意志共和国,简称东德
DEN--丹麦
DFR--联邦德国,简称西德
DOM--多米尼加
ECU--厄瓜多尔
EGY--埃及
ENG--英国
EST--爱沙尼亚
ETH--埃塞俄比亚
FIN--芬兰
FRA--法国
GER--纳粹德国
GRE--希腊
GUA--危地马拉
GUY--圭亚那
HAI--海地
HOL--荷兰
HON--洪都拉斯
HUN--匈牙利
ICL--冰岛
IDC--印度支那
IND--印度
INO--印度尼西亚
IRE--爱尔兰
IRQ--伊拉克
ISR--以色列
ITA--意大利
JAP--日本
JOR--约旦
KOR--韩国
LAT--拉脱维亚
LEB--黎巴嫩
LIB--利比里亚
LIT--立陶宛
LUX--卢森堡
MAN--伪满洲国
MEN--伪蒙古
MEX--墨西哥
MON--蒙古
NEP--尼泊尔
NIC--尼加拉瓜
NOR--挪威
NZL--新西兰
OMN--阿曼
PAK--巴基斯坦
PAL--巴勒斯坦
PAN--巴拿马
PAR--巴拉圭
PER--波斯(伊朗)
PHI--菲律宾
POL--波兰
POR--葡萄牙
PRK--朝鲜
PRU--秘鲁
REB--叛军
ROM--罗马尼亚
RSI--意大利共和国
SAF--南非
SAL--萨尔瓦多
SAU--沙特阿拉伯
SCH--瑞士
SIA--泰国
SIK--新疆
SLO--斯洛伐克
SOV--苏联
SPA--西班牙
SPR--西班牙共和国
SWE--瑞典
SYR--叙利亚
TAN--唐努图瓦
TIB--西藏
TUR--土耳其
URU--乌拉圭
USA--美国
VEN--委内瑞拉
VIC--维希法国
YEM--也门
YUG--南斯拉夫