如何将编程生成的文件列表传递给`git filter-branch`? [英] How to pass a programmatically generated list of files to `git filter-branch`?

查看:295
本文介绍了如何将编程生成的文件列表传递给`git filter-branch`?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在拆分git repo的一部分以创建新的repo,并试图使用 git filter-branch 来维护文件的历史记录被转移到新项目。我知道 - 子目录过滤器,但这不是一个好的解决方案,因为我要取出的文件不会干净地映射到一个子目录。到目前为止,我发现的最佳选择是 - index-filter ,用法如下:

  git filter-branch -f --index-filter'git read-tree --empty&& git reset -q$ {GIT_COMMIT} - <文件列表>'--prune-empty -f 

这似乎工作,但我希望能够以编程方式生成的文件列表保持,所以我可以迭代地精炼这个列表。我正在尝试获取我想保存在另一个文件中的文件列表,并将其追加到表示要为每个提交执行的命令的字符串中,如下所示:

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

不幸的是,这会导致

 致命错误标记'--prune-empty'用于文件名
之后

code>

即使只是回显文件似乎也会造成麻烦:

  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 
致命:ambiguous argument'b.txt':未知版本或路径不在工作树中。
使用' - '分隔修订版本的路径,如下所示:
'git< command> [< revision> ...] - [< file>]'

早先我也试过连接字符串:

  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 
致命:模棱两可的参数'read-tree':未知版本或路径不在工作树中。
使用' - '分隔修订版本的路径,如下所示:
'git< command> [< revision> ...] - [< file>]'

我认为这只是串接,不像我期望的那样在shell中发生。有谁知道我该如何做这项工作?如果你能解释这些错误是什么意思,那将是非常好的。谢谢。

解决方案 ...- filter 需要成为一个单一的字符串。该字符串保存为shell变量:

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

在适当的位置,filter-branch脚本(可在 git -core 子目录,例如 / usr / libexec / git-core / usr / local / libexec / git-核心)做到这一点:

  eval$ filter_index< / dev / null || 
dieindex 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} - <文件列表>'\
--prune-empty -f

(当你有一个静态列表的时候它就起作用)并修改它以从〜/ to_keep.txt 中提取动态列表。我将原件部分分为三行,用于展示目的,但也因为我们现在可以集中在中间线上。



<编辑修复注释中提到的换行符问题。让我们来创建一个别名或shell函数, xc ,将换行符转换为空格]

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

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)\ 

或者,正如你尝试过的(但是有一个改变):

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

设置 tmp = $(xc <〜/ to_keep.txt))。



请注意,如果任何文件名包含空白的话。例如,假设文件名为文件(内嵌空白)。 eval 会在空格处中断参数,而 git reset 命令会得到名称 a 和文件作为两个独立的参数。



只要你没有任何这样的文件名,你不必担心解决这个问题。



另一个潜在的问题是如果这个文件列表变得很长。您可能会遇到可以发送到一个文件的参数数目的内核限制。你应该可以使用 xargs 来解决这个问题(对于这个问题,通过一些工作和使用 -0 ,以处理文件名中的空格)。


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.

解决方案

Each argument to the various ...-filters needs to be a single string. That string is saved as a shell variable:

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

At the appropriate point, the filter-branch script (found in the git-core subdirectory, e.g., /usr/libexec/git-core or /usr/local/libexec/git-core) does this:

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

(except for the commit-filter which is run with /bin/sh -c "$filter_commit" ...).

Your assumption is thus correct, and what you need is to make the list of files be part of a single, white-space-separated string.

The easiest way to do this would be to start with your original command:

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

(which works when you have a static list) and modify it to extract the dynamic list from ~/to_keep.txt. I split the original into three lines partly for display purposes, but also because we can now concentrate just on the middle line.

[Edit to fix newline issue noted in comment. Let's make an alias or shell function, xc, that translates newlines to spaces]

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

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

or:

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

or, as you attempted (but with one change):

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

(having set tmp=$(xc < ~/to_keep.txt)).

Note that none of this correct things if any of the file names contains white space. For instance, suppose a file is named a file (with embedded blank). The eval will break arguments at spaces, and the git reset command will get the names a and file as two separate arguments.

As long as you don't have any such file names, you need not worry about addressing this.

One other potential problem is if this list of files gets very long. You may run into kernel limits on the number of arguments that can be sent to one file. You should be able to use xargs to solve this (and, for that matter, with some work and use of -0, to handle white-space in file names).

这篇关于如何将编程生成的文件列表传递给`git filter-branch`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆