一个肯定能让你节省几个小时的小知识
大家好,我是 uwoerla,一个人称撸(划)码(水)小能手的程序猿。
最近一段时间,每次经过旁边大佬工位,总是发现他在快速的切屏,不知道在搞什么?难道他发现了快乐星球?
![](https://img.haomeiwen.com/i12902848/78a0781f4693cac8.jpeg)
终于有一天当他沉浸其中的时候,让我发现了,原来他是在撸 Linux 的源码。
撸代码又不是划水,至于这样藏着掖着?
我也试一试?
Linux 的源代码会不会太难了?有点怂。
最终我还是爬上了 GitHub,找到了 Linux 源代码的仓库。
Linux 永远的神
30年的祖传老代码
![](https://img.haomeiwen.com/i12902848/995194088fd32686.png)
不愧为现在互联网的基石,7.8k 的 watch,115k 的 star,37.9k 的 fork,star 数全 GitHub 排名第19名,真是666。
![](https://img.haomeiwen.com/i12902848/790788f664ff3234.png)
Linux 起源于1991年,距离今天已经30年了,30年的祖传老代码,应该可以说是人类历史上最最有价值的代码啦,你值得拥有。
打开 iTerm 键入git clone git@github.com:torvalds/linux.git
之后,我的 Linux 源码下载之旅这就开始了。
不得不说 Linux 的源码仓库可太大了,最终下载下来大概占用了 4.7G 的磁盘空间,有 824.1W个 Enumerating object,72865 个文件。下载的时候等的我都要吐血了,中间我还跑出去吃了个饭,回来的时候发现它...它竟然还在下载。
du -sh linux/
4.6G linux/
remote: Enumerating objects: 8241432, done.
接收对象中: 20% (1682045/8241432), 1.44 GiB | 235.00 KiB/s
.
.
.
接收对象中: 100% (8227041/8227041), 3.18 GiB | 232.00 KiB/s, 完成.
处理 delta 中: 100% (6846961/6846961), 完成.
正在检出文件: 100% (72865/72865), 完成.
前前后后应该等了大概 4 个小时,终于它下完了,这时间可太久了。
下载下来之后,我啥也没干,第一件事儿就是看看它的目录结构,嗯它的目录结构是这样的:
$ tree linux/ -L 1
linux/
├── COPYING
├── CREDITS
├── Documentation
├── Kbuild
├── Kconfig
├── LICENSES
├── MAINTAINERS
├── Makefile
├── README
├── arch
├── block
├── certs
├── crypto
├── drivers
├── fs
├── include
├── init
├── ipc
├── kernel
├── lib
├── mm
├── net
├── samples
├── scripts
├── security
├── sound
├── tools
├── usr
└── virt
22 directories, 7 files
哈哈不管代码撸不撸的动,我总算是位见过 Linux 源代码的目录结构的程序员了。
是谁动了我刚克隆的源代码
刚克隆的 Linux 源代码莫名其妙少了几个文件
在一个帖子里看到撸 Linux 的源码可以基于v4.13这个版本,所以我就在 iTerm 中键入 git checkout v4.13
来尝试 check out 对应的代码。
本以为会很顺利,结果:
$ git checkout v4.13
error: 您对下列文件的本地修改将被检出操作覆盖:
include/uapi/linux/netfilter/xt_CONNMARK.h
include/uapi/linux/netfilter/xt_DSCP.h
include/uapi/linux/netfilter/xt_MARK.h
include/uapi/linux/netfilter/xt_RATEEST.h
include/uapi/linux/netfilter/xt_TCPMSS.h
include/uapi/linux/netfilter_ipv4/ipt_ECN.h
include/uapi/linux/netfilter_ipv4/ipt_TTL.h
include/uapi/linux/netfilter_ipv6/ip6t_HL.h
net/netfilter/xt_DSCP.c
net/netfilter/xt_HL.c
net/netfilter/xt_RATEEST.c
net/netfilter/xt_TCPMSS.c
tools/memory-model/litmus-tests/Z6.0+pooncelock+poonceLock+pombonce.litmus
请在切换分支前提交或贮藏您的修改。
error: 工作区中下列未跟踪的文件将会因为检出操作而被覆盖:
Documentation/arm/Samsung/clksrc-change-registers.awk
Documentation/security/LSM.rst
drivers/staging/rtl8188eu/hal/odm_HWConfig.c
drivers/staging/rtl8188eu/hal/odm_RTL8188E.c
drivers/staging/rtl8188eu/include/odm_HWConfig.h
drivers/staging/rtl8188eu/include/odm_RTL8188E.h
tools/testing/selftests/rcutorture/configs/rcu/SRCU-t
tools/testing/selftests/rcutorture/configs/rcu/SRCU-t.boot
tools/testing/selftests/rcutorture/configs/rcu/SRCU-u
tools/testing/selftests/rcutorture/configs/rcu/SRCU-u.boot
请在切换分支前移动或删除。
终止中
这是怎么事儿?
![](https://img.haomeiwen.com/i12902848/6b614d2b534e3df6.jpg)
然后我连忙在 iTerm 中键入git status
来追踪文件状态
$ git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: include/uapi/linux/netfilter/xt_CONNMARK.h
修改: include/uapi/linux/netfilter/xt_DSCP.h
修改: include/uapi/linux/netfilter/xt_MARK.h
修改: include/uapi/linux/netfilter/xt_RATEEST.h
修改: include/uapi/linux/netfilter/xt_TCPMSS.h
修改: include/uapi/linux/netfilter_ipv4/ipt_ECN.h
修改: include/uapi/linux/netfilter_ipv4/ipt_TTL.h
修改: include/uapi/linux/netfilter_ipv6/ip6t_HL.h
修改: net/netfilter/xt_DSCP.c
修改: net/netfilter/xt_HL.c
修改: net/netfilter/xt_RATEEST.c
修改: net/netfilter/xt_TCPMSS.c
修改: tools/memory-model/litmus-tests/Z6.0+pooncelock+poonceLock+pombonce.litmus
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
这就更尴尬了,我刚克隆的热乎的代码,我确定我什么都没改。然而这是怎么回事儿?整地我都有点儿怀疑我自己是不是真地干了啥了?
![](https://img.haomeiwen.com/i12902848/4f72ed30a091b23d.png)
但是我真地啥都没动呀,代码却又真地被改了,这简直太不可思议了。
无奈之下我决定随便找个文件一探究竟,于是我进入了include/uapi/linux/netfilter_ipv6/
这个目录下。
$ cd include/uapi/linux/netfilter_ipv6/
$ pwd
github/linux/include/uapi/linux/netfilter_ipv6
$ ls
ip6_tables.h ip6t_NPT.h ip6t_ah.h ip6t_hl.h ip6t_mh.h ip6t_rt.h
ip6t_LOG.h ip6t_REJECT.h ip6t_frag.h ip6t_ipv6header.h ip6t_opts.h ip6t_srh.h
不知道你有没有注意到ip6t_hl.h
这个文件,它在git status
给出的信息中是ip6t_HL.h
,而在文件目录下的实际情况是ip6t_hl.h
。
![](https://img.haomeiwen.com/i12902848/20b7d9ff48d4974b.png)
也就是说不知道因为什么原因ip6t_HL.h
这个文件它变成了ip6t_hl.h
这个文件。
接着我就找到了include/uapi/linux/netfilter_ipv6/ip6t_hl.h
这个文件,尝试把它恢复到它变更之前的状态。
$ cd -
github/linux
$ git checkout -- include/uapi/linux/netfilter_ipv6/ip6t_HL.h
执行完文件恢复指令后,我再次执行git status
查看文件的状态
$ git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: include/uapi/linux/netfilter/xt_CONNMARK.h
修改: include/uapi/linux/netfilter/xt_DSCP.h
修改: include/uapi/linux/netfilter/xt_MARK.h
修改: include/uapi/linux/netfilter/xt_RATEEST.h
修改: include/uapi/linux/netfilter/xt_TCPMSS.h
修改: include/uapi/linux/netfilter_ipv4/ipt_ECN.h
修改: include/uapi/linux/netfilter_ipv4/ipt_TTL.h
修改: include/uapi/linux/netfilter_ipv6/ip6t_hl.h
修改: net/netfilter/xt_DSCP.c
修改: net/netfilter/xt_HL.c
修改: net/netfilter/xt_RATEEST.c
修改: net/netfilter/xt_TCPMSS.c
修改: tools/memory-model/litmus-tests/Z6.0+pooncelock+poonceLock+pombonce.litmus
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
于此同时我又进入了include/uapi/linux/netfilter_ipv6/
这个目录下查看对应的文件
$ cd include/uapi/linux/netfilter_ipv6/
$ ls
ip6_tables.h ip6t_LOG.h ip6t_REJECT.h ip6t_frag.h ip6t_mh.h ip6t_rt.h
ip6t_HL.h ip6t_NPT.h ip6t_ah.h ip6t_ipv6header.h ip6t_opts.h ip6t_srh.h
同样的情况,我注意到先前的ip6t_hl.h
这个文件,这个时候在git status
给出的信息中是ip6t_hl.h
,而在文件目录下的实际情况是ip6t_HL.h
。
![](https://img.haomeiwen.com/i12902848/be33b910bc3a446c.png)
这就更让我郁闷了,对文件ip6t_hl.h
进行恢复之后它变成了文件ip6t_HL.h
,但是它(ip6t_HL.h
)依然是一个被变更的文件。
看样子是文件变更并没有恢复成功,我单纯地只是想下载 Linux 的源码装(看)个(下)逼,不成想下载之后,莫名其妙的多出一些文件变更,而且还撤销不了。
难道今日不宜撸代码?
![](https://img.haomeiwen.com/i12902848/3584a36c37df58fd.jpeg)
紧接着我又去看了下文件内容的变更情况
![](https://img.haomeiwen.com/i12902848/a300dbaf8c7764f3.jpg)
看来不只是文件名称的变更,文件内容同样也变更了。
Git 对文件名称大小写不敏感
默认的情况下 Git 对文件名的大小写是不敏感的
感觉应该是有两个文件,这个时候我有点怀疑会不会是 Git 对文件名称大小写不敏感,把两个文件当成一个文件来处理了?
抱着这个疑问我再次爬上了 Github 进入到了https://github.com/torvalds/linux/tree/master/include/uapi/linux/netfilter_ipv6
这个目录
GitHub 上真的有两个文件。
这两个文件分别是include/uapi/linux/netfilter_ipv6/ip6t_HL.h
、include/uapi/linux/netfilter_ipv6/ip6t_hl.h
,一个文件名大写,一个文件名小写。
![](https://img.haomeiwen.com/i12902848/8ecf90c36faeacc5.png)
文件的内容和「文件内容差异」这张图片中的内容也相符合。
![](https://img.haomeiwen.com/i12902848/cc707ec494702d39.png)
但是现在的情况是代码克隆下来之后却只有一个文件了,是因为 Git 对文件名称大小写不敏感,后下载的那个文件覆盖了先下载的文件吗?是ip6t_hl.h
覆盖了ip6t_HL.h
,所以才会出现上文中我们所看到的现象吗?
一番检索后我发现 Git 真地有一个配置是设置它是否忽略文件名大小写的。
这一点不知道大家是否知道,我平时真的是没有注意到,难道是我平时的文件命名太过规范了吗,所以遇不到吗?
Git 略文件名大小写这个配置默认是打开的,也就是说默认的情况下 Git 对文件名的大小写是不敏感的。
我们可以通过设置core.ignorecase
这个参数改变 Git 对文件名大小写敏感性。
注意到这个方向后,感觉抓到了救命稻草,于是我就尝试先在 Linux 这个仓库内设置 Git 的大小写敏感策略。
设置前我先查看了一下它本来的设置情况
$ ls -A linux/
.DS_Store .gitignore Kbuild arch include net usr
.clang-format .idea Kconfig block init samples virt
.cocciconfig .mailmap LICENSES certs ipc scripts
.get_maintainer.ignore COPYING MAINTAINERS crypto kernel security
.git CREDITS Makefile drivers lib sound
.gitattributes Documentation README fs mm tools
$ cat linux/.git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:torvalds/linux.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
通过 Git 客户端直接从远程仓库克隆代码,默认是没有对大小写敏感做显示的设置的。
然后我就通过git config core.ignorecase false
这条指令尝试去对这个仓库做单独的大小写敏感策略设置。
$ pwd
github/linux
$ git config core.ignorecase false
下面是设置之后的配置项内容
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
precomposeunicode = true
ignorecase = false
[remote "origin"]
url = git@github.com:torvalds/linux.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
设置完之后我又一次尝试对文件进行恢复操作,然而我发现这并没有起到任何作用。
这个时候我意识到,如果是因为 Git 对文件名称大小写不敏感,后下载的那个文件覆盖了先下载的文件这一假设引起的,那么文件名大小写冲突应该只会发生在 Linux 仓库下载的过程中,因此在下载之后设置大小写敏感策略是不会起到任何作用的。
最后我决定先为 Git 设置一个全局的大小写敏感策略,然后再克隆 Linux 仓库的源代码。
Git 设置全局的大小写敏感策略对应的指令是git config --global core.ignorecase false
。
$ git config --global core.ignorecase false
$ git config --global --get core.ignorecase
false
设置完我就换了个目录重新从 GitHub 克隆 Linux 的源代码。
因为有了第一次克隆的经验,我意识到它等待的时间会特别长,所以在克隆 Linux 源代码的同时我决定在 GitHub 上建了个实验仓库来验证这个假设。
我爬上了 GitHub 创建了如下的这个仓库 (为了显得更规范,写这篇文章的时候仓库我重新建过)。
![](https://img.haomeiwen.com/i12902848/ca527fd53dd7ae32.png)
这个仓库很简单,整个仓库除了
README.md
文件之外,只有两个文件,一个文件名是YEAH
,另一个文件名是yeah
,是的,如你所见这个两个文件的文件名在忽略大小写之后是一样的。
然后我在自己的本本上尝试克隆这个简单地不能再简单的仓库。
$ git clone git@github.com:tobrainto/git-ignore-case-same-file-name.git
Cloning into 'git-ignore-case-same-file-name'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (9/9), done.
Resolving deltas: 100% (1/1), done.
克隆很顺利,然而当我查看克隆下来的文件时
$ cd git-ignore-case-same-file-name/
$ ls
README.md yeah
$ git status
位于分支 main
您的分支与上游分支 'origin/main' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: YEAH
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
同样的结果,丢了一个文件,也就是说在git config --global core.ignorecase false
这个全局参数设置之后我克隆仓库,依然未能解决问题。
到这里上面的假设自然就被推翻了,显然 Git 的确有对文件名大小写是否敏感的设置参数,但是它不是引起我在克隆 Linux 源代码过程中丢失文件的原因,然后我就取消了上面正在缓慢进行对 Linux 源码的第二次克隆。
操作系统默认对文件(夹)名大小写不敏感
默认情况下,macOS 系统和 Windows 系统是不允许同一目录下存在忽略大小写之后文件名相同的文件(夹)的。
折腾了这么一圈我都有点儿想放弃了,我决定直接把有问题的文件创建出来,然后从 GitHub 上拷贝下对应的内容。
先从ip6t_HL.h
开始,当我拷贝ip6t_hl.h
文件准备改名为ip6t_HL.h
时,操作系统直接提示我ip6t_HL.h
已被占用。
![](https://img.haomeiwen.com/i12902848/5e4eac724e046c30.png)
是 macOS 不允许同一目录下存在忽略大小写之后同名的文件吗?
为了排除 Git 的影响,我换个目录又确认了一遍。
$ cd /Users/yeah/
$ pwd
/Users/yeah/
$ touch A
$ touch a
$ ls
A
$ pwd
/Users/yeah/
rm -rf *
$ ls
$ touch a
$ touch A
$ ls
a
还真是的呢,macOS 不允许同一目录下存在忽略大小写之后同名的文件。
原来是这个原因。
Linux 系统中文件(夹)名是区分大小写的,而 Windows 系统和 macOS 系统中文件(夹)名是不区分大小写的,当在同一目录下存在区分大小写的同名文件(夹)时,处理起来就会变得比较麻烦。
简单说就是:Linux 系统可以在同一路径下同时建立 YEAH 和 yeah 这两个区分大小写的文件夹或者文件,而在 Windows 系统和 macOS 系统中这样就做会提示文件名已被占用,从而无法创建。
所以克隆 Linux 源代码的时候,当文件include/uapi/linux/netfilter_ipv6/ip6t_hl.h
被保存之后,文件include/uapi/linux/netfilter_ipv6/ip6t_HL.h
就无法保存了;在我尝试恢复的时候一个文件又会覆盖另一个文件。
隐约的记得之前我应该也有遇到过,但是这种场景出现的概率非常小,然后我就完全没有印象了。
到这里原因已经很明确了,是因为操作系统对文件(夹)名大小写不敏感。
那 Mac OS 可以支持区分文件名大小写吗?
这个当然,但是区分文件名大小写不是 Mac OS 的默认选项,如果需要支持区分文件名大小写,需要我们稍微做一些功课,进行一些设置。
Apple 文件系统 (APFS)
基于 Apple 官网的这个页面
https://support.apple.com/zh-cn/guide/disk-utility/dsku19ed921c/20.0/mac/11.0
我们可以看出 Mac 支持多种文件系统格式。
APFS 适用于 macOS 10.13 或后续版本使用的文件系统,APFS 支持以下4种格式。
- APFS:使用 APFS 格式。如果不需要加密或区分大小写格式,请选取此选项。
- APFS(加密):使用 APFS 格式且加密宗卷。
- APFS(区分大小写):使用 APFS 格式并区分文件和文件夹名称的大小写。例如,名称为"Homework"和"HOMEWORK"的文件夹是两个不同的文件夹。
- APFS(区分大小写,加密):使用 APFS 格式,区分文件和文件夹名称的大小写且加密宗卷。例如,名称为"Homework"和"HOMEWORK"的文件夹是两个不同的文件夹。
使用 APFS 的 Mac 我们可以轻松的通过添加 APFS 容器中的宗卷(具体可以参考这个页面https://support.apple.com/zh-cn/guide/disk-utility/dskua9e6a110/20.0/mac/11.0 ),在添加宗卷时指定格式为「 APFS(区分大小写)」来低成本的开辟一块区分大小写的存储块。
Mac OS 扩展
Mac OS 扩展适用于 macOS 10.12 或之前版本使用的文件系统,Mac OS 扩展 支持以下4种格式。
- Mac OS 扩展(日志式):使用 Mac 格式(日志式 HFS Plus)来保护分层文件系统的完整性。如果不需要加密或区分大小写格式,请选取此选项。
- Mac OS 扩展(日志式,加密):使用 Mac 格式,要求密码,并加密分区。
- Mac OS 扩展(区分大小写,日志式):使用 Mac 格式并区分文件夹名称的大小写。例如,名称为"Homework"和"HOMEWORK"的文件夹是两个不同的文件夹。
- Mac OS 扩展(区分大小写,日志式,加密):使用 Mac 格式,区分文件夹名称的大小写,要求密码,并加密分区。
使用 Mac OS 扩展的 Mac 我们可以通过分区,在分区的时候指定格式为「Mac OS 扩展(区分大小写,日志式)」来获得区分大小写的一个分区。
由于我的本本已经升级到了 macOS Big Sur(11.2.1)(升级过程还蛮坑的,感兴趣的我之前的文章:升级 macOS Big Sur 差点丢了我多年的珍藏文件(夹)!!!),所以这里我采用的是通过添加 APFS 宗卷的方式来获得一块区分大小写的存储块,整个过程是这样的。
-
找到 mac 的磁盘工具
image
-
选择磁盘,点击 "添加APFS宗卷"
image
-
设置宗卷的名称、选择「 APFS(区分大小写)」格式,设置宗卷的大小。
image
10G应该够我用来存 Linux 的源代码了
image
-
确认宗卷信息,确定"添加"
image
image
-
查看已添加的宗卷
image
到这里一个支持文件名区分大小写的宗卷就添加完毕了。
-
进入新添加的宗卷
查看宗卷的挂栽点,挂载点为/Volumes/x
image
前往挂栽点去看看
image
或者直接
$ cd /Volumes/x
$ pwd
/Volumes/x
- 创建文件试一把
$ touch A
$ touch a
$ ls
A a
同一目录下已经可以同时存在区分大小写同名的文件。
在区分大小写的宗卷上再次克隆 Linux 源代码
强迫症的我在区分大小写的宗卷上再次克隆 Linux 源代码
准备好区分文件名大小写的宗卷之后,我就在/Volumes/x
这个目录下重新从 GitHub 上克隆 Linux 仓库的源代码。
又是漫长地等待,4 个小时过去了,第二次真正意义上克隆才算完成。
当我再次敲下git status
指令时
$ pwd
/Volumes/x
$ cd linux/
$ git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。
无文件要提交,干净的工作区
我太难了!!!
![](https://img.haomeiwen.com/i12902848/031b2886eeff51b5.png)
1991年,Linus Torvalds 永远的神。。。
![](https://img.haomeiwen.com/i12902848/85e18e8278ad0370.jpeg)
Checkout 到v4.13的 Commit
$ git checkout v4.13
正在检出文件: 100% (82007/82007), 完成.
注意:正在检出 'v4.13'。
您正处于分离头指针状态。您可以查看、做试验性的修改及提交,并且您可以通过另外
的检出分支操作丢弃在这个状态下所做的任何提交。
如果您想要通过创建分支来保留在此状态下所做的提交,您可以通过在检出命令添加
参数 -b 来实现(现在或稍后)。例如:
git checkout -b <新分支名>
HEAD 目前位于 569dbb88e80d Linux 4.13
![](https://img.haomeiwen.com/i12902848/c561d35162ac91d0.jpeg)
心情豁然开朗,好像后面马上就可以愉快的撸(划)码(水)啦!
有没有涨知识呢?收藏一波吧?
最后补充一点
Windows 怎么整?
如果你的环境是 Windows 也是有解决办法的,从 Windows 10 Version 1803 更新开始,微软为 NTFS 文件系统新增了一个 SetCaseSensitiveInfo 标志。
你可以有选择的为所需文件夹启用此 flag,启用之后 NTFS 文件系统就会针对该文件夹将其子文件视为区分大小写的文件系统。
那么如何开启文件的 SetCaseSensitiveInfo 标志呢,只需要你用管理员身份运行一下这个指令就可以了
fsutil file SetCaseSensitiveInfo you-dir-path enable
我是 uwoerla,大家撸码愉快呀!!!
网友评论