如何删除不再在工作目录中的已提交文件

时间:2013-08-12 21:35:51

标签: git shell

我有一个包含许多文件的仓库,这些文件已不在工作目录中 - 已在存储库的月/年内添加和删除的文件。

我想创建一个文件,其中包含存储在提交历史中但不再需要的所有这些文件的列表,包括它们的位置..即

/web/scripts/index.php
/sql/tables.sql
...

然后我想要一个运行该文件的命令,并完全从提交历史中删除其中引用的文件,类似于git rm --cached,但是对于文件列表。

2 个答案:

答案 0 :(得分:3)

简短回答

别名David Underhill's script,然后运行(谨慎):

$ git delete `git log --all --pretty=format: --name-only --diff-filter=D`

解释

David Underhill的命令使用filter-branch修改存储库的历史记录,删除给定文件路径的所有历史记录。

该脚本完整(source):

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch otherwise leaves behind for a long time
rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune

将此脚本保存到硬盘驱动器上的某个位置(例如/path/to/deletion_script.sh),并确保其可执行文件(chmod +x /path/to/deletion_script.sh)。

然后alias the command

$ git config --global alias.delete '!/path/to/deletion_script.sh'

获得sorted list of all deleted files

$ git log --all --pretty=format: --name-only --diff-filter=D | sort -u

将所有这些结合在一起

使用已删除文件的列表,只需挂钩git delete即可处理列表中的每个文件:

$ git delete `git log --all --pretty=format: --name-only --diff-filter=D`

测试/示例用法

  1. 创建一个包含添加,重命名和删除的虚拟存储库:

    mkdir test_repo
    cd test_repo/
    git init
    echo "Dummy content" >> stays.txt
    git add stays.txt && git commit -m "First file, will stay"
    echo "Rename content" >> will_rename.txt
    git add will_rename.txt && git commit -m "Going to rename"
    echo "Delete this file" >> will_delete.txt
    git add will_delete.txt && git commit -m "Delete this file"
    git mv will_rename.txt renamed.txt && git commit -m "File renamed"
    git rm will_delete.txt && git commit -m "File deleted"
    
  2. 检查历史:

    $ git whatchanged --oneline
    d768c58 File deleted
    :100644 000000 7a4187c... 0000000... D  will_delete.txt
    96aadf0 File renamed
    :000000 100644 0000000... 94a12c7... A  renamed.txt
    :100644 000000 94a12c7... 0000000... D  will_rename.txt
    3ba05fa Delete this file
    :000000 100644 0000000... 7a4187c... A  will_delete.txt
    c88850a Going to rename
    :000000 100644 0000000... 94a12c7... A  will_rename.txt
    6db6015 First file, will stay
    :000000 100644 0000000... f3ae800... A  stays.txt
    
  3. 删除旧文件:

    $ git delete `git log --all --pretty=format: --name-only --diff-filter=D`
    Rewrite 8c2009db5ac05b27cd065482da94dec717f5ef4a (8/9)rm 'will_delete.txt'
    Rewrite e1348d588597f2f6dd63cade081e0fbdf8692c74 (9/9)
    Ref 'refs/heads/master' was rewritten
    Counting objects: 27, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (22/22), done.
    Writing objects: 100% (27/27), done.
    Total 27 (delta 12), reused 10 (delta 0)
    
  4. 立即检查存储库。请注意,删除操作已从历史记录中删除,并且重命名显示为最初以这种方式添加文件。

    c800020 File renamed
    :000000 100644 0000000... 94a12c7... A  renamed.txt
    0a729d7 First file, will stay
    :000000 100644 0000000... f3ae800... A  stays.txt
    

答案 1 :(得分:0)

添加@David的答案时,如果要格外小心,并确保不删除以后在历史记录中随后添加的任何文件,请使用以下命令块代替git delete $(git log --all --pretty=format: --name-only --diff-filter=D) (考虑将其作为函数添加到.bashrc中)

current=($(git ls-files))
tracked=($(git log --all --pretty=format: --name-only --diff-filter=D | xargs))
deleted=()
resurrected=()
for file in "${tracked[@]}"; do
if [[ " ${current[@]} " =~ " $file " ]]; then
  resurrected+=("$file")
else
  deleted+=("$file");
fi
done
echo "Deleted: ${deleted[@]}"
echo "Resurrected: ${resurrected[@]}"
git delete "${deleted[@]}"