Git 学习
 Git 学习 
 git filter-branch
参考: 处理.git文件夹过大出现臃肿问题-filter-branch和BFG工具
- 使用git filter-branch清理历史记录
 filter-branch文档1 2 3 4 5 6 7 8 9 $ git verify-pack -v .git/objects/pack/pack-xxx.idx | sort -k 3 -n | tail -3 # 查看大文件信息 > 73671b13992abba02a7fa56d37735d4ac01803b1 blob 62992368 62936889 132214388 git rev-list --objects --all | grep 73671b13992abba02a7fa56d37735d4ac01803b1 # 查看大文件的路径 git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}')" # 查询前10个大文件 git filter-branch --force --index-filter "git rm -rf --cached --ignore-unmatch 文件名" --prune-empty --tag-name-filter cat -- --all # 在所有分支上删除该文件 git filter-branch --force --index-filter "git rm -rf --cached --ignore-unmatch 文件名" --prune-empty --tag-name-filter cat -- HEAD # 只在当前分支删除该文件 
- 删除和重建索引1 2 3 4 git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin # 删除原始备份 git reflog expire --expire=now --all #所有未关联对象过期时间为现在, 默认为90天 git gc --aggressive --prune=now # 通过gc清理文件并优化本地存储库 git push --all --force origin # 强制提交 
- 检查结果1 2 du -sh .git # 查看.git文件夹大小 git count-objects -vH # 查看.git文件夹大小 
git 原理
参考: Understanding Git Filter-branch and the Git Storage Model
git show 能够查看每个 commit 的内容, 底层使用了 deltas in packfiles 和 copy-on-write forking, 但是实际上每次 commit 均保存了整个 repo 的所有内容(snapshot). 这个 snapshot 可以用 git cat-file -p COMMIT_ID 来查看, 也可以用 git cat-file -p COMMIT_ID:PATH/TO/FILE 来查看某个文件的内容.
 snapshot 记录了所有文件的内容, 但是并没有记录文件名, 而是用一个 tree 对象来记录文件名和文件内容的对应关系. 每个存储的文件都经过哈希处理, 并且以压缩格式存储, 按哈希值编制索引. 如
1
2
3
4
5
6
7
8
$ git cat-file -p ec7cd2821d4bcbafe08f3eca6ea60487bfdc1b52
100644 blob 5d1d2bb79e1521b28dd1b8ff67f9b04f38d83620    index.md
040000 tree b7160f7d05d5b5bfe28bad029b1b490e310cff22    redirects
040000 tree d5672dd9ef15adcd1527813df757847d745e299a    second-edition
$ git cat-file -p d219be3c5010f64960ddb609a849fc42a01ad31b # the tree
100644 blob 5d1d2bb79e1521b28dd1b8ff67f9b04f38d83620    index.md
040000 tree b7160f7d05d5b5bfe28bad029b1b490e310cff22    redirects
040000 tree d48b2e06970cf3a6ae65655c340922ae69723989    second-edition
只有 second-edition 的 hash 不同, 说明两个提交之间该文件发生了修改.
- worktree: 工作区, 也就是我们平时看到的文件夹
- index: 暂存区, 也就是我们平时用 git add添加的文件
- HEAD: 当前分支的最新提交
filter-repo 工具
repo: newren/git-filter-repo
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
import git_filter_repo as fr
# git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -20 | awk '{print$1}')" | cut -d " " -f 2 | sort -u
# git cat-file -p COMMIT_ID:PATH/TO/FILE | git hash-object -w --stdin
filenames_m = {
    b"ECG_routine/inference_model/models/single_channel/class_model.pb": r"D:/code/ecg_routine/models/single_channel/class_model.pb",
    b"ECG_routine/inference_model/models/single_channel/cylen_model.pb": r"D:/code/ecg_routine/models/single_channel/cylen_model.pb",
}
filenames_r = [
    ["ECG_routine/inference_model/models/single_TU_model.pb", "ECG_routine/inference_model/models/single_T_model.pb"],
    ["ECG_routine/inference_model/models/single_cylen_model.pb", "ECG_routine/inference_model/models/single_channel/cylen_model.pb"],
]
filenames_d = []
modify_blob = {k: [] for k in filenames_m}
def get_blobid(commit: fr.Commit, _): # 获取文件的所有历史blob_id
    for file_change in commit.file_changes:
        if file_change.filename in modify_blob:
            modify_blob[file_change.filename].append(file_change.blob_id)
def set_blob(blob: fr.Blob, _): # 将文件的所有blob_id都设置为同一个, 即最后一个
    for k, v in modify_blob.items():
        if blob.original_id in v[:-1]:
            blob.original_id = v[-1]
            with open(filenames_m[k], "rb") as f:
                blob.data = f.read()
            break
args = fr.FilteringOptions.parse_args(["--force", "--refs", "refs/heads/backup", "--invert-paths"] + " ".join([f"--path-rename {i[1]}:{i[1]}.new" for i in filenames_r]).split(" ") + " ".join([f"--path-rename {i[0]}:{i[1]}" for i in filenames_r]).split(" ") + " ".join([f"--path-rename {i[1]}.new:{i[1]}" for i in filenames_r]).split(" "))
fr.RepoFilter(args).run() # 将更改文件名的提交从一开始就更改文件名
args = fr.FilteringOptions.parse_args(["--force", "--refs", "refs/heads/backup2"])
fr.RepoFilter(args, commit_callback=get_blobid).run()
args = fr.FilteringOptions.parse_args(["--force", "--refs", "refs/heads/backup2"])
fr.RepoFilter(args, blob_callback=set_blob).run()
git command
比较文件在不同 commit 下的差异
排除所有空格导致的差异
1
$ git diff -w -b $COMMIT_1 $COMMIT_2 -- $FILE
 本文由作者按照  CC BY 4.0  进行授权