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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4541300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 09:42:47  来源:igfitidea点击:

Export only modified and added files with folder structure in Git

gitgit-diff

提问by Michael Kuhinica

I would like to get a list of modified and added files in an specific commit so that I can export them and generate a package with the file structure.

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

The idea is to get the package and extract it on the server. For many reasons I can't create a hook to pull the repo automatically and the easiest way I have to keep the server updated is generating this package.

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

回答by Aristotle Pagaltzis

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

    Take a diff of the given commit to its parent(s) (including all subdirectories, not just the top directory).

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

    Do not output the commit SHA1. Output only the names of the affected files instead of a full diff.

  3. --diff-filter=ACMRT:

    Only show files added, copied, modified, renamed or that had their type changed (eg. file → symlink) in this commit. This leaves out deleted files.

  1. git diff-tree -r $commit_id

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

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

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

  3. --diff-filter=ACMRT

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



UPDATE FROM THE COMMENT:
Base on the question context and the comments below, with the following command, you can get the ACMRTfiles as a .tarfile with their folder structure.

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

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -

回答by James Ehly

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

Just to round this out, here is the command piped to tar. This exports the files into a tar archive.

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

回答by MM.

Here is a one-line command that works on Windows 7. Run it from your repository's top-level folder.

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

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

对于 /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 FAanswers the inevitable xcopy question about whether you're copying a file or a directory (file), and the possible question about overwriting a file (overwrite All)
  • usebackqallows us to use the output from our git command as the input to our do clause
  • HEAD~1 HEADgets all differences between the previous commit and the current HEAD
  • %~fAtransforms the git output into fully qualified paths (needed to change forward slashes to backslashes)
  • C:\git_changed_files\is where you will find all the files that are different
  • echo FA回答了关于您是复制文件还是目录(文件)的不可避免的 xcopy 问题,以及关于覆盖文件(覆盖全部)的可能问题
  • usebackq允许我们使用 git 命令的输出作为 do 子句的输入
  • HEAD~1 HEAD获取上一次提交和当前 HEAD 之间的所有差异
  • %~fA将 git 输出转换为完全限定的路径(需要将正斜杠改为反斜杠)
  • C:\git_changed_files\是您可以找到所有不同文件的地方

回答by Nicolas Dermine

if your commit hash is for example a9359f9, this command :

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

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

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

will extract the files modified in the commit and place them in patch.zip while keeping the project directory structure intact.

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

a bit verbose, the commit hash is mentioned three times, but it seems to work for me.

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

got it here : http://tosbourn.com/2011/05/git/using-git-to-create-an-archive-of-changed-files/

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

回答by Wendel

You can export diff using Tortoise Gitto MS Windows:

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

I right click and select TortoiseGit> Show Logand Log Messageswill be open.

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

Select two revisions and compare it. Difference betweenwill be open.

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

Select the files and Export selection to ...to a folder!

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

enter image description here

在此处输入图片说明

回答by pietr

I needed to update my test server and add files that changed since version 2.1.
For me worked similar solution as James Ehly posted, but in my case i wanted to export to archive package of difference between two older tags - tag_ver_2.1 and tag_ver_2.2 not the only one commit.

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

For example:

tag_ver_2.1 = 1f72b38ad
tag_ver_2.2 = c1a546782

Here is modified example:

例如:

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

回答by Ashutosh Tripathy

Below commands worked for me.

以下命令对我有用。

If you want difference of the files changed by the last commit:

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

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

or if you want difference between two specific commits:

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

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

or if you have uncommitted files, remember git way is to commit everything, branches are cheap:

或者如果您有未提交的文件,请记住 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^)

回答by Lemon Juice

I have made a php script to export changed files on Windows. If you have a localhost development server with php set up then you can run it easily. It will remember your last repository and export always to the same folder. The export folder is always emptied before exporting. You will also see deleted files in red so you know what to delete on the server.

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

These are just two files so I will post them here. Let's assume your repositories are located under c:/www in their own folders and that http://localhostalso points to c:/www and is php-enabled. Let's put these 2 files in c:/www/git-export -

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

index.php :

索引.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 :

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

And now load localhost/git-export/ in a browser. The script is set up to export always to c:/www/export - change all paths to suit your environment or modify the script to suit your needs.

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

This will work if you have Git installed so that the git command is in your PATH - this can be configured when you run the windows Git installer.

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

回答by catalinp

To export modified files starting with a date:

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

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

Shortcut (use alias)

快捷方式(使用别名)

  git exportmdf 2016-11-01 11.tar

Alias in .gitconfig

.gitconfig 中的别名

  [alias]
  exportmdf = "!f() { \
    git diff --stat @{} --diff-filter=ACRMRT --name-only | xargs tar -cf ; \
  }; f"

回答by Patrick.SE

Here is a small bash(Unix) script that I wrote that will copy files for a given commit hash with the folder structure:

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

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

if [ -d "" ]; then

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

Create a file named '~/Scripts/copy-commit.sh' then give it execution privileges:

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

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

Then, from the root of the git repository:

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

~/Scripts/copy-commit.sh COMMIT_KEY ~/Existing/Destination/Folder/