为什么刚克隆的Linux内核代码仓库中部分⽂件丢失?你肯定也
会遇到
⼀个肯定能让你节省⼏个⼩时的⼩知识
⼤家好,我是 uwoerla,⼀个⼈称撸(划)码(⽔)⼩能⼿的程序猿。
最近⼀段时间,每次经过旁边⼤佬⼯位,总是发现他在快速的切屏,不知道在搞什么?难道他发现了快乐星球?
终于有⼀天当他沉浸其中的时候,让我发现了,原来他是在撸 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: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 上真的有两个⽂件。
这两个⽂件分别是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: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: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: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
linux内核文件放在哪$ touch A
$ ls
a
还真是的呢,macOS 不允许同⼀⽬录下存在忽略⼤⼩写之后同名的⽂件。
原来是这个原因。
Linux 系统中⽂件(夹)名是区分⼤⼩写的,⽽ Windows 系统和 macOS 系统中⽂件(夹)名是不区分⼤⼩写的,当在同⼀⽬录下存在区分⼤⼩写的同名⽂件(夹)时,处理起来就会变得⽐较⿇烦。
简单说就是:Linux 系统可以在同⼀路径下同时建⽴ YEAH 和 yeah 这两个区分⼤⼩写的⽂件夹或者⽂件,⽽在 Windows 系统和 macOS 系统中这样就做会提⽰⽂件名已被占⽤,从⽽⽆法创建。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。