90

我想在特定提交中获取修改和添加的文件列表,以便我可以导出它们并生成具有文件结构的包。

这个想法是获取包并将其提取到服务器上。由于许多原因,我无法创建一个挂钩来自动提取 repo,而我必须保持服务器更新的最简单方法是生成这个包。

4

13 回答 13

117
git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id
  1. git diff-tree -r $commit_id

    将给定提交的差异与其父级(包括所有子目录,而不仅仅是顶级目录)进行比较。

  2. --no-commit-id --name-only

    不要输出提交 SHA1。仅输出受影响文件的名称而不是完整的差异。

  3. --diff-filter=ACMRT

    仅显示此提交中添加、复制、修改、重命名或类型更改的文件(例如文件→符号链接)。这会忽略已删除的文件。


评论更新:
根据问题上下文和下面的评论,使用以下命令,您可以将ACMRT文件作为具有.tar文件夹结构的文件获取。

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -
于 2010-12-27T21:06:03.627 回答
70
git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | xargs tar -rf mytarfile.tar

只是为了解决这个问题,这里是通过管道传输到 tar 的命令。这会将文件导出到 tar 存档中。

于 2011-09-24T05:01:30.070 回答
41

这是适用于 Windows 7 的单行命令。从存储库的顶级文件夹中运行它。

for /f "usebackq tokens=*" %A in (`git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT HEAD~1 HEAD`) 做 echo FA|xcopy "%~fA" "C:\git_changed_files\%A"

  • echo FA回答了关于您是在复制文件还是目录(文件)的不可避免的 xcopy 问题,以及关于覆盖文件(覆盖全部)的可能问题
  • usebackq允许我们使用 git 命令的输出作为 do 子句的输入
  • HEAD~1 HEAD获取上一次提交和当前 HEAD 之间的所有差异
  • %~fA将 git 输出转换为完全限定的路径(需要将正斜杠更改为反斜杠)
  • C:\git_changed_files\是您可以找到所有不同文件的地方
于 2012-04-22T20:57:28.817 回答
37

如果您的提交哈希是例如 a9359f9,则此命令:

git archive -o patch.zip a9359f9 $(git diff --name-only a9359f9^..a9359f9)

将提取提交中修改的文件并将它们放在 patch.zip 中,同时保持项目目录结构不变。

有点冗长,提交哈希被提到了三遍,但它似乎对我有用。

在这里得到它:http: //tosbourn.com/2011/05/git/using-git-to-create-an-archive-of-changed-files/

于 2013-12-19T15:56:40.547 回答
31

您可以使用Tortoise Git将差异导出到 MS Windows:

我右键单击并选择TortoiseGit >显示日志日志消息将打开。

选择两个修订版并进行比较。之间的差异将被打开。

选择文件并将选择导出到...到一个文件夹!

在此处输入图像描述

于 2016-10-29T16:32:14.327 回答
8

我需要更新我的测试服务器并添加自 2.1 版以来更改的文件。
对我来说,与 James Ehly 发布的解决方案类似,但在我的情况下,我想将两个旧标签之间的差异导出到存档包 - tag_ver_2.1 和 tag_ver_2.2 不是唯一的一次提交。

例如:

tag_ver_2.1 = 1f72b38ad
tag_ver_2.2 = c1a546782

这里是修改示例:

git diff-tree -r --no-commit-id --name-only c1a546782 1f72b38ad | xargs tar -rf test.tar
于 2015-02-13T14:38:34.517 回答
7

下面的命令对我有用。

如果您想要上次提交更改的文件的差异:

git archive -o update.zip HEAD $(git diff --name-only HEAD HEAD^)

或者如果您想要两个特定提交之间的差异:

git archive -o update.zip sha1 $(git diff --name-only sha1 sha2)

或者如果您有未提交的文件,请记住 git 方式是提交所有内容,分支很便宜:

git stash
git checkout -b feature/new-feature
git stash apply
git add --all
git commit -m 'commit message here'
git archive -o update.zip HEAD $(git diff --name-only HEAD HEAD^)
于 2018-09-24T12:45:18.190 回答
2

我制作了一个 php 脚本来导出 Windows 上更改的文件。如果您有一个设置了 php 的 localhost 开发服务器,那么您可以轻松运行它。它会记住您的最后一个存储库并始终导出到同一个文件夹。导出文件夹总是在导出前清空。您还将看到已删除的文件以红色显示,因此您知道要在服务器上删除什么。

这些只是两个文件,所以我将它们发布在这里。假设您的存储库位于它们自己的文件夹中的 c:/www 下,并且http://localhost也指向 c:/www 并且启用了 php。让我们把这两个文件放在 c:/www/git-export -

索引.php:

<?php
/* create directory if doesn't exist */
function createDir($dirName, $perm = 0777) {
    $dirs = explode('/', $dirName);
    $dir='';
    foreach ($dirs as $part) {
        $dir.=$part.'/';
        if (!is_dir($dir) && strlen($dir)>0) {
            mkdir($dir, $perm);
        }
    }
}

/* deletes dir recursevely, be careful! */
function deleteDirRecursive($f) {

    if (strpos($f, "c:/www/export" . "/") !== 0) {
        exit("deleteDirRecursive() protection disabled deleting of tree: $f  - please edit the path check in source php file!");
    }

    if (is_dir($f)) {
        foreach(scandir($f) as $item) {
            if ($item == '.' || $item == '..') {
                continue;
            }

            deleteDirRecursive($f . "/" . $item);
        }    
        rmdir($f);

    } elseif (is_file($f)) {
        unlink($f);
    }
}

$lastRepoDirFile = "last_repo_dir.txt";
$repo = isset($_POST['repo']) ? $_POST['repo'] : null;


if (!$repo && is_file($lastRepoDirFile)) {
    $repo = file_get_contents($lastRepoDirFile);
}

$range = isset($_POST['range']) ? $_POST['range'] : "HEAD~1 HEAD";


$ini = parse_ini_file("git-export.ini");

$exportDir = $ini['export_dir'];
?>

<html>
<head>
<title>Git export changed files</title>
</head>

<body>
<form action="." method="post">
    repository: <?=$ini['base_repo_dir'] ?>/<input type="text" name="repo" value="<?=htmlspecialchars($repo) ?>" size="25"><br/><br/>

    range: <input type="text" name="range" value="<?=htmlspecialchars($range) ?>" size="100"><br/><br/>

    target: <strong><?=$exportDir ?></strong><br/><br/>

    <input type="submit" value="EXPORT!">
</form>

<br/>


<?php
if (!empty($_POST)) {

    /* ************************************************************** */
    file_put_contents($lastRepoDirFile, $repo); 

    $repoDir = $ini['base_repo_dir'] ."/$repo";
    $repoDir = rtrim($repoDir, '/\\');

    echo "<hr/>source repository: <strong>$repoDir</strong><br/>";
    echo "exporting to: <strong>$exportDir</strong><br/><br/>\n";


    createDir($exportDir);

    // empty export dir
    foreach (scandir($exportDir) as $file) {
        if ($file != '..' && $file != '.') {
            deleteDirRecursive("$exportDir/$file");
        }
    }

    // execute git diff
    $cmd = "git --git-dir=$repoDir/.git diff $range --name-only";

    exec("$cmd 2>&1", $output, $err);

    if ($err) {
        echo "Command error: <br/>";
        echo implode("<br/>", array_map('htmlspecialchars', $output));
        exit;
    }


    // $output contains a list of filenames with paths of changed files
    foreach ($output as $file) {

        $source = "$repoDir/$file";

        if (is_file($source)) {
            if (strpos($file, '/')) {
                createDir("$exportDir/" .dirname($file));
            }

            copy($source, "$exportDir/$file");
            echo "$file<br/>\n";

        } else {
            // deleted file
            echo "<span style='color: red'>$file</span><br/>\n";
        }
    }
}
?>

</body>
</html>

git-export.ini :

; path to all your git repositories for convenience - less typing
base_repo_dir = c:/www

; if you change it you have to also change it in the php script
; in deleteDirRecursive() function - this is for security
export_dir = c:/www/export

现在在浏览器中加载 localhost/git-export/。该脚本设置为始终导出到 c:/www/export - 更改所有路径以适合您的环境或修改脚本以满足您的需要。

如果您安装了 Git 并且 git 命令位于您的 PATH 中,这将起作用 - 这可以在您运行 Windows Git 安装程序时进行配置。

于 2011-01-06T02:38:04.127 回答
2

我也有同样的问题。@NicolasDermine 的解决方案不起作用,因为在比较的提交之间更改了太多文件。我收到一个错误,即 shell 参数太长。

因此,我添加了一个 python 实现。这可以通过安装pip install gitzip然后执行

python -m gitzip export.zip <commit id> <commit id>

在存储库根目录中创建一个export.zip文件,其中包含保留目录结构的所有更改文件。

也许有人也需要它,所以我想在这里分享它。

免责声明:我是gitzip模块的作者。

于 2020-12-08T09:19:55.837 回答
1

要导出以日期开头的修改文件:

  diff --stat @{2016-11-01} --diff-filter=ACRMRT --name-only | xargs tar -cf 11.tar

快捷方式(使用别名)

  git exportmdf 2016-11-01 11.tar

.gitconfig 中的别名

  [alias]
  exportmdf = "!f() { \
    git diff --stat @{$1} --diff-filter=ACRMRT --name-only | xargs tar -cf $2; \
  }; f"
于 2016-11-28T13:34:52.123 回答
1

这是我编写的一个小型 bash(Unix) 脚本,它将使用文件夹结构复制给定提交哈希的文件:

ARRAY=($(git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $1))
PWD=$(pwd)

if [ -d "$2" ]; then

    for i in "${ARRAY[@]}"
    do
        : 
        cp --parents "$PWD/$i" $2
    done
else
    echo "Chosen destination folder does not exist."
fi

创建一个名为 '~/Scripts/copy-commit.sh' 的文件,然后赋予它执行权限:

chmod a+x ~/Scripts/copy-commit.sh

然后,从 git 存储库的根目录:

~/Scripts/copy-commit.sh COMMIT_KEY ~/Existing/Destination/Folder/
于 2018-02-23T21:53:43.693 回答
0

我之前也遇到过类似的问题。我写了一个简单的 Shell 脚本。

$git log --reverse commit_HashX^..commit_HashY --pretty=format:'%h'

上述命令将以相反的顺序显示从 commit_HashX 到 commit_HashY 的 Commit Hash(Revision)。

 456d517 (second_hash)
 9362d03
 5362d03
 226x47a
 478bf6b (six_hash)

现在使用上述命令的主要 Shell 脚本。

commitHashList=$(git log --reverse $1^..$2 --pretty=format:'%h')
for hash in $commitHashList
do
    echo "$hash"
    git archive -o \Path_Where_you_want_store\ChangesMade.zip $hash
done

将此代码添加到 export_changes.sh。现在将文件和提交哈希传递给脚本。

确保初始 commit_hash 必须是第一个参数,然后是您要导出更改的最后一个 commit_hash。

例子:

$sh export_changes.sh hashX hashY

将此脚本放入 git local 目录或在脚本中设置 git local 目录路径。 希望能帮助到你..!

于 2018-10-04T16:01:52.543 回答
0

这对我适用于 Unix 和 Windows:

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT __1__.. | xargs cp --parents -t __2__

命令中有两个占位符。您需要根据您的目的更换它们:

  • __1__ :在您要导出的所有提交之前替换为提交的提交 id (例如 997cc7b6 - 不要忘记在提交 id 之后保留该双点 - 这意味着“涉及比此提交更新的所有提交”)

  • __2__:替换为要导出文件的现有路径(例如 ../export_path/)

结果,您将在文件夹结构中获取文件(没有 zips/tars...),因为有人可能习惯于在 svn 存储库中使用tortoise svn 导出

例如,当您想要手动部署少数最后一次提交的添加/修改文件时,这非常有用。因此,您可以通过 ftp 客户端复制这些文件。

于 2019-02-28T10:32:39.303 回答