在Git中仅导出具有文件夹结构的已修改和已添加文件


82

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

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

Answers:


106
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 -

现在,我只需要找到一种方法即可将此命令的结果与tar粘合在一起!谢谢!
Michael Kuhinica 2011年

8
@Taverneiro您可以将其传递到此答案中给定tar命令,例如。的tgz文件:git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -
Matthieu Sadouni 2014年

67
git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | xargs tar -rf mytarfile.tar

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


1
可以对一个tar文件进行多次提交吗?
Evolutionxbox

4
尝试将多个提交添加到同一tar文件时,有时tar会再次使用其他名称(filename1.ext)创建filename.ext。我意识到,如果我使用zip,我可以添加并覆盖该文件(如果该文件存在于zip中)...您只需要用xargs zip替换xargs zip -r0 myzipfile.zip $ 1
Ricardo Martins

这里需要xargs吗?我知道它适用于rm之类的东西,但我认为tar可以处理所需数量的文件。
Erdal G.

1
请谨慎处理此答案,它使用diff文件,但作为当前分支的当前版本
mems

1
在Windows从属计算机中执行此命令时,出现以下错误“'xargs'无法识别为内部或外部命令,可操作程序或批处理文件”。
纳文·普拉萨德

40

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

/ f“ usebackq tokens = *”%A在(`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 \是找到所有不同文件的位置

1
当我看到那堆混乱的符号时,我并不希望它能在不进行大量调整的情况下工作。
内森·

3
对于所有对Windows批处理文件不太熟悉的人(像我一样),请多加注意:如果要在批处理文件中使用以上命令,则需要在for循环内将%A替换为%% A
buddybubble 2015年

1
什么?大声笑。只需使用git存档-git diff组合(Nicolas的答案)。更加容易和直接。(老实说,我不知道这里发生了什么。)
ADTC 2015年

1
警告:这会从您的工作副本中复制文件,但实际上可能与HEAD(或您用其替换的提交哈希)不匹配的文件
Simon East

1
很棒的东西,但是只能使用CMD而不是Powershell,-v°v-
Rodion Bykov

33

如果您的提交哈希例如是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/


我想我可以functionPowerShell中使用以将命令简化为一个函数名,并仅使用$args一次传入提交哈希。在* nix中,您可能可以使用shell脚本来达到相同的效果。
ADTC

1
伙计...我为您拥有一杯啤酒:)
deanpodgornik

3
@BenSewards用于commit1和commit2之间的提交范围(包括以后的提交,不包括以前的提交;顺序无关紧要)就可以了git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1 commit2)。传递给archive命令的提交可以是任意提交(很可能是HEAD或以后的提交),并且文件被拉出,就像在该提交阶段将其检出一样。传递给的commit1commit2diff仅用于生成要拉文件的列表-它们不影响所拉文件的版本。
ADTC

1
附加提示:如果要将多个提交范围的更改文件组合到一个存档中,只需diff使用分号重复该命令:git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1A commit1B; git diff --name-only commit2A commit2B; git diff --name-only commit3A commit3B)
ADTC 2015年

4
由于路径名中有空格,我不得不使用管道对xargs进行处理,以处理NULgit diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD
Boggin

25

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

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

选择两个修订并进行比较。两者之间的差异将是开放的。

选择文件,然后将选择导出到...到文件夹!

在此处输入图片说明


1
绝对是 如此之多的答案都只有Linux命令,而且很多都没有捕获到有效的更改,只有已提交的更改。很高兴这已发布!
dythim

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

我刚刚检测到一个奇怪的问题:如果我尝试上面的代码,它的工作原理就像一个魅力。但是我将tar -rf test.tar更改为tar -zcf test.tar.gz,然后生成的文件缺少几个文件。是什么导致结果差异?
阿尔贝托·亚历山大·罗德里格斯

在Windows从属计算机中执行此命令时,出现以下错误“'xargs'无法识别为内部或外部命令,可操作程序或批处理文件”。
纳文·普拉萨德

4

下面的命令为我工作。

如果您希望上次提交更改的文件有所不同:

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

2

我已经制作了一个PHP脚本来导出Windows上的更改文件。如果您有安装了php的localhost开发服务器,则可以轻松运行它。它将记住您的上一个存储库,并始终将其导出到同一文件夹。在导出之前,导出文件夹始终为空。您还将看到红色的已删除文件,因此您知道要在服务器上删除的内容。

这些只是两个文件,因此我将在此处发布。假设您的存储库位于其各自文件夹中的c:/ www下,并且http:// localhost也指向c:/ www并且已启用php。让我们将这两个文件放在c:/ www / git-export-

index.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安装程序时可以对其进行配置。


这段代码很棒,但是我想导出带有子模块的文件和目录,您的代码只能与主存储库一起使用,如果子模块更改了您的代码,则说子模块文件夹已删除!
morteza khadem

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"

0

这是我写的一个小的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/

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本地目录或在脚本中设置git本地目录路径。 希望能帮助到你..!


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

结果,您将把文件放到一个文件夹结构中(没有zip / tars ...),因为有人可能习惯在svn存储库中使用svn乌龟出口

例如,当您要手动部署很少提交的添加/修改的文件时,这非常有用。这样,您就可以通过ftp客户端复制这些文件了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.