Git에서 폴더 구조로 수정 및 추가 된 파일 만 내보내기


82

특정 커밋에서 수정 및 추가 된 파일 목록을 가져 와서 내보내고 파일 구조로 패키지를 생성하고 싶습니다.

아이디어는 패키지를 가져 와서 서버에서 추출하는 것입니다. 여러 가지 이유로 리포지토리를 자동으로 가져 오는 후크를 만들 수 없으며 서버를 업데이트하는 가장 쉬운 방법은이 패키지를 생성하는 것입니다.

답변:


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을 출력하지 마십시오. 전체 diff 대신 영향을받는 파일의 이름 만 출력합니다.

  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

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를 xargs zip -r0 myzipfile.zip $ 1
Ricardo Martins

여기에 xargs가 필요합니까? 나는 그것이 rm과 같은 것임을 알고 있지만 tar가 원하는만큼 많은 파일을 처리 할 수 ​​있다고 생각했습니다.
Erdal은 G.

1
이 답변에 자르,이 걸릴은 diff 파일을 수 있지만, 현재 지점의 현재 버전으로
MEMS

1
Windows 슬레이브 시스템에서이 명령을 실행하는 동안 " 'xargs'가 내부 또는 외부 명령, 작동 가능한 프로그램 또는 배치 파일로 인식되지 않습니다."라는 오류가 발생합니다.
Navin

40

다음은 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"

  • echo FA 는 파일을 복사하는지 디렉토리 (파일)를 복사하는지에 대한 불가피한 xcopy 질문과 파일 덮어 쓰기 (모두 덮어 쓰기)에 대한 가능한 질문에 답합니다.
  • usebackq 를 사용하면 git 명령의 출력을 do 절의 입력으로 사용할 수 있습니다.
  • HEAD ~ 1 HEAD 는 이전 커밋과 현재 HEAD의 모든 차이점을 가져옵니다.
  • % ~ fA 는 git 출력을 정규화 된 경로로 변환합니다 (슬래시를 백 슬래시로 변경해야 함).
  • C : \ git_changed_files \ 는 다른 모든 파일을 찾을 수있는 곳입니다.

1
심벌이 엉망이 된 것을 보았을 때 나는 그것이 크게 조정하지 않고는 작동 할 것이라고 기대하지 않았습니다. 그러나 그것은 첫 번째 시도에서 작동했습니다. 감사합니다.
네이도

3
Windows 배치 파일에 익숙하지 않은 (나와 같은) 모든 사람들을위한 약간의주의 : 배치 파일에서 위의 명령을 사용하려면 for 루프 내에서 % A를 %% A로 바꿔야합니다.
buddybubble

1
뭐? LOL. 그냥 사용하는 자식은 diff - 자식 아카이브 콤보 (니콜라스 '대답을). 훨씬 쉽고 간단합니다. (솔직히 여기서 무슨 일이 일어나고 있는지 모르겠습니다.)
ADTC

1
경고 : 이것은 작업 복사본에서 파일을 복사합니다. 실제로 HEAD에있는 것과 일치하지 않을 수 있습니다 (또는이를 대체 한 커밋 해시)
Simon East

1
훌륭하지만 Powershell이 ​​아닌 CMD에서만 작동합니다. -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/


내가 사용할 수있는 생각 function에서 PowerShell을 단지 함수 이름에 명령을 줄이고 사용하는 $args(가) 한 번만 해시를 커밋에 전달합니다. * nix에서는 셸 스크립팅을 사용하여 동일한 효과를 얻을 수 있습니다.
ADTC 2014 년

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 2015

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

4
경로 이름의 공백으로 인해 NUL을 처리하는 xargs에 파이프를 사용해야했습니다git diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD
Boggin

25

Tortoise Git 을 사용하여 diff 를 MS Windows로 내보낼 수 있습니다 .

나는 오른쪽 버튼으로 클릭하고 TortoiseGit > 로그 표시를 하고 로그 메시지가 공개됩니다.

두 버전을 선택하고 비교하십시오. 차이 가 열립니다.

파일을 선택하고 선택 항목을 ... 폴더로 내보내기 !

여기에 이미지 설명 입력


1
명확히. 이것에 대한 많은 답변에는 리눅스 전용 명령이 있었고 많은 것들이 작업 변경 사항을 캡처하지 않고 커밋 된 변경 사항 만 캡처했습니다. 이것이 게시되어 기쁩니다!
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'가 내부 또는 외부 명령, 작동 가능한 프로그램 또는 배치 파일로 인식되지 않습니다."라는 오류가 발생합니다.
Navin prasad

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

Windows에서 변경된 파일을 내보내는 PHP 스크립트를 만들었습니다. 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 명령이 PATH에 있도록 Git을 설치 한 경우 작동합니다. 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

나는 또한 이전에 비슷한 문제에 직면했습니다. 간단한 쉘 스크립트를 작성했습니다.

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

이제 위의 명령을 사용하는 기본 셸 스크립트입니다.

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/)로 바꿉니다.

결과적으로 누군가 svn 저장소에서 tortoise svn 내보내기 를 사용하는 데 익숙 할 수 있으므로 파일을 폴더 구조 (zip / tars 없음 ...)로 가져옵니다 .

예를 들어 마지막 커밋이 거의없는 추가 / 수정 파일을 수동으로 배포하려는 경우 매우 유용합니다. 따라서 ftp 클라이언트를 통해 이러한 파일을 복사 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.