Export only modified and added files with folder structure in Git

GitGit Diff

Git Problem Overview


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.

Git Solutions


Solution 1 - Git

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.


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

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

Solution 2 - Git

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.

Solution 3 - Git

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

> 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 answers 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)
  • usebackq allows us to use the output from our git command as the input to our do clause
  • HEAD~1 HEAD gets all differences between the previous commit and the current HEAD
  • %~fA transforms 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

Solution 4 - Git

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

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.

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/

Solution 5 - Git

You can export diff using Tortoise Git to MS Windows:

I right click and select TortoiseGit > Show Log and Log Messages will be open.

Select two revisions and compare it. Difference between will be open.

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

enter image description here

Solution 6 - Git

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.

For example:

tag_ver_2.1 = 1f72b38ad
tag_ver_2.2 = c1a546782

Here is modified example:

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

Solution 7 - Git

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

Solution 8 - Git

I also had the same problem. The solution of @NicolasDermine didn't work since too many files have changed between the compared commits. I got an error that the shell arguments are too long.

As a consequence I added a python implementation. This can be installed via pip install gitzip and then executed with

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

in the repositories root creating an export.zip file containing all changed files retaining the directory structure.

Maybe someone needs it too so I thought to share it here.

> Disclaimer: I am the author of the gitzip module.

Solution 9 - Git

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.

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://localhost also points to c:/www and is php-enabled. Let's put these 2 files in 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

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.

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.

Solution 10 - Git

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

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

Solution 11 - Git

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

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

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

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

Then, from the root of the git repository:

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

Solution 12 - Git

I also faced a similar problem before. I wrote a simple Shell script.

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

The above command will display Commit Hash(Revision) from commit_HashX to commit_HashY in reverse order.

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

Now the main Shell Script using above command.

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

Add this code to export_changes.sh. Now pass file and commit hashes to the script.

Make sure that initial commit_hash must be the first argument then last commit_hash up to which you want to export changes.

Example:

$sh export_changes.sh hashX hashY

Put this script into git local directory or set git local directory path in the script. Hope it Helps..!

Solution 13 - Git

This works for me for both Unix and Windows:

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

There are two placeholders in the command. You need to replace them for your purpose:

  • 1: replace with commit id of commit right before all of the commits you want to export (e.g. 997cc7b6 - don't forget to keep that doubledot after commit id - this means "involve all commits newer than this commit")

  • 2: replace with existing path where you want to export your files (e.g. ../export_path/)

As a result you will get your files in a folder structure (no zips/tars...) as someone might be used to using tortoise svn exports in svn repositories.

This is for example pretty useful when you want to perform manual deploy of added/modified files of few last commits. So you can then just copy these files through ftp client.

Solution 14 - Git

git archive -o package.zip HEAD $(git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT )

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMichael KuhinicaView Question on Stackoverflow
Solution 1 - GitAristotle PagaltzisView Answer on Stackoverflow
Solution 2 - GitJames EhlyView Answer on Stackoverflow
Solution 3 - GitMM.View Answer on Stackoverflow
Solution 4 - GitNicolas DermineView Answer on Stackoverflow
Solution 5 - GitWendelView Answer on Stackoverflow
Solution 6 - GitpietrView Answer on Stackoverflow
Solution 7 - Gitme_astrView Answer on Stackoverflow
Solution 8 - Gitmiile7View Answer on Stackoverflow
Solution 9 - GitLemon JuiceView Answer on Stackoverflow
Solution 10 - GitcatalinpView Answer on Stackoverflow
Solution 11 - GitPatrick.SEView Answer on Stackoverflow
Solution 12 - GitRaj HawaldarView Answer on Stackoverflow
Solution 13 - GitSkybamarView Answer on Stackoverflow
Solution 14 - GitNathan RonaView Answer on Stackoverflow