Export only modified and added files with folder structure in Git

70,737

Solution 1

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

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

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

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

Share:
70,737
Michael Kuhinica
Author by

Michael Kuhinica

Commands electronic brains through words and syntax.

Updated on September 20, 2021

Comments

  • Michael Kuhinica
    Michael Kuhinica over 2 years

    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.

  • Michael Kuhinica
    Michael Kuhinica over 13 years
    Now I just have to find ou a way to glue the result of this command with tar! Thank you!
  • Nathan Fig
    Nathan Fig about 10 years
    When I saw that mess of symbols I did not expect it to work without extensive tweaking... but it worked on the first try, thanks.
  • evolutionxbox
    evolutionxbox over 9 years
    Can this be done for multiple commits into one tar file?
  • ADTC
    ADTC over 9 years
    I think I can use function in PowerShell to reduce the command to just a function name and use $args to pass in the commit hash just once. In *nix, you can probably use shell-scripting to achieve the same effect.
  • Ricardo Martins
    Ricardo Martins over 9 years
    When trying to add multiple commits to the same tar file, sometimes tar creates filename.ext again with other name (filename1.ext). I realize that if I use zip, I can add and overwrite the file if it exists in the zip... You just need to replace the xargs with xargs zip -r0 myzipfile.zip $1
  • Matthieu Sadouni
    Matthieu Sadouni over 9 years
    @Taverneiro You can pipe it to the tar command given in this answer, eg. for a tgz file : git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -
  • deanpodgornik
    deanpodgornik about 9 years
    Man... I own you a beer :)
  • Ben Sewards
    Ben Sewards almost 9 years
    just a note that git archive doesn't work for a range of revisions
  • buddybubble
    buddybubble over 8 years
    a little heads up for all the guys who are (like me) not very familiar with windows batch files: If you want to use the above command in a batch file, you will need to replace %A with %%A inside the for loop
  • ADTC
    ADTC over 8 years
    @BenSewards for range of commits between commit1 and commit2 (inclusive of later commit, exclusive of earlier commit; order doesn't matter) just do git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1 commit2). The commit passed to the archive command can be any arbitrary commit (most likely either the HEAD or the later commit), and the files are pulled as if they are checked out at that commit stage. The commit1 and commit2 passed to diff are only used to generate the list of files to pull - they do not affect the version of files pulled.
  • ADTC
    ADTC over 8 years
    What? LOL. Just use the git archive - git diff combo (Nicolas' answer). Much easier and straightforward. (I honestly don't know what's happening here.)
  • ADTC
    ADTC over 8 years
    Additional tip: If you want to combine the changed files of multiple ranges of commits into one archive, just repeat the diff command with a semicolon: 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)
  • Boggin
    Boggin almost 8 years
    Due to spaces in path names I had to use a pipe to xargs handling the NUL git diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD
  • Erdal G.
    Erdal G. almost 8 years
    Is xargs needed here? I know it is for stuff like rm, but I thought tar could handle as many files as you want.
  • Alberto Alexander Rodriguez
    Alberto Alexander Rodriguez almost 7 years
    I've just detected a WEIRD issue: if I try the code above it works like a charm. But I change the tar -rf test.tar for tar -zcf test.tar.gz, then the resulting file is missing several files. What can be causing this difference in result?
  • morteza khadem
    morteza khadem over 6 years
    this code is great, but i want export files and directories with submodules your code only worked with main repository and if submodules changed your code say submodule folder deleted!
  • mems
    mems about 6 years
    Be carefull with this answer, this take diff files but as the current version of the current branch
  • Navin prasad
    Navin prasad over 5 years
    While executing this command in the windows slave machine, I'm getting the following error "'xargs' is not recognized as an internal or external command, operable program or batch file."
  • Navin prasad
    Navin prasad over 5 years
    While executing this command in the windows slave machine, I'm getting the following error "'xargs' is not recognized as an internal or external command, operable program or batch file."
  • Simon East
    Simon East about 5 years
    Warning: this will copy files from your working copy, which may not actually match what's in HEAD (or whatever commit hash you replace it with)
  • user1169587
    user1169587 almost 5 years
    @mems assume the diff is fileA, then how to get the $commit_id version of fileA instead of the current version of fileA?
  • user1169587
    user1169587 almost 5 years
    @ADTC cannot use git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1A..commit3B)? but need to repat the diff command with a semicolon?
  • dythim
    dythim almost 5 years
    Definitely. So many answers to this had linux-only commands, and a lot did not capture working changes, only changes which had been committed. Glad this was posted!
  • ADTC
    ADTC over 4 years
    @user1169587 your version is for a single range of commits (ALL commits from 1A to 3B). I was talking about combining multiple ranges of commits (1st range 1A to 1B; 2nd range 2A to 2B; 3rd range 3A to 3B; etc) so the commits outside these ranges would be ignored.
  • Rodion Bykov
    Rodion Bykov almost 4 years
    Great stuff, but works with only CMD not Powershell, -v°v-
  • Michael Li
    Michael Li over 2 years
    perfect. it solve my question.
  • Nathan Rona
    Nathan Rona about 2 years
    You can create a zip-file through git archive by: git archive -o upadate.zip HEAD $(git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT <comit_id>)