2

I'm splitting off part of a git repo to create a new repo, and am trying to use git filter-branch to maintain the history of the files that are being moved to the new project. I know about --subdirectory-filter but this is not a good solution because the files I'm pulling out don't map cleanly to one subdirectory. The best option I've found so far is --index-filter, used as follows:

git filter-branch -f --index-filter 'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- <list of files>' --prune-empty -f

This seems to work, except I'd like to be able to programmatically generate the list of files to keep so I can iteratively refine this list. I'm currently trying to get a list of the files I want to keep in another file, and append this to the string representing the command to be executed for each commit as follows:

tmp=$(cat ~/to_keep.txt) && git filter-branch -f --index-filter 'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- '$tmp --prune-empty -f

Unfortunately, this results in

fatal: bad flag '--prune-empty' used after filename

Even just echoing the files seems to cause trouble:

tmp=$(echo a.txt b.txt) && git filter-branch -f --index-filter 'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- '$tmp --prune-empty -f
fatal: ambiguous argument 'b.txt': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

I've also tried concatenating the strings earlier:

tmp1=$(echo a.txt b.txt) && tmp2='git read-tree --empty && git reset -q "${GIT_COMMIT}" -- ' && tmp3=${tmp2}${tmp1} && git filter-branch -f --index-filter $tmp3 --prune-empty -f
fatal: ambiguous argument 'read-tree': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

I assume this is just concatenation not happening as I expect in the shell. Does anyone know how I can make this work? It would be great if you could explain what these errors mean, as well. Thanks.

4

1 に答える 1

3

さまざまな への各引数は...-filter、単一の文字列である必要があります。その文字列はシェル変数として保存されます。

    --index-filter)
            filter_index="$OPTARG"
            ;;

適切な時点で、フィルタ ブランチ スクリプト (またはgit-coreサブディレクトリにある) は次のことを行います。/usr/libexec/git-core/usr/local/libexec/git-core

    eval "$filter_index" < /dev/null ||
            die "index filter failed: $filter_index"

(で実行される commit-filter を除く/bin/sh -c "$filter_commit" ...)。

したがって、あなたの仮定は正しく、ファイルのリストを空白で区切られた単一の文字列の一部にする必要があります。

これを行う最も簡単な方法は、元のコマンドから開始することです。

git filter-branch -f --index-filter \
    'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- <list of files>' \
    --prune-empty -f

(静的リストがある場合に機能します) から動的リストを抽出するように変更し~/to_keep.txtます。見やすくするためと、中央の行だけに集中できるようにするため、オリジナルを 3 行に分割しました。

[コメントに記載されている改行の問題を修正するために編集します。xc改行をスペースに変換するエイリアスまたはシェル関数を作成しましょう]

xc() {
    tr '\n' ' '
}

"git read-tree --empty && git reset -q \"\${GIT_COMMIT}\" -- $(xc < ~/to_keep.txt)" \

また:

'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- '"$(xc < ~/to_keep.txt)" \

または、あなたが試みたように(ただし、1つの変更があります):

'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- '"$tmp" \

(設定済みtmp=$(xc < ~/to_keep.txt))。

ファイル名のいずれかに空白が含まれている場合、これは正しくないことに注意してください。たとえば、ファイルに名前が付けられているとしますa file(空白が埋め込まれています)。はeval引数をスペースで区切り、git resetコマンドは名前afileを 2 つの別個の引数として取得します。

そのようなファイル名がない限り、これに対処することについて心配する必要はありません。

もう 1 つの潜在的な問題は、このファイルのリストが非常に長くなった場合です。1 つのファイルに送信できる引数の数がカーネルの制限に達する場合があります。これを解決するために を使用できるはずです(さらに言えば、ファイル名の空白を処理xargsするために を使用して作業することもできます)。-0

于 2013-10-22T01:34:10.143 に答える