4

Situation: I have an old commit that I need to merge selectively with the latest commit. Some files have no changes and other files have significant changes that I need to review and merge selectively.

Let's say that the old commit 1 had files A, B, C.

The latest commit 5 has involved changing files B and C since commit 1 and also added files D, E and F.

So between commits 1 and 5 files B and C have changed, that is, running diff on 1:B and 5:B; and on 1:C and 5:C would show differences.

I need to get filenames B and C only.

That is, all files not belonging to 1 but changed or added until and including 5 should NOT show up.

Michael Wild
  • 24,977
  • 3
  • 43
  • 43
LetMeSOThat4U
  • 6,470
  • 10
  • 53
  • 93
  • You should clarify what "commit `1` had files `A`, `B`, `C`" means: only that they were present in the tree when commit `1` was made, or that changes to them were commit right in commit `1`. I assume the latter, while answers by @Michael Wild and @hlovdal assume the former. – tanius Mar 02 '14 at 01:10

4 Answers4

2

You might try

git diff --diff-filter=M 1 5

The available filters are

  • A: Added
  • C: Copied
  • D: Deleted
  • M: Modified
  • R: Renamed
  • T: Type changed (symlink, regular file, etc.)
  • U: Unmerged
  • X: Unknown
  • B: Pairing broken

Refer to the git-diff(1) manual page for all the details.

Edit:

If you are interested in what changed how between two commits, you can also use the --name-status option, which for every file that changed outputs one of the above codes. This option can also be used with git-log telling you what type of change was made to which files for each of the commits.

Michael Wild
  • 24,977
  • 3
  • 43
  • 43
  • I'm sorry it doesn't work after all (used another command in a script that just printed contents of commit "1" and thought this was the output). – LetMeSOThat4U Feb 21 '13 at 17:00
  • Regardless of whether I used git diff --diff-filter 1 5 or git diff --diff-filter 5 1 it still displays ALL the modified files and does not limit output to contents of older commit. – LetMeSOThat4U Feb 21 '13 at 17:01
  • I think you're missing the `=M` at the end of `--diff-filter=M`. – Michael Wild Feb 21 '13 at 17:05
  • No, I just mistyped the example command, this is my actual command: git diff --diff-filter=M ce7dece9f43ced88325e12cf8b72cb324a14ac73 HEAD --name-status . I also tried reversing order of sha and HEAD and got the same number of files. – LetMeSOThat4U Feb 21 '13 at 19:18
  • Try putting the `--name-status` before the commits, right after the `diff-filter=M`. The order of the commit arguments is irrelevant, as you're just asking which files are different. It would flip the left-hand and right-hand sides of the diff if you left out the `--name-status`. To confirm that it is actually working, verify that **only** files with status `M` show up. Then run with `--diff-filter=ACDRT' and check that you **don't** get files with status `M`. – Michael Wild Feb 22 '13 at 07:17
  • I'm sorry, this STILL does not work. Perhaps it's because I run this in msys-git? (windows) – LetMeSOThat4U Feb 27 '13 at 13:44
1

To only consider the the files that are present in commit 1, run

$ git ls-tree --name-only commit1 | xargs git diff commit1 commit5 --

Note that this will include files that were present in commit 1 but are deleted in commit 5 (which will show up as deleted files in the diff). If you want to avoid that, find the common subset of files:

$ git ls-tree --name-only commit1 > all-files-in-commit1
$ git ls-tree --name-only commit5 > all-files-in-commit5
$ comm -1 -2  all-files-in-commit1 all-files-in-commit5 > common-files
$ xargs git diff commit1 commit5 -- < common-files
hlovdal
  • 26,565
  • 10
  • 94
  • 165
  • Those number are unrelated. `git ls-tree ce7dece...` shows 299 because that is how many files present when you check out that commit. `git show ce7dece...` shows 16 because that is the number of files changed compared to the parent commit. – hlovdal Feb 27 '13 at 14:23
0

Try (using SHAs 1 and 5 as in your example):

git diff 1 5 $(git diff --raw --no-commit-id --name-only -r 1~1 1)

Or more comfortably, so you only have to type every SHA once:

c1="1"; c2="5"
git diff $c1 $c2 $(git diff --raw --no-commit-id --name-only -r $c1~1 $c1)

How it's meant to work: The first git diff compares the two commits. The second generates a list of paths to limit the comparison to. This list is generated by comparing which files changed between your older commit $c1 and its parent $c1~1. (This also works when specifying something like the tenth-last commit using c1="HEAD~10", since HEAD~10~1 is a valid tree-ish in git, equivalent to HEAD~11.)

The subshell uses git diff --raw to als work for merge commits. Else we could use git diff-tree --no-commit-id --name-only -r $c1, which automatically compares a commit with its parent.

TODO: Not yet fully functional. At times it works, at times I get "fatal: ambiguous argument '<filename>': unknown revision or path not in the working tree."

tanius
  • 14,003
  • 3
  • 51
  • 63
0

Assuming that, with your "latest commit" 5 you refer to HEAD, you could also use this different strategy for your task. I found it to be the most workable so far:

  1. Apply your changes from the old commit to your staging area, without committing (-n = --no-commit):

    git cherry-pick -n <commit>
    
  2. Unstage all the changes you just made:

    git reset 
    
  3. Compare what these changes are, relative to your last commit:

    git diff HEAD
    
  4. Selectively add the changes you want:

    git add -p
    
  5. And finally make the new commit:

    git commit -m "commit message"
    

Source: Adapted from this answer.

Community
  • 1
  • 1
tanius
  • 14,003
  • 3
  • 51
  • 63