介绍

  • Unity 会为目录生成一个对应的 .meta 文件
  • Git 不保存目录,只保存文件

问题

由于 Unity 与 Git 对待目录的不同规则,导致在协作使用时会出现一些问题:

  1. 如果一个人在 Unity 中将目录及其文件全部删除,那么 Unity 会将目录对应的 .meta 文件也删除,然后在 Git 中提交推送
  2. 其他人拉取更新后,由于 Git 并不追踪目录的改变,所以其他人本地还留有被删除的目录,因此打开 Unity 后会自动生成目录的 .meta 文件
  3. 如果其他人在提交时忘记了,又会错误地将此文件添加到 Git 并提交

注意:OS X 中使用 Finder 打开目录后会自动生成 .DS_Store 文件,Git 仓库中一般会将其添加到忽略规则中,因此只含有这个文件的目录也需要删除。

代码

只需要将以下代码保存为 Editor 目录下的脚本,然后执行菜单 Tools/Remove Empty Directories 就可以删除空目录了。

Assets/Editor/Tools/RemoveEmptyDirectories.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;

public static class RemoveEmptyDirectories
{
    const string title = "Remove Empty Directories";

    [MenuItem("Tools/" + title)]
    public static void CleanEmptyDirectories()
    {
        var di = new DirectoryInfo("Assets/");
        var dis = new List<DirectoryInfo>();

        DoRemoveEmptyDirectory(di, dis);

        if (dis.Count == 0)
        {
            EditorUtility.DisplayDialog(title, "No Empty Directory", "OK");
            return;
        }

        var sb = new System.Text.StringBuilder();
        for (int i = 0; i < dis.Count; ++i)
        {
            int index = i + 1;
            sb.AppendLine(index.ToString() + " " + dis[i].FullName);
        }

        if (EditorUtility.DisplayDialog(title, sb.ToString(), "OK", "Cancel"))
        {
            foreach (var target in dis)
            {
                if (File.Exists(target.FullName + ".meta"))
                    File.Delete(target.FullName + ".meta");

                target.Delete(true);
            }
            AssetDatabase.Refresh();
        }
    }

    public static bool DoRemoveEmptyDirectory(DirectoryInfo target, List<DirectoryInfo> dis)
    {
        bool hasDirOrFile = false;
        foreach (var di in target.GetDirectories())
        {
            bool result = DoRemoveEmptyDirectory(di, dis);
            if (result) hasDirOrFile = true;
        }

        foreach(var fi in target.GetFiles())
        {
            if (!fi.Name.StartsWith(".") && !fi.FullName.EndsWith(".meta"))
            {
                hasDirOrFile = true;
            }
        }

        if (hasDirOrFile == false)
        {
            if (dis.Contains(target) == false)
                dis.Add(target);
        }

        return hasDirOrFile;
    }
}