Reputation: 379
I need to get a list of files changes between 2 commits, but excluding commits with a given tag.
I am currently using this line
git diff --oneline --diff-filter=AM FIRST_TAG HEAD -- SpecificFolder
as I only need the names of the files added or modified (within the SpecificFolder dir) between FIRST_TAG and now
now, i need to exclude commits tagged as EXCLUDE_TAG/* in this comparison. Is this supported by git diff? or should I do several steps like
Upvotes: 0
Views: 332
Reputation: 488619
You will need your multi-step process, or something similar.
"Exclude certain commits" is a phrase that, in Git—especially as applied to diffs—makes little sense. It's like saying "I'd like to be over there, but excluding my left arm." :-)
Fundamentally, the issue here is that:
git diff
compares two such snapshots
(with "the index" and "the work-tree" basically acting as temporary snapshots, vs the commits, which are permanent-and-unchanging snapshots).
Once you have the result the comparison, you can reduce it in various ways, such as limiting it with --diff-filter
, printing just file names with --name-only
(I am guessing you meant that instead of --oneline
here), and/or looking only at changes in SpecificFolder
, but we always start with the two specific snapshots.
Meanwhile, a tag name just specifies some commit in a long chain of commits, e.g.:
tag:v1.0
|
v
...--o--o--o <-- master
\
o--o--o <-- develop
^
|
tag:v1.1-rc1
Here, each round o
node represents a commit. The commit that is the most recent, or tip, commit on master
is the commit that you copy for working-on when you run git checkout master
. That same commit—the same snapshot—is named by the tag v1.0
, but once you make a new commit on master
, the tag stays where it is, and the branch name moves forward to accommodate the new commit:
tag:v1.0
|
v
...--o--o--o--o <-- master
\
o--o--o <-- develop
^
|
tag:v1.1-rc1
Similarly, the tag v1.1-rc1
identifies one specific commit, currently one commit "behind" the tip of develop
.
You can run git diff v1.0 v1.1-rc1
to compare the two specific tagged commits. This completely ignores every other commit: it just extracts the two snapshots and compares them.
Since, in this case, you're looking just for added or modified files, it makes some sense to go one commit at a time:
tag:EXCLUDE_TAG/2
|
v
o--o <-- feature
/
...--o--A--*--o <-- master
\
\ *--*
\ / \
*--* *--*--* <-- develop (HEAD)
\ /
*--*
^
|
tag:EXCLUDE_TAG/1
You can now run git rev-list --ancestry-path A..HEAD
, where A
is some identifier that locates commit A
, and the current commit is the tip commit of branch develop
, which is the current branch. This will print out the hash ID of every starred commit (note that A
's ID itself will not be printed).
From this list, you can then strip out the hash ID of exactly one starred commit, since it has a name EXCLUDE_TAG/1
. You can use git for-each-ref 'refs/tags/EXCLUDE_TAG/*'
to locate these. (Try it with no options, then add --format
directives to limit its output to what you really want. If you have a mix of annotated and lightweight tags, remember that you may have to "peel" some of them to find their target commits. %(*objectname)
will do that for annotated tags, but produces no output for lightweight tags.) Note that some—like EXCLUDE_TAG/2
in this example—may not be in the original list of commit hashes.
You now have a list of all non-excluded commit hashes. You can therefore compare any one commit with any other, such as comparing each commit with its immediate parent, to see what changed in that one commit. But there is a flaw in this strategy: what will you do with merge commits? In the above diagram, there is one merge commit, three steps behind the tip of develop
. It may differ from either or both of its parent commits—in fact, if it's an evil merge, it will contain changes that are not in either parent (this is the definition of an evil merge: see Evil merges in git?). You must decide for yourself what to do for such cases.
Upvotes: 1