Chỉ xuất các tệp đã sửa đổi và thêm vào có cấu trúc thư mục trong Git


82

Tôi muốn nhận danh sách các tệp đã sửa đổi và thêm vào trong một cam kết cụ thể để tôi có thể xuất chúng và tạo một gói có cấu trúc tệp.

Ý tưởng là lấy gói và giải nén nó trên máy chủ. Vì nhiều lý do, tôi không thể tạo hook để kéo repo tự động và cách dễ nhất mà tôi phải cập nhật máy chủ là tạo gói này.

Câu trả lời:


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

    Thực hiện một sự khác biệt của cam kết đã cho cho (các) mẹ của nó (bao gồm tất cả các thư mục con, không chỉ thư mục trên cùng).

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

    Không xuất SHA1 cam kết. Chỉ xuất tên của các tệp bị ảnh hưởng thay vì một khác biệt đầy đủ.

  3. --diff-filter=ACMRT:

    Chỉ hiển thị các tệp được thêm, sao chép, sửa đổi, đổi tên hoặc đã thay đổi loại của chúng (ví dụ: tệp → liên kết biểu tượng) trong cam kết này. Điều này loại bỏ các tệp đã bị xóa.


CẬP NHẬT TỪ BÌNH LUẬN:
Căn cứ vào ngữ cảnh câu hỏi và nhận xét bên dưới, với lệnh sau, bạn có thể lấy các ACMRTtệp dưới dạng .tartệp với cấu trúc thư mục của chúng.

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

Bây giờ tôi chỉ cần tìm một cách để gắn kết quả của lệnh này với tar! Cảm ơn bạn!
Michael Kuhinica

8
@Taverneiro Bạn có thể chuyển nó đến lệnh tar được đưa ra trong câu trả lời này , ví dụ. cho một tập tin tgz:git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -
Matthieu Sadouni

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

Chỉ để làm tròn điều này, đây là lệnh được đưa vào tar. Điều này xuất các tệp thành một kho lưu trữ tar.


1
Điều này có thể được thực hiện cho nhiều cam kết vào một tệp tar không?
Evolutionxbox

4
Khi cố gắng thêm nhiều cam kết vào cùng một tệp tar, đôi khi tar tạo lại filename.ext với tên khác (filename1.ext). Tôi nhận ra rằng nếu tôi sử dụng zip, tôi có thể thêm và ghi đè lên file nếu nó tồn tại trong zip ... Bạn chỉ cần thay thế các xargs với xargs nén -r0 myzipfile.zip $ 1
Ricardo Martins

Có cần xargs ở đây không? Tôi biết nó dành cho những thứ như rm, nhưng tôi nghĩ tar có thể xử lý nhiều tệp như bạn muốn.
Erdal G.

1
Hãy cẩn thận với câu trả lời này, điều này có các tệp khác nhưng là phiên bản hiện tại của nhánh hiện tại
mems

1
Trong khi thực hiện lệnh này trong máy nô lệ windows, tôi gặp lỗi sau "'xargs' không được nhận dạng là lệnh nội bộ hoặc lệnh bên ngoài, chương trình có thể hoạt động hoặc tệp hàng loạt".
Navin prasad

40

Đây là lệnh một dòng hoạt động trên Windows 7. Chạy nó từ thư mục cấp cao nhất của kho lưu trữ của bạn.

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"

  • echo FA trả lời câu hỏi xcopy không thể tránh khỏi về việc bạn đang sao chép một tệp hay một thư mục (tệp) và câu hỏi có thể về việc ghi đè tệp (ghi đè lên Tất cả)
  • usebackq cho phép chúng tôi sử dụng đầu ra từ lệnh git của chúng tôi làm đầu vào cho mệnh đề do của chúng tôi
  • HEAD ~ 1 HEAD nhận được tất cả sự khác biệt giữa cam kết trước đó và HEAD hiện tại
  • % ~ fA chuyển đầu ra git thành các đường dẫn đủ điều kiện (cần thiết để thay đổi dấu gạch chéo về phía trước thành dấu gạch chéo ngược)
  • C: \ git_changed_files \ là nơi bạn sẽ tìm thấy tất cả các tệp khác nhau

1
Khi tôi nhìn thấy mớ ký hiệu hỗn độn đó, tôi đã không mong đợi nó hoạt động mà không cần chỉnh sửa nhiều ... nhưng nó đã hoạt động trong lần thử đầu tiên, cảm ơn.
Nathan Fig.

3
Lưu ý một chút cho tất cả những người (như tôi) không quen thuộc với các tệp loạt của Windows: Nếu bạn muốn sử dụng lệnh trên trong một tệp lô, bạn sẽ cần thay% A bằng %% A trong vòng lặp for
buddybubble,

1
Gì? CƯỜI LỚN. Chỉ cần sử dụng git archive - git diff combo (câu trả lời của Nicolas). Dễ dàng và đơn giản hơn nhiều. (Thực lòng tôi không biết chuyện gì đang xảy ra ở đây.)
ADTC

1
Cảnh báo: điều này sẽ sao chép tập tin từ bản sao làm việc của bạn, mà có thể không thực sự phù hợp với những gì trong HEAD (hoặc bất cứ cam kết băm bạn thay thế nó bằng)
Simon Đông

1
Công cụ tuyệt vời, nhưng chỉ hoạt động với CMD không phải Powershell, -v ° v-
Rodion Bykov

33

nếu băm cam kết của bạn là ví dụ a9359f9, lệnh này:

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

sẽ trích xuất các tệp được sửa đổi trong cam kết và đặt chúng vào patch.zip trong khi vẫn giữ nguyên cấu trúc thư mục dự án.

hơi dài dòng, băm cam kết được đề cập ba lần, nhưng nó có vẻ hiệu quả với tôi.

lấy nó ở đây: http://tosbourn.com/2011/05/git/using-git-to-create-an-archive-of-changed-files/


Tôi nghĩ rằng tôi có thể sử dụng functiontrong PowerShell để giảm lệnh thành chỉ một tên hàm và sử dụng $argsđể chuyển vào hàm băm cam kết chỉ một lần. Trong * nix, bạn có thể sử dụng shell-scripting để đạt được hiệu quả tương tự.
ADTC

1
Người đàn ông ... Tôi sở hữu cho bạn một ly bia :)
deanpodgornik

3
@BenSewards cho phạm vi cam kết giữa commit1 và commit2 (bao gồm cam kết sau, không bao gồm cam kết trước đó; thứ tự không quan trọng) chỉ cần làm git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1 commit2). Cam kết được chuyển cho archivelệnh có thể là bất kỳ cam kết tùy ý nào (rất có thể là HEAD hoặc cam kết sau đó) và các tệp được kéo như thể chúng được kiểm tra ở giai đoạn cam kết đó. Các commit1commit2 truyền cho diffchỉ được sử dụng để tạo ra danh sách các tập tin cần kéo - họ không ảnh hưởng đến phiên bản của file kéo.
ADTC

1
Mẹo bổ sung: Nếu bạn muốn kết hợp các tệp đã thay đổi của nhiều phạm vi cam kết vào một kho lưu trữ, chỉ cần lặp lại difflệnh với dấu chấm phẩy: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 23/11/15

4
Do khoảng trắng trong tên đường dẫn, tôi phải sử dụng một đường ống để xargs xử lý NULgit diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD
Boggin

25

Bạn có thể xuất khác nhau bằng Tortoise Git sang MS Windows:

Tôi nhấp chuột phải và chọn TortoiseGit > Hiển thị Nhật kýThông báo Nhật ký sẽ được mở.

Chọn hai bản sửa đổi và so sánh nó. Sự khác biệt giữa sẽ được mở.

Chọn tệp và xuất lựa chọn sang ... vào một thư mục!

nhập mô tả hình ảnh ở đây


1
Chắc chắn. Rất nhiều câu trả lời cho câu hỏi này chỉ có các lệnh linux và rất nhiều câu trả lời không nắm bắt được các thay đổi đang hoạt động, mà chỉ có các thay đổi đã được cam kết. Rất vui vì điều này đã được đăng!
dythim 17/07/19

8

Tôi cần cập nhật máy chủ thử nghiệm của mình và thêm các tệp đã thay đổi kể từ phiên bản 2.1.
Đối với tôi, giải pháp tương tự như James Ehly đã đăng, nhưng trong trường hợp của tôi, tôi muốn xuất sang gói lưu trữ sự khác biệt giữa hai thẻ cũ hơn - tag_ver_2.1 và tag_ver_2.2 không phải là thẻ duy nhất.

Ví dụ:

tag_ver_2.1 = 1f72b38ad
tag_ver_2.2 = c1a546782

Đây là ví dụ đã sửa đổi:

git diff-tree -r --no-commit-id --name-only c1a546782 1f72b38ad | xargs tar -rf test.tar

Tôi vừa phát hiện một vấn đề WEIRD: nếu tôi thử mã ở trên, nó hoạt động như một sự quyến rũ. Nhưng tôi thay đổi tar -rf test.tar cho tar -zcf test.tar.gz, thì tệp kết quả bị thiếu một số tệp. Điều gì có thể gây ra sự khác biệt trong kết quả?
Alberto Alexander Rodriguez

Trong khi thực hiện lệnh này trong máy nô lệ windows, tôi gặp lỗi sau "'xargs' không được nhận dạng là lệnh nội bộ hoặc lệnh bên ngoài, chương trình có thể hoạt động hoặc tệp hàng loạt".
Navin prasad

4

Các lệnh dưới đây phù hợp với tôi.

Nếu bạn muốn sự khác biệt của các tệp được thay đổi bởi lần cam kết cuối cùng:

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

hoặc nếu bạn muốn có sự khác biệt giữa hai cam kết cụ thể:

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

hoặc nếu bạn có các tệp chưa được cam kết, hãy nhớ git cách là cam kết mọi thứ, các chi nhánh rất rẻ:

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

Tôi đã tạo một tập lệnh php để xuất các tệp đã thay đổi trên Windows. Nếu bạn có một máy chủ phát triển localhost với php được thiết lập thì bạn có thể chạy nó một cách dễ dàng. Nó sẽ ghi nhớ kho lưu trữ cuối cùng của bạn và xuất luôn vào cùng một thư mục. Thư mục xuất luôn được làm trống trước khi xuất. Bạn cũng sẽ thấy các tệp đã xóa có màu đỏ để bạn biết những gì cần xóa trên máy chủ.

Đây chỉ là hai tập tin nên tôi sẽ đăng chúng ở đây. Giả sử các kho lưu trữ của bạn được đặt dưới c: / www trong các thư mục riêng của chúng và http: // localhost cũng trỏ đến c: / www và được bật php. Hãy đặt 2 tệp này trong 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

Và bây giờ tải localhost / git-export / trong trình duyệt. Tập lệnh được thiết lập để xuất luôn sang c: / www / export - thay đổi tất cả các đường dẫn cho phù hợp với môi trường của bạn hoặc sửa đổi tập lệnh cho phù hợp với nhu cầu của bạn.

Điều này sẽ hoạt động nếu bạn đã cài đặt Git để lệnh git nằm trong PATH của bạn - điều này có thể được cấu hình khi bạn chạy trình cài đặt windows Git.


mã này thật tuyệt, nhưng tôi muốn xuất các tệp và thư mục với mô-đun con, mã của bạn chỉ hoạt động với kho lưu trữ chính và nếu mô-đun con thay đổi mã của bạn, hãy nói rằng thư mục mô-đun con đã bị xóa!
Morteza khadem

1

Để xuất các tệp đã sửa đổi bắt đầu bằng một ngày:

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

Phím tắt (sử dụng bí danh)

  git exportmdf 2016-11-01 11.tar

Bí danh trong .gitconfig

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

0

Đây là một tập lệnh bash (Unix) nhỏ mà tôi đã viết sẽ sao chép các tệp cho một băm cam kết nhất định với cấu trúc thư mục:

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

Tạo một tệp có tên '~ / Scripts / copy-commit.sh' sau đó cấp cho nó các đặc quyền thực thi:

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

Sau đó, từ gốc của kho lưu trữ git:

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

0

Tôi cũng gặp phải một vấn đề tương tự trước đây. Tôi đã viết một kịch bản Shell đơn giản.

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

Lệnh trên sẽ hiển thị commit Hash (Bản sửa lại) từ commit_HashX thành commit_HashY theo thứ tự ngược lại.

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

Bây giờ là Shell Script chính bằng lệnh trên.

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

Thêm mã này vào export_changes.sh. Bây giờ chuyển tệp và cam kết băm cho tập lệnh.

Đảm bảo rằng commit_hash ban đầu phải là đối số đầu tiên rồi đến commit_hash cuối cùng mà bạn muốn xuất các thay đổi.

Thí dụ:

$ sh export_changes.sh hashX hashY

Đặt tập lệnh này vào thư mục cục bộ git hoặc đặt đường dẫn thư mục cục bộ git trong tập lệnh. Hy vọng nó giúp..!


0

Điều này phù hợp với tôi cho cả Unix và Windows:

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

Có hai trình giữ chỗ trong lệnh. Bạn cần thay thế chúng cho mục đích của mình:

  • __1__ : thay thế bằng id cam kết của cam kết ngay trước tất cả các cam kết mà bạn muốn xuất (ví dụ: 997cc7b6 - đừng quên giữ lại hai lần đó sau id cam kết - điều này có nghĩa là "liên quan đến tất cả các cam kết mới hơn cam kết này")

  • __2__ : thay thế bằng đường dẫn hiện có mà bạn muốn xuất tệp của mình (ví dụ: ../export_path/)

Kết quả là bạn sẽ nhận được các tệp của mình trong một cấu trúc thư mục (không có khóa / tars ...) vì ai đó có thể đã quen với việc sử dụng các tệp xuất svn rùa trong kho lưu trữ svn.

Ví dụ, điều này khá hữu ích khi bạn muốn thực hiện triển khai thủ công các tệp đã thêm / sửa đổi của một vài lần cam kết cuối cùng. Vì vậy, bạn có thể chỉ cần sao chép các tệp này thông qua ứng dụng khách ftp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.