2012-03-20 16 views
1

は、私がgit ls-tree -r masterを実行したとき、私はこれに似た何かを持っていると言う:Gitのツリーからblobオブジェクトを手動で削除するにはどうしたらいいですか?

100644 blob a450cb6b6371494ab4b3da450f6e7d543bfe3493 FooBar/readme.txt 
100644 blob a339338d7ad5113740244e7f7d3cbb236cb47115 Foobar/readme.txt 

どのように私はこのツリーオブジェクトから2番目ブロブを削除することができますか?

これは、git rm Foobar/readme.txtを実行するだけでPOSIXシステムで実行できると仮定しています。 Windowsで同じことをどうすればできますか?

+1

大文字と小文字を区別する問題があります。ここで問題となるのは、これらはPOSIXシステム上の2つの異なるファイルですが、Windowsシステム上の「同じ」ファイルです。つまり、これはhttp://stackoverflow.com/questions/2528589/git-windows-case-sensitive-file-names-not-handled-properlyと同じです。 – torek

+0

これは似ていますが、私は解決策を探しています一般的な意味でもあります。私は、このトリックはgitの内部でツリーオブジェクトを手動で変更することができると考えています。 – syvex

+0

本当に "ツリーを修正する"ことはできませんが、余分なものがない* new *ツリー(別のツリーなどを指す)を指す新しいコミットを構築することはできます。最も簡単な方法は、POSIXシステムにレポをクローンし、通常のgitコマンドを使用してコミットし、次にWindowsレポにコミットをフェッチすることです。 – torek

答えて

1

git filter-branch with --index-filterは、あなたが作業ツリー上ではなくインデックス上で動作しているので動作します。

+0

残念ながら、現在のものと思われるものを削除しようとします。だから 'git rm -f Foobar/readme.txt'を試しても、代わりにFooBar/readme.txtが削除されます。 – syvex

+0

もちろん、Aaahです。 Windozeとそれは大胆なファイルシステムです。あなたのためにより良い答えを見つけようとしていますが、これはやや難しいと感じています。あなたは問題なくWindowsにそのリポジトリを複製できますか? – ralphtheninja

1

[OK]をクリックして、MacOSでこれをテストしてみましょう。これは、ケースの折り畳みで同様の問題があります。

gitのすべてのバージョンが "同じ"かどうか、Windows gitが同じように動作するかどうかはわかりませんが、実際にはこのスクリプトはgit plumbingを深くする必要はありません。ls-tree -rcat-fileおよびrm --cached

スクリプトも軽度にテストされています。 (注:タブが壊れている、cmd-C/cmd-Vはタブを貼り付けたが、私はstackoverflowのためにインデントしなければならなかったので、ファイルのインデントが下に上がってしまった... ...)

#! /bin/bash 

usage() 
{ 
cat << EOF 
usage: $0 [-h] [-r] [branch] 

-h: print usage help 
-r: rename ALL colliding files to their hashes 
EOF 
} 

DO_RENAME=false 
while getopts "hr" opt; do 
case $opt in 
h) usage; exit 0;; 
r) DO_RENAME=true;; 
*) usage 1>&2; exit 1;; 
esac 
done 
shift $(($OPTIND - 1)) 

case $# in 
0) branch=HEAD;; 
1) branch=$1;; 
*) usage 
esac 

# literal tab, so that it's easily distinguished from spaces 
TAB=$(printf \\t) 

branch=$(git rev-parse --symbolic $branch) || exit 

tempfile=$(mktemp -t git-casecoll) 
trap "rm -f $tempfile; exit 0" 0 
trap "rm -f $tempfile; exit 1" 1 2 3 15 

# First, let's find out whether there *are* any file name 
# case collisions in the tree. 
git ls-tree -r $branch > $tempfile 
nfiles=$(wc -l < $tempfile | sed 's/ *//g') 
n2=$(sort "-t$TAB" -k2 -f -u $tempfile | wc -l | sed 's/ *//g') 
if [ $nfiles -eq $n2 ]; then 
echo no collisions found 
exit 0 
fi 
echo "$(($nfiles - $n2)) collision(s) found" 

# functions needed below 

# decode git escapes in pathnames 
decode_git_pathname() 
{ 
local path="$1" 
case "$path" in 
\"*\") 
    # strip off leading and trailing double quotes 
    path=${path#\"} 
    path=${path%\"} 
    # change % into %% 
    path=${path/\%/%%} 
    # and then interpret backslashes with printf 
    printf -- "$path";; 
*) 
    # not encoded, just print it as is 
    printf %s "$path";; 
esac 
} 

show_or_do_rename() 
{ 
local mode=$1 path="$(decode_git_pathname "$2")" sha1=$3 
local renamed_to="$(dirname "$path")/$sha1" 
local ftype=${mode:0:2} 

if [ $ftype != 10 ]; then 
    echo "WARNING: I don't handle $ftype files ($mode $path) yet" 
    return 1 
fi 
if $DO_RENAME; then 
    # git mv does not work, but git rm --cached does 
    git rm --cached --quiet "$path" 
    rm -f "$path" 
    git cat-file -p $sha1 > "$renamed_to" 
    chmod ${mode:2} "$renamed_to" 
    git add "$renamed_to" 
    echo "renamed: $path => $renamed_to" 
else 
    if [ $ftype != 10 ]; then 
    echo "# I don't handle extracting a $ftype file ($mode) yet" 
    else 
    echo will: mv "$path" "$renamed_to" 
    fi 
fi 
} 

# Now we have to find which ones they were, which is more difficult. 
# We still want the sorted ones with case folded, but we don't want 
# to remove repeats, instead we want to detect them as we go. 
# 
# Note that Dir/file collides with both dir/file and dir/File, 
# so if we're doing rename ops, we'll rename all three. We also 
# don't know if we're in a collision-group until we hit the second 
# entry, so the first time we start doing a collision-group, we 
# must rename two files, and from then on (in the same group) we 
# only rename one. 
prevpath="" 
prevlow="" 
prevsha= 
in_coll=false 
sort -f $tempfile | 
while IFS="$TAB" read -r info git_path; do 
    set -- $info 
    mode=$1 
    # otype=$2 -- we don't care about the object type? 
    # it should always be "blob" 
    sha1=$3 
    lowered="$(printf %s "$git_path" | tr '[:upper:]' '[:lower:]')" 
    if [ "$prevlow" = "$lowered" ]; then 
    if $in_coll; then 
     echo "  and: $prevpath vs $git_path" 
     show_or_do_rename $mode "$git_path" $sha1 
    else 
     echo "collision: $prevpath vs $git_path" 
     show_or_do_rename $mode "$prevpath" $prevsha 
     show_or_do_rename $mode "$git_path" $sha1 
     in_coll=true 
    fi 
    else 
    prevlow="$lowered" 
    prevpath="$git_path" 
    prevsha=$sha1 
    in_coll=false 
    fi 
done 

サンプルランです。私はLinuxボックスで「Windows用の悪い」レポを作って、それをMacにクローンしました。

$ git clone ... 
Initialized empty Git repository in /private/tmp/caseissues/.git/ 
remote: Counting objects: 16, done. 
remote: Compressing objects: 100% (8/8), done. 
remote: Total 16 (delta 1), reused 0 (delta 0) 
Receiving objects: 100% (16/16), done. 
Resolving deltas: 100% (1/1), done. 
$ cd caseissues 
$ git status 
# On branch master 
# Changed but not updated: 
# (use "git add <file>..." to update what will be committed) 
# (use "git checkout -- <file>..." to discard changes in working directory) 
# 
# modified: FooBar/readme.txt 
# 
no changes added to commit (use "git add" and/or "git commit -a") 
$ git-casecoll.sh 
1 collision(s) found 
collision: FooBar/readme.txt vs Foobar/readme.txt 
will: mv FooBar/readme.txt FooBar/31892d33f4a57bff0acd064be4bb5a01143dc519 
will: mv Foobar/readme.txt Foobar/591415e1e03bd429318f4d119b33cb76dc334772 
$ git-casecoll.sh -r 
1 collision(s) found 
collision: FooBar/readme.txt vs Foobar/readme.txt 
renamed: FooBar/readme.txt => FooBar/31892d33f4a57bff0acd064be4bb5a01143dc519 
renamed: Foobar/readme.txt => Foobar/591415e1e03bd429318f4d119b33cb76dc334772 
$ git status 
# On branch master 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
# renamed: FooBar/readme.txt -> FooBar/31892d33f4a57bff0acd064be4bb5a01143dc519 
# renamed: Foobar/readme.txt -> Foobar/591415e1e03bd429318f4d119b33cb76dc334772 
# 

(私はこれらのノートを固定するために自分の名前を選ぶ、この時点で、私があるため、ケースすごみの、FooBarの中に手動で小文字-INGのB、オートコンプリート、それを聞かせて、再度試してみました)

$ git mv FooBar/31892d33f4a57bff0acd064be4bb5a01143dc519 FooBar/readme_A.txt 
$ git mv FooBar/591415e1e03bd429318f4d119b33cb76dc334772 FooBar/readme_B.txt 
fatal: not under version control, source=FooBar/591415e1e03bd429318f4d119b33cb76dc334772, destination=FooBar/readme_B.txt 
$ git mv Foobar/591415e1e03bd429318f4d119b33cb76dc334772 FooBar/readme_B.txt 
$ git status 
# On branch master 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
# renamed: FooBar/readme.txt -> FooBar/readme_A.txt 
# renamed: Foobar/readme.txt -> FooBar/readme_B.txt 
# 
$ git commit -m 'fix file name case issue' 
[master 4ef3a55] fix file name case issue 
2 files changed, 0 insertions(+), 0 deletions(-) 
rename FooBar/{readme.txt => readme_A.txt} (100%) 
rename Foobar/readme.txt => FooBar/readme_B.txt (100%) 
関連する問題