问题
Is there a way to test if two diffs or patches are equivalent?
Let's say you have the following git commit history, where features F and G are cleanly rebaseable to E:
G
/
A--B--C--D--E
\
F
Due to limitations in our current deployment process, we have the following, somewhat related graph (it's not version controlled)
G'
/
------------E'
\
F'
F' and G' will ultimately be applied to the head E', in some to be determined order, so it would end up like
------------E'--G'--F'
Is there a way to test that the diff from E' to G' is the same as the patch produced by the git commit of G from B?
I fully realize that in an ideal world, revision control would solve this, and we're getting there, but that's not where we are currently.
You could essentially play both patches on separate checkouts and compare the outputs, but that seems kind of clunky. And comparing the diffs themselves, I'm assuming, wouldn't work because line numbers could change. Even if G' and F' were rebased to E', the patch for F' would ultimately be applied to G', making the diff context of the patch different.
回答1:
diff <(git show COMMIT1SHA) <(git show COMMIT2SHA)
回答2:
Since git 2.19 there is an extra tool for that: git range-diff
So you probably want:
git range-diff E..G E..G'
This should work even for more than one patch in each range.
回答3:
For the benefit of the reader, here is an update to the answer of @mrbrdo with a little tweak:
- Adds
git sdiff
alias for easy access of this.sdiff
stands forshow diff
. - Ignore annotated tag headers using the
^{}
suffix. - Allow
diff
options to be used like-u
etc.
Run this once in each of your accounts in which you use git
:
git config --global alias.sdiff '!'"bash -c 'O=(); A=(); while x=\"\$1\"; shift; do case \$x in -*) O+=(\"\$x\");; *) A+=(\"\$x^{}\");; esac; done; g(){ git show \"\${A[\$1]}\" && return; echo FAIL \${A[\$1]}; git show \"\${A[\$2]}\"; }; diff \"\${O[@]}\" <(g 0 1) <(g 1 0)' --"
Afterwards you can use this:
git sdiff F G
Please note that this needs bash
version 3 or above.
Explained:
git config --global alias.sdiff
adds an alias namedgit sdiff
into the global~/.gitconfig
.!
runs the alias as shell commandbash -c
we needbash
(orksh
), as<(..)
does not work indash
(aka./bin/sh
).O=(); A=(); while x="$1"; shift; do case $x in -*) O+=("$x");; *) A+=("$x^{}");; esac; done;
separates options (-something
) and arguments (everything else). Options are in arrayO
while arguments are in arrayA
. Note that all arguments get^{}
attached, too, which skipps over annotations (such that you can use annotated tags).g(){ git show "${A[$1]}" && return; echo FAIL ${A[$1]}; git show "${A[$2]}"; };
creates a helper function, which doesgit show
for the first argument. If that fails, it outputs "FAIL first-argument" and then outputs the second argument. This is a trick to reduce the overhead in case something fails. (A proper error managment would be too much.)diff "${O[@]}" <(g 0 1) <(g 1 0)
runsdiff
with the given options against the first argument and the second argument (with said FAIL-error fallback to the other argument to reduce thediff
).--
allows to passdiff
-options (-something
) to this alias/script.
Bugs:
Number of arguments is not checked. Everything behind the 2nd argument is ignored, and if you give it too few, you just see
FAIL
or nothing at all.Errors act a bit strange and clutter the output. If you do not like this, run it with
2>/dev/null
(or change the script appropriately).It does not return an error if something breaks.
You probably want to feed this into a pager by default.
Please note that it is easy to define some more aliases like:
git config --global alias.udiff '!git sdiff -u'
git config --global alias.bdiff '!git sdiff -b'
git config --global alias.pager '!pager() { cd "$GIT_PREFIX" && git -c color.status=always -c color.ui=always "$@" 2>&1 | less -XFR; }; pager'
git config --global alias.ddiff '!git pager udiff'
I hope this does not need to be explained further.
For more aliases like that, perhaps have a look at my GitHub repo
回答4:
I would just let git
try and see:
git checkout E -b FG
git cherry-pick F' G'
git checkout E -b GF
git cherry-pick G' F'
git diff FG GF
git branch -D FG GF
来源:https://stackoverflow.com/questions/8569699/is-there-a-way-to-compare-two-diffs-or-patches