Xcode 吸吮。git reflog 保存了我的生活。
我在两天之内遇到了Xcode 的两个致命问题。
首先, 昨天在Xcode 上Check Out 一个之前的Commit 时,出现了.xcworkspace文件不存在的提示,并且提供了Close 和Re-Save 的选项。
The workspace file that was at “” has disappeared. Do you want to re-save the container, or close it?
考虑到有人会直接Google 提示信息,所以我把提示信息复制过来便于被Google 到。
Xcode .xcworkspace 文件丢失提示
我在Finder 里确认了文件存在,于是选择了Close。但当我再打开项目时,所有文件索引和Git 索引都在Xcode 里不见了:
Xcode 丢失Git仓库索引
Xcode 丢失文件索引
虽然这些文件依然存在本地,理论上可以在命令行里直接使用Git 的命令来恢复,但由于Xcode 里的Git 选项里所有Git 仓库信息丢失,不能直接用图形化的界面来恢复到之前的样子。
开始我以为是iCloud 的锅,但今天我把工程放到本地空间来编写依然遇到了同样的问题。所以我猜测这个问题的出现是因为Xcode writes to file 的时候并没有以atomically 的方式?(正好前两天在看write 到Document Directory 的相关接口)无论如何,既然不是iCloud 的问题,这应该就是Xcode 的问题了。好在我在Check Out 前的那次Commit 的同时也Push 到了Github,因此直接把仓库重新Clone 了下来。
这个问题的解决方法是:弹出提示时应该选择Re-Save 而不是Close,即使实际上Finder 里文件存在。
第二个问题是, 如果直接在Xcode 的Git分支A的Commit B处右键选择Check Out(下图1),而不是 在分支A 处选择Check Out(下图2,因为已Check Out 到该Branch,所以这里是灰色),那么…
那么,首先我们会看到,没有任何一个Branch 后面会显示“(current)”,如下,可与上面的图对比。
其次,如果你同时载入了远程仓库,你会在Push 的时候看到Xcode 一直在loading,无法加载出远程的Branch供你选择。
Xcode 无法加载远程Branch
再次,也是更严重的,假如你在这次Check Out 后对代码进行修改,然后Commit,你会发现Xcode 确实能暂时提交并显示此次Commit:
但你也发现了,由于你没从main Branch Check Out 过去,这个Commit 并没有提交到main。
我刚发现这个问题的时候,如何补救这个场面呢?肯定想把最新的那次Commit 代表的Branch 给Merge 到main Branch。但想要在Xcode 里Merge A Branch 到 B Branch,必须先Check Out 到B ,之后选择Merge A Branch into B Branch。
所以,我Check Out到了main Branch。
这下好了,我发现刚刚的那个Commit 彻底在Xcode 中消失了。我无从Merge:
而刚刚Xcode 根本无法加载远程仓库,所以我刚刚的改动并没有被Push 到远程仓库上。
所以,我对Initial Commit 更改后的Commit,全部“丢失”了。
——但也不是彻底丢失。以下是我补救的过程:
首先,我搜到了jameskaron 在2017 年写的一篇博客:[IOS][git]使用XCODE9时使用git commit导致代码丢失问题 。
很遗憾,按照原文的方法,使用git log
命令并没有看到修改后的Commit,只有Initial Commit。这说明似乎Xcode 并没有提交那次Commit ?
本来刚刚燃起希望的我以为我已经完全丢失这份Commit 了。
然后在StackOverflow 上搜到了一个好像不太相关,但确实派上了用场的问题:How to get back to the latest commit after checking out a previous commit?
作为对Git 没有太多了解的小朋友,第一次知道reflog。于是我抱着试一试的心态,输入了git reflog
,查看了指代日志(?应该是这样翻译的吧)。
Hooray!
注意第4行99eb23
虽然Commit 的log 里面啥都没有,但reflog 里出现了修改Initial Commit 之后的那次Commit 的7 位Hash 。
最后通过git checkout hash
(这里的hash 是99eb123),成功回到了这个“丢失”的Commit。
这时如果需要使用Xcode 的图形界面来把这个Commit 的分支Merge 到main,需要新建一个Branch,然后Check Out到main,然后把新Branch Merge 到main:
至此,这个问题解决了。
由于我并没有非常深入地学习过Git,因此我不太确定这些问题出现的具体原因。但Xcode 确实允许直接Check Out 到某一个Commit (而不是Branch),并且允许之后对它修改,然后再Commit,95%以上的开发者在这一步发现之前Check Out 错了时都会Check Out 到相应的分支以便Merge,谁知Check Out 之后就无法在Xcode 的界面里看到新的Commit 了。这很可能会导致开发者以为自己丢失了修改。
同样也因为我没有非常深入地学习过Git,很可能这整篇文章都是个错误。如果你认为我有什么写的不对的地方,欢迎你提出来。
Xcode sucks.
Hope this help.