之前也总结过不少的关于 git 使用的文章,但都很少提及 .git
目录,都知道在 git init
之后,git 会在目录下创建一个 .git
目录,该目录中保存着 git 的一切。昨天在 Twitter 上正好有人分享了三篇文章,今天就顺便学习一下。
通过.git 目录学习 git
— Hao Chen (@haoel) February 5, 2020
- https://t.co/pBe2jE7QXM
- https://t.co/w8kV4oAocV
- https://t.co/GqnF5FnkY2
展开 git 目录,大致是这样的:
├── HEAD
├── branches
├── config
├── description
├── hooks
│ ├── pre-commit.sample
│ ├── pre-push.sample
│ └── ...
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
其中的每一个文件和目录都是有意义的,下面就一个一个看看。首先是 HEAD 文件,这个不陌生吧,git 中一个非常重要的概念,后面展开。
conf 文件中包含着 repository 的配置,包括 remote 的地址,提交时的 email, username, 等等,所有通过 git config ..
来设置的内容都在这里保存着。如果熟悉甚至可以直接修改该文件。
被 gitweb(github 之前) 用来描述 repository 内容。
hooks,国内通常被翻译成钩子,git 中一个比较有趣的功能。可以编写一些脚本让 git 在各个阶段自动执行。这些脚本被称为 hooks, 脚本可以在 commit/rebase/pull 等等环节前后被执行。脚本的名字暗示了脚本被执行的时刻。一个比较常见的使用场景就是在 pre-push
阶段检查本地提交是否遵循了 remote 仓库的代码风格。
该文件中定义的文件不会被 git 追踪,和 .gitignore
作用相同。大部分情况下 .gitignore
就足够了,但知道 info/exclude
文件的存在也是可以的。
每一次创建一些文件,提交,git 都会压缩并将其保存到自己的数据结构中。压缩的内容会拥有一个唯一的名字,一个 hash 值,该 hash 值会保存到 object 目录中。
在浏览 object 目录之前我们要问自己一个问题,什么是一次提交 (commit)?一次提交是当前工作目录的一个快照,但又不止于此。
事实上,当使用 git 提交时,git 为了创建工作区的快照,只做了两件事:
一旦快照被创建,压缩的内容和名字都会到 object 目录中:
├── 4c
│ └── f44f1e3fe4fb7f8aa42138c324f63f5ac85828 // hash
├── 86
│ └── 550c31847e518e1927f95991c949fc14efc711 // hash
├── e6
│ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 // hash
├── info // let's ignore that
└── pack // let's ignore that too
上面的内容是新建了一个空文件 file__1.txt
并提交后 object 目录的结构。需要注意的是,如果 hash 是 4cf44f1e...
,那么 git 会将内容保存到 4c
子目录中,然后将文件命名为 f44f1...
。这样就使得 object 目录缩小了,最多只会有 00-ff 这些目录。
commit 由四部份组成:
如果解压一个 commit 文件,可以查看到这些内容。
// by looking at the history you can easily find your commit hash
// you also don't have to paste the whole hash, only enough
// characters to make the hash unique
git cat-file -p 4cf44f1e3fe4fb7f8aa42138c324f63f5ac85828
可以看到:
tree 86550c31847e518e1927f95991c949fc14efc711
author Pierre De Wulf <test[@gmail.com](mailto:pie@gmail.com)> 1455775173 -0500
committer Pierre De Wulf <[test@gmail.com](mailto:pie@gmail.com)> 1455775173 -0500
commit A
两个重要的内容:
86550
同样是一个 object, 可以在 object 目录中找到查看具体的快照内容:
git cat-file -p 86550c31847e518e1927f95991c949fc14efc711
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file_1.txt
这里就找到了上面提到的三个 object 中的另外一个,这个 object 是 blob, 后文在展开。
到目前为止你已经知道了 git 中的一切都可以通过正确的 hash 来获取。现在然我们来看看 HEAD.
cat HEAD
ref: refs/heads/master
然而,HEAD 不是一个 hash, 那也 OK,之前的文章在介绍 HEAD 时都将 HEAD 比喻成一个指针,指向当前工作的分支。让我们来看看 refs/heads/master
cat refs/heads/master
4cf44f1e3fe4fb7f8aa42138c324f63f5ac85828
熟悉吧,这就是第一次提交的 hash. 这就显示了 branchs, tags, 都是指向 commit 的指针。这也就意味着你可以删除所有的分支,所有的 tags,但所有的提交依然还在。如果还想了解更多,可以查看 git book
年前我的 NAS 系统盘挂掉,数据倒是没丢,但让我的很多配置都要重来。
已经总结了:
剩下的其他就是应用数据的恢复和备份了。这里再总结一下 Gogs 数据的备份和恢复。
之前使用的是 Qnap club 上面 qpkg 文件来安装的 Gogs, 这次迁移到 Docker 中。
Gogs 的主要数据和其他很多应用类似,主要是数据库和本地配置文件。
关于 MySQL 数据库的备份就不再多说了,之前也有总结过文章。
使用 qpkg 文件安装的 QNAP 应用都会将数据存储在 /share/CACHEDEV1_DATA/.qpkg/
目录下,找到该目录下的 /share/CACHEDEV1_DATA/.qpkg/Gogs/
文件夹,如果不知道要备份该目录下的哪一个文件,笨办法就是把整个目录打包备份。
不过如果简单的查看一下 Gogs 的 Docker 镜像使用 就知道
# Pull image from Docker Hub.
$ docker pull gogs/gogs
# Create local directory for volume.
$ mkdir -p /var/gogs
# Use `docker run` for the first time.
$ docker run --name=gogs -p 10022:22 -p 10080:3000 -v /var/gogs:/data gogs/gogs
# Use `docker start` if you have stopped it.
$ docker start gogs
在 Gogs 的 Docker 镜像中 Gogs 只挂载了 /data
目录,所有的数据都保存在该目录中。
/var/gogs
|-- git
| |-- gogs-repositories
|-- ssh
| |-- # ssh public/private keys for Gogs
|-- gogs
|-- conf
|-- data
|-- log
观察该目录,就能看到主要是三个目录,结构一目了然。但是 QNAP 应用中结构就不那么清晰了。
/git/gogs-repositories
目录对应着 /home/gogs-repositories
目录,里面保存着所有 git 仓库文件。等启动 Docker 容器后,可以将该目录中的文件全部拷贝到 Docker 容器挂载的目录,比如我就是 /share/gogs/git
目录。
拷贝后可能还有权限问题,使用 chown user:group -R *
来解决一下(这里的 user group 要换成你系统中对应的)。
另外注意 /gogs/conf/
目录下的配置文件,这是一个全局的配置,非常重要。
Docker 启动过程中可能遇到如下错误:
error: kex_exchange_identification: client sent invalid protocol identifier
初步判断就是 Docker 端口配置错误,我之前配置 的 Gogs 服务,监听的两个端口分别是 10080 和 10022,所以在 Container Station 中配置的时候改一下即可。
Gitea 是一个 Gogs 的社区 fork 1,看对比 是一个 Gogs 极好的代替品。
https://blog.gitea.io/2016/12/welcome-to-gitea/ ↩
2019 年一整年都在履行着一件事情,那就是把以前所有用过的云服务替换成自己部署的私有云服务。
我是很多年 的 Dropbox 用户,至今为止所有的设备上也都安装着 Dropbox, 很多经常性使用的文件,配置也都在同步着,除了有些时候的网络问题,绝大部分使用完全没有问题。但之后陆续接触了一些其他文件同步工具,比如 pCloud,知道了原来可以端到端同步让文件同步更加安全;知道了 NextCloud,原来可以把数据交付给自己,让自己的数据更加安全;之后又知道了 Syncthing,原来可以让数据交换不像 NextCloud 那样走中心节点,可以自己实现一套分布式的文件同步系统,甚至这套系统部署节点越多,同步速度越快,并且不用互联网也能用。
所以最后的结果就是我彻底放弃了中心化的同步工具 (Dropbox, Google Drive),转而使用分布式的 Syncthing,并且让 Syncthing 同步原来的 Dropbox 文件夹,无痛迁移。
代码托管倒是没太多可谈的,毫无疑问 GitHub,最多在配置一下 mirror, 定期从 GitHub repository 中备份到 GitLab 或者自建的 Gogs 中。
另外 Gogs 的社区 fork 版本 Gitea 也不错。
笔记是另一个重头,之前从 Evernote 叛逃 到 WizNote,陆陆续续也续了三年会员,但一直也非常担心 WizNote 做不下去,所以一直留着一手,方便快速导出走人。
不过前不久再次发现 Joplin,作为一款本地笔记应用非常完美,唯独缺少同步功能,不过 Joplin 依赖本地文件,那么用上面提到的文件同步工具就完美地解决了同步问题。
另外 leanote 似乎也是一个自建不错的选择,不过目前我没尝试。
最近 QNAP 后台换了内存后起了很多应用,已经到了完全记不住端口的地步,为了不用每次都登录后台查看,只能用记事本记着,略感不方便,所以想起了能不能搞一个导航页给我自己用。所以就找到了 webstack,webstack 看到有好几个实现,不过其他的都需要数据库,不如纯文本来的方便,所以直接搞 Jekyll 的。
让其初始化就是缩小状态。
之前有文章写过如何在 Qnap 上使用 Container Station 来安装 NextCloud,之前重度使用 NextCloud,里面已经存了近 70G 的文件内容,这次系统重建后,下载新的镜像,然后重新恢复,费了一番时间,主要是恢复数据库,然后还有本地挂载的文件,以及升级版本。
从备份的角度来看,也正是这三个部分比较重要:
以上三个内容,前两个需要完全备份,否则会造成数据丢失。第三个则可以从 Docker Hub 上拉下来。
docker pull nextcloud:latest
我是从 13.0.4 版本的镜像升级到 14.0.9 版本,升级后重启容器,出现了如下的错误:
AH00169: caught SIGTERM, shutting down
调查发现,NextCloud 在升级过程中将自己变成了维护状态,这个配置在数据目录下的 config/config.php
文件中,打开该文件,搜索 maintenance
:
'maintenance' => true
将 true 改成 false, 然后重启容器,即可进入 NextCloud 应用。
使用同样的方式,将 NextCloud 从 17 版本升级到 18 版本。
访问 ip/status.php 会看到:
{"installed":true,"maintenance":true,"needsDbUpgrade":true,"version":"18.0.11.2","versionstring":"18.0.11","edition":"","productname":"Nextcloud","extendedSupport":false}
报错内容:
InvalidArgumentException: Column name “oc_flow_operations”.”entity” is NotNull, but has empty string or null as default.
解决方法,见 issue,因为我使用的是 MariaDB 所以:
alter table oc_flow_operations add column entity character varying(256) not null;
如果是其他数据库可能需要对应的 SQL 语句。
从 NextCloud 18 升级到 NextCloud 19 没有遇到什么坑。
将 Nextcloud docker 镜像升级到 20.0 没有遇到问题。
同样也没有遇到问题。
都知道其实 QNAP 的 Container Station 就是 Docker,所以桌面版可以修改的国内镜像地址,QNAP 系统上也能够修改,可以快速提高镜像的下载速度。
其实在网页管理段也能够手动添加,在 Container Station 属性中,Registry 服务器可以手动添加。
或者我们可以 SSH 登录到后台,然后手动编辑配置文件,docker 的路径是 /share/CACHEDEV1_DATA/.qpkg/container-station/
然后在该目录下有 etc/docker.json
文件,手动修改该文件:
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}
然后重启 Container Station 服务:
/etc/init.d/container-station.sh restart
可用的镜像地址,可以参考我另外一篇文章。
version: "2"
services:
bookstack:
image: linuxserver/bookstack
container_name: bookstack
environment:
- PUID=1000
- PGID=1000
- DB_HOST=10.0.3.1:3306
- DB_USER=bookstack
- DB_PASS=password
- DB_DATABASE=bookstack
volumes:
- /share/Container/bookstack_config:/config
ports:
- 6875:80
restart: unless-stopped
version: '2'
services:
filerun:
image: afian/filerun
container_name: filerun
environment:
FR_DB_HOST: 10.0.3.1
FR_DB_PORT: 3306
FR_DB_NAME: filerun
FR_DB_USER: filerun
FR_DB_PASS: password
APACHE_RUN_USER: www-data
APACHE_RUN_USER_ID: 1000
APACHE_RUN_GROUP: www-data
APACHE_RUN_GROUP_ID: 100
ports:
- "30080:80"
volumes:
- /share/filerun/html:/var/www/html
- /share/filerun/user-files:/user-files
restart: unless-stopped
docker run --name=calibre-web --restart=always \
-v /share/vol4Book/CalibreBooks:/books \
-v /share/Container/calibre-web/app:/calibre-web/app \
-v /share/Container/calibre-web/kindlegen:/calibre-web/kindlegen \
-v /share/Container/calibre-web/config:/calibre-web/config \
-e USE_CONFIG_DIR=true \
-e APP_REPO=https://github.com/janeczku/calibre-web.git \
-e APP_BRANCH=master \
-e SET_CONTAINER_TIMEZONE=true \
-e CONTAINER_TIMEZONE=Asia/Shanghai \
[-e PGID=100 -e PUID=1000 \]
-p 8083:8083 \
technosoft2000/calibre-web
docker run -d \
-v /share/NextCloud:/var/www/html \
-p 20080:80 \
nextcloud
docker run --name=gogs \
-p 10022:22 \
-p 10080:3000 \
-v /share/gogs:/data \
gogs/gogs
Tiny Tiny RSS
docker run -d --name ttrss --restart=unless-stopped \
-e SELF_URL_PATH=http://192.168.2.200:181 \
-e DB_HOST=10.0.3.1 \
-e DB_PORT=5432 \
-e DB_NAME=ttrss \
-p 181:80 \
wangqiru/ttrss
全文插件:
docker run -d \
--name=mercury-parser-api \
-p 3080:3000 \
wangqiru/mercury-parser-api
version: '3'
services:
weblate:
image: weblate/weblate
volumes:
- /share/Container/weblate/weblate-data:/app/data
env_file:
- ./environment
restart: always
ports:
- 5080: 8080
depends_on:
- database
- cache
database:
image: postgres:11-alpine
env_file:
- ./environment
volumes:
- /share/Container/weblate/postgres-data:/var/lib/postgresql/data
restart: always
cache:
image: redis:4-alpine
restart: always
command: ["redis-server", "--appendonly", "yes"]
volumes:
- /share/Container/weblate/redis-data:/data
还需要把这个项目 中的 environment 文件拷贝过来,然后再:
docker-compose build
docker-compose up
docker run -d \
--name rrshare \
-p 3001:3001 \
-v /share/rrshare:/opt/work/store \
oldiy/rrshare64:latest
docker run -d \
--name=lychee-laravel \
--restart always \
-v /share/Container/lychee/conf:/conf \
-v /share/Picture/Lychee:/uploads \
-e PHP_TZ=Asia/Shanghai \
-e PHP_MAX_EXECUTION_TIME=600 \
-e DB_CONNECTION=sqlite \
-e DB_DATABASE=/conf/lychee.db \
-p 90:80 \
80x86/lychee:latest
docker run -d \
--name=flexget \
-p 3539:3539 \
-v /share/Container/flexget/data:/data \
-v /share/Container/flexget/config:/config \
-e FG_WEBUI_PASSWD=password \
-e FG_LOG_LEVEL=info \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Asia/Shanghai \
wiserain/flexget
上周五 NAS 系统盘挂掉后一个周末都没有过好,一边忙着备份数据,一边要忙着整理系统应用和配置。早以前除了云端同步数据曾经出现过一两次数据丢失的情况,本地保存的数据还没有出现过管理的问题,系统会用 Clonezilla 全量备份,笔电日常数据则辅以同步工具 Dropbox(后替换成 Syncthing) 和自建的 NextCloud,平时丢数据的可能倒是比较小,但唯一疏漏的 NAS,因为硬盘不是一次性买全而是分了几次购买,所以从一开始就没有规划好如何存储与备份,而系统的酷狼 4T 盘可能经过几次家里停电,SMART INFO 出现警告的时候也没有想到备份,所以造成了从周五开始突然系统盘变成只读状态,无奈只能立即开启 rsync 手动先将系统盘中的数据备份到其他盘。等到周日把损坏的系统盘送修之后,是时候来思考一下如何管理及备份本地数据了。
经过这一次的“事故”,倒是没有丢数据,但是却是一件非常闹心的事情,去年花了一年的时间渐渐的逃离了云端服务,将照片从 Google Photos 离线到本地,将音乐从网易云音乐离线备份了一份到本地,想想这一切非常美好,没想到发生这么一件事情,所以也渐渐感受到本地 RAID 的重要性,对于重要的数据在本地一定要有一份备份。
原来我对数据的划分非常模糊,一股脑的备份笔电磁盘中所有数据,当然这种不会出现漏备份某些文件的情况,但这也是成本比较高的一种做法,不仅备份数据量大,并且备份的过程也比较长。而经过这次的事故,渐渐的对需要备份的数据有了一定区分度,由我产生的文件一定是最重要的,备份等级最高,包括我自己写下的文本(笔记,博客,TODO),拍摄的照片视频,代码文件,配置以及数据库文件(当然绝大部分代码都是 git 在另外有备份),不过还有一种非常容易忽略的就是,创建的比如音乐歌单,读书列表,在 Trello 中的 Task 等等,当然大部分情况下都是对应的服务中,网易云音乐就曾经莫名其妙地弄丢我一个歌单,这些”文件”都需要提高备份级别。
所以简单的来说数据需要分类处理,依据重要程度不同需要备份的方法也可以有对应不同,就目前我个人遇到的情况而言。
几大类由我产生的重要数据:
几类由我整理收集的数据:
针对上面这些数据,分别有不同的备份方法:
对于纯文本的内容,适合 git 来备份,commit && push 就能够至少在两地有备份了。Git 的分布式特性天然的适合文本的存档和备份管理。
适合 git 来备份的有:
对于云同步工具,适合同步二进制文件,比如文档或者图片。
从 2020 年中开始,就陆陆续续将 Dropbox,NextCloud 替换成了 Syncthing,几乎是无缝的迁移,将同步的文件夹添加到 Syncthing 中,我的所有设备都加上 Syncthing 的客户端进行同步。
对于需要备份,但是查看次数非频繁的文档,适合单独使用硬盘来备份,比如:
对于媒体文件,尤其是较大的电影,电视剧,图片,等等适合使用单独的备份盘来备份。
使用 OpenMediaVault 的系统使用 SnapRAID 来对磁盘做冗余备份,以防止一块硬盘挂掉。
macOS 下有 Time Machine 备份,Linux Mint 下可以使用 rsync 来备份,如果是 Windows 或 [[Proxmox VE]] 我就是用 Clonezilla 来备份一个分区。
对磁盘的管理,对数据的种类进行划分,对不同的数据进行不同的备份处理。
如果磁盘存放的是个人数据,则要尽量减少磁盘的写入,尽量在备份的时候对磁盘写入,从而延长磁盘的寿命,如果可能这部分个人数据尽量使用 RAID1 或者 RAID5,做到即使有一块磁盘故障时数据不会丢失。
而另外一类场景则是下载电影,当使用 BT 或者 PT 时,磁盘会有大量的读写会大大减少磁盘的寿命,所以我的建议是单独使用一块磁盘作为下载盘,有对磁盘的大量不间断读写时都存放到该磁盘,并周期性对磁盘进行维护,比如使用同步工具定时将数据移动或者拷贝到另外的磁盘中进行保存。
网上有一种说法,说家用 RAID 实际用途并不大,这句话并不完全正确,如果家用 24h 挂机 PT 的话,如果数据是写到 RAID5 中,那么实际上三块磁盘都在工作,无形中就同时降低了三块磁盘的寿命。如果是常年 PT 的话,还是根据个人情况来选择最合适的方案。
平时没有注意备份 QNAP 上系统盘的数据,从昨天开始系统盘突然只读,而无法写入,发现磁盘有问题了,无奈只能边申请售后,边想着怎么备份数据,还要恢复这么多的配置。
平常的哪些文件备份倒是还好说,但是一直有用 QNAP 提供的 SQL server 服务,这部分数据平时也没有 mysqldump 下来,所以这就变得比较尴尬,但是 sql server 还有一种方法可以从物理文件中恢复。
用 ps 工具查看可以看到 1:
/bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/usr/local/mysql/var --pid-file=/var/lock/qmysql.pid --user=admin
datadir
就是 marialdb 真正存放数据的地方,ssh 进后台,备份该部分数据。
在该目录中能看到不同的文件
通常情况下 linux 是在 /var/lib/mysql/
文件夹下,QNAP 的地址是 /usr/local/mysql/var
目录下。
使用 rsync
将该目录下的数据全部备份到另外的磁盘上
rsync -azvhP --progress /usr/local/mysql/var/ /share/Backup/mysql_var/
等 QTS 系统安装完成后,再使用 rsync 来将数据恢复回来。
750 小时的运行时间,Amazon Elastic Compute Cloud (Amazon EC2) 是一种 Web 服务,可以在云中提供安全并且可调整大小的计算容量。
对象存储服务
MySQL、PostgreSQL、MariaDB、Oracle BYOL 或 SQL Server 的托管关系数据库服务。
快速灵活的 NoSQL 数据库,具有无缝可扩展性。
计算服务
快速、灵活、完全托管的推送消息收发服务。
1000 封电子邮件
虽然网易云音乐发布了 Linux 版本的客户端,但网易隐藏的云音乐的上传入口只有 Windows 版本的也有,所以这里记录一下如何在 Linux Mint 下使用 PlayOnLinux 来安装网易云音乐 Windows 客户端。
安装过程比较简单,我个人使用 Wine 3.20 版本,然后按照 PlayOnLinux 提示的步骤一步步执行即可。