- A+
一个肯定能让你节省几个小时的小知识
大家好,我是 小猿来也,一个人称撸(划)码(水)小能手的程序猿。
最近一段时间,每次经过旁边大佬工位,总是发现他在快速的切屏,不知道在搞什么?难道他发现了快乐星球?
终于有一天当他沉浸其中的时候,让我发现了,原来他是在撸 Linux 的源码。
撸代码又不是划水,至于这样藏着掖着?
我也试一试?
Linux 的源代码会不会太难了?有点怂。
最终我还是爬上了 GitHub,找到了 Linux 源代码的仓库。
Linux 永远的神
30年的祖传老代码
不愧为现在互联网的基石,7.8k 的 watch,115k 的 star,37.9k 的 fork,star 数全 GitHub 排名第19名,真是666。
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 请在切换分支前移动或删除。 终止中
这是怎么事儿?
然后我连忙在 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")
这就更尴尬了,我刚克隆的热乎的代码,我确定我什么都没改。然而这是怎么回事儿?整地我都有点儿怀疑我自己是不是真地干了啥了?
但是我真地啥都没动呀,代码却又真地被改了,这简直太不可思议了。
无奈之下我决定随便找个文件一探究竟,于是我进入了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
。
也就是说不知道因为什么原因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
。
这就更让我郁闷了,对文件ip6t_hl.h
进行恢复之后它变成了文件ip6t_HL.h
,但是它(ip6t_HL.h
)依然是一个被变更的文件。
看样子是文件变更并没有恢复成功,我单纯地只是想下载 Linux 的源码装(看)个(下)逼,不成想下载之后,莫名其妙的多出一些文件变更,而且还撤销不了。
难道今日不宜撸代码?
紧接着我又去看了下文件内容的变更情况
看来不只是文件名称的变更,文件内容同样也变更了。
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
,一个文件名大写,一个文件名小写。
文件的内容和「文件内容差异」这张图片中的内容也相符合。
但是现在的情况是代码克隆下来之后却只有一个文件了,是因为 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 创建了如下的这个仓库 (为了显得更规范,写这篇文章的时候仓库我重新建过)。
这个仓库很简单,整个仓库除了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
已被占用。
是 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 的磁盘工具
-
选择磁盘,点击 "添加APFS宗卷"
-
设置宗卷的名称、选择「 APFS(区分大小写)」格式,设置宗卷的大小。
10G应该够我用来存 Linux 的源代码了
-
确认宗卷信息,确定"添加"
-
查看已添加的宗卷
到这里一个支持文件名区分大小写的宗卷就添加完毕了。 -
进入新添加的宗卷
查看宗卷的挂栽点,挂载点为/Volumes/x
前往挂栽点去看看
或者直接
$ 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' 一致。 无文件要提交,干净的工作区
我太难了!!!
1991年,Linus Torvalds 永远的神。。。
Checkout 到v4.13的 Commit
$ git checkout v4.13 正在检出文件: 100% (82007/82007), 完成. 注意:正在检出 'v4.13'。 您正处于分离头指针状态。您可以查看、做试验性的修改及提交,并且您可以通过另外 的检出分支操作丢弃在这个状态下所做的任何提交。 如果您想要通过创建分支来保留在此状态下所做的提交,您可以通过在检出命令添加 参数 -b 来实现(现在或稍后)。例如: git checkout -b <新分支名> HEAD 目前位于 569dbb88e80d Linux 4.13
心情豁然开朗,好像后面马上就可以愉快的撸(划)码(水)啦!
有没有涨知识呢?收藏一波吧?
最后补充一点
Windows 怎么整?
如果你的环境是 Windows 也是有解决办法的,从 Windows 10 Version 1803 更新开始,微软为 NTFS 文件系统新增了一个 SetCaseSensitiveInfo 标志。
你可以有选择的为所需文件夹启用此 flag,启用之后 NTFS 文件系统就会针对该文件夹将其子文件视为区分大小写的文件系统。
那么如何开启文件的 SetCaseSensitiveInfo 标志呢,只需要你用管理员身份运行一下这个指令就可以了
fsutil file SetCaseSensitiveInfo you-dir-path enable
我是小猿来也,大家撸码愉快呀!!!