Figure out dependencies of a commit we try to cherry pick
Asked Answered
M

2

9

Assume something like the following:

                            HEAD/master     
                             |
A<--B<--C<--D<--E<--F<--G<--J  
        ^
        official 

Where official is a branch.
I wanted to cherry-pick 2 commits to official branch e.g.E and J
Both these commits were fixes affecting the same 3 files.
When I did git cherry-pick E it went fine but when I did git cherry-pick J I got some conflicts.
Looking at the diffs I realized that I needed to also cherry pick commit F which did a change in two of those 3 files which change was basically a method definition change and J was done on top of that.
So it was easy to fix by just doing git cherry-pick F && git cherry-pick J
Question:
If I wasn't aware of the changes done in those files and commit F was a big commit changing many files: Is there another way to figure out on which commit a commit we are trying to cherry pick depends on without manually doing a git log on the file and going commit by commit?

Messner answered 7/2, 2017 at 20:46 Comment(0)
L
8

Is there another way to figure out on which commit a commit we are trying to cherry pick depends on without manually doing a git log on the file and going commit by commit?

Yes there is - I wrote git-deps to automate precisely that task. Since it currently accepts a range of commits to analyse, in the case of your example you would need to pass it a singleton range containing just commit J, and also tell it to exclude any dependencies which are already in official:

git deps --exclude-commits official J^!

Behind the scenes the tool will then look at the diff of J^ with J, and then run git blame on any lines which are removed or modified, plus a configurable number of lines of surrounding context. This would result in it discovering that one or more of the lines J removes or modifies come from commit F, therefore it would output the SHA1 of commit F.

git-deps can not only discover dependencies of a single commit or range of commits, but also recurse a whole dependency tree. Therefore the entire cherry-picking scenario you describe could be automated into a single command:

git deps --recurse --exclude-commits official J^! | \ 
    tsort | \ 
    tac | \ 
    xargs -t git cherry-pick

Please note that this only guarantees that the cherry-picks will all cleanly apply, not that they are semantically correct. So the resulting branch head will still need to be carefully inspected and tested for correctness.

More convenient porting of commits between branches is the most obvious (but not the only) use case of git-deps, and for this reason I have dedicated a section of the README specifically to this use case. It even includes a YouTube video showing you exactly how to use it.

If you would like more details, you may be interested in a couple of talks I have presented on this and other related tools:

I also spoke about the tool in episode #32 of the GitMinutes podcast.

Leann answered 25/10, 2018 at 9:52 Comment(2)
Thank you for your answer! Do you know if there is a similar cherry picking dependency tracking solution but for Subversion?Gilges
Pretty sure Subversion simply isn't powerful enough to achieve this itself. You could use git-svn though.Leann
T
0

I got some [merge] conflicts ... Is there another way to figure out on which commit a commit we are trying to cherry pick depends on ...

Not really, no.

... without manually doing a git log on the file and going commit by commit?

Even this won't necessarily do it—at least, not without actually thinking about the problem. Consider the following bit of C code:

void f(int x) {
    int i;

    /*
     * additional lines here, enough to mess with git context
     * since that only looks at +/- 3 lines around each patch
     * segment
     */

    for (i = 0; i < x; i++)
        do_something(i);
}

Suppose that one intermediate commit introduces an additional variable j, and then a later commit changes do_something(i) to do_something(j). If you cherry-pick only the later commit, Git will happily change the line with no conflicts, but the code will no longer compile, as there is no int j in function f.

That said, it would be possible to write a tool that parses git show output for each intermediate commit, figures out which lines are changed (relative to the version in the work-tree, rather than using the actual numbers in each diff, since those may depend on a previous skipped diff), and points you to commits that modify conflicting regions. But I don't know of any such tool.

Thirtieth answered 8/2, 2017 at 3:53 Comment(2)
So what is your workflow in cases like this? What have you found as most efficient?Messner
@Jim: generally, a lot of inspection of commits—basically, what you ended up doing. Complex cherry-picks should be rare in part because the tools don't deal well with them, and the tools don't deal well with then in part because they're rare, which is sort of catch-22.Thirtieth

© 2022 - 2024 — McMap. All rights reserved.