Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Rebase-with-history Result based on Full Result #129

Closed
make1980-zz opened this issue May 30, 2018 · 8 comments
Closed

Generate Rebase-with-history Result based on Full Result #129

make1980-zz opened this issue May 30, 2018 · 8 comments

Comments

@make1980-zz
Copy link

Hi Michael,

First of all imerge is an extremely useful tool! Thanks for inventing it. In our case we develop on a project in parallel with its development in the open source community and we have to periodically merge our repo with the community repo.

To achieve this in a simple way we use imerge to do a full merge and persist that. Whenever there is new change from the community we do another imerge and persist the full result as well. This all works. Well the problem is that we don't want the full merge result to be in our master branch since the history is complex, which is useful just for the merge. We can call "simplify" command twice here - once with "rebase-with-history" and another with "full" but the second imerge is based on "full" so simplifying the second imerge won't change the history of the first imerge which is full.

Do you have any suggestion on how to solve this? I guess we'll have to use "git commit-tree" to modify the history, which is actually how "simplify" command generates the "rebase-with-history" result. But can't figure out how to do it manually.

Really appreciate your help in advance!

@make1980-zz
Copy link
Author

make1980-zz commented May 30, 2018

@mhagger

Thanks for your response Michael. Let me give a concrete example here using a similar diagram you used to demonstrate how imerge works (tried to capture the screen as well in case the indent/space doesn't work). Let's say currently our master has commit 0, 1, 2. And community has v1.0 containing commit A, B. After a full merge the diagram is like:

o - 0 -  1  - 2    ← master
    |    |    |
    A - A1 - A2
    |    |    |
    B - B1 - B2
    ↑
  v1.0

Then I have a branch merge-master-full persisting the above state. And I'll persist the rebase-with-history result to merge-master so merge-master is like:

o - 0 - 1 - 2     ← master
    |       |
    A - - -A2
    |       |
    B - - -B2
    ↑
  v1.0

Later on there is commit C in community branch v2.0, and we want to do imerge again on merge-master-full instead of merge-master since I can apply C gradually on B1 and B2, instead of directly on B2 so the result is:

o - 0 -  1  - 2    ← master
    |    |    |
    A - A1 - A2
    |    |    |
    B - B1 - B2
    |    |    |
    C - C1 - C2
    ↑
  v2.0

Now I want to do a simplification using "rebase-with-history" to get a new merge-master. However imerge only knows that C1-C2 is in progress so it won't collapse A1/B1 and the end result will be:

o - 0 -  1  - 2    ← master
    |    |    |
    A - A1 - A2
    |    |    |
    B - B1 - B2
    |         |
    C -  - - C2
    ↑
  v2.0

And my desired result is:

o - 0 - 1 -  2    ← master
    |        |
    A - - - A2
    |        |
    B - - - B2
    |        |
    C - - - C2
    ↑
  v2.0

So my question is really how to get this last stage from the "full" merge result which is generated through 2 imerge merge operations.

I'll copy this to the issue post on github so that you can answer from there.

2018-05-30 12:28 GMT-07:00 Michael Haggerty [email protected]:

If all you want in the end is a simple merge, that's not hard to do by hand,
for example using git imerge reparent twice. I didn't quite understand why
you don't use git imerge merge in the first place, but I suppose you have
your reasons.

If you want more detailed advice you should make a sketch or something to
make it more understandable. And preferably in an issue so that other people
can get involved.

Hope that helps,
Michael

@mhagger
Copy link
Owner

mhagger commented May 31, 2018

In this case, where there is only one commit C, you could fix this up by hand using git imerge reparent to create a new commit with the same contents as C2 but with parents C and B2 from your second diagram.

But I suppose that often there will be commits C, D, E, etc., in which case it quickly gets pretty unwieldy to do this by hand.

The code equivalent in git-imerge is method GitRepository.reparent(). You could call that from your own code to do this in an automated way. There is also a method GitRepository.create_commit_chain() that might give you more tips.

I wouldn't recommend starting from the MergeState.simplify_to_*() methods, because they are too tied up with an in-progress imerge.

@make1980-zz
Copy link
Author

Thanks a lot, Michael! I actually extended reparent command to accept an additional parameter (--commit-to-reparent) which can specify a commit other than HEAD for reparent. And the consequence will be reparenting all the way back from this target commit to HEAD.

I'd like to send a pull request so that this new parameter can be supported from imerge - look forward to your input.

@mhagger
Copy link
Owner

mhagger commented Jun 1, 2018

I'd like to send a pull request so that this new parameter can be supported from imerge - look forward to your input.

Nice. Please @-mention me on the PR so that I don't overlook it.

@make1980-zz
Copy link
Author

I've created this pull request: #130

@mhagger
Copy link
Owner

mhagger commented Jun 24, 2018

I was thinking a little bit more about your style of using git-imerge. It seems that you don't really need to preserve the full merge forever. What if you could instead simplify to a history that includes the incremental merges along both outer edges but omitting the merges in the interior? So for example, currently I believe that you retain both the full merge:

o - 0 -  1  - 2    ← master
    |    |    |
    A - A1 - A2
    |    |    |
    B - B1 - B2
    ↑
  v1.0

(which has all of the information that you will need for subsequent merges) and the rebase-with-history:

o - 0 - 1 - 2     ← master
    |       |
    A - - -A2
    |       |
    B - - -B2
    ↑
  v1.0

(which, if I understand correctly, is the version that you work off of day-to-day). What if, instead, you could simplify to this:

o - 0 -  1  - 2    ← master
    |         |
    A        A2
    |         |
    B - B1 - B2
    ↑
  v1.0

Later on, if there are new commits C and D in community branch v2.0, you would again simplify the same way, giving you:

o - 0 -  1  - 2    ← master
    |         |
    A        A2
    |         |
    B - B1 - B2
    |         |
    C        C2
    |         |
    D - D1 - D2
    ↑
  v2.0

Would that have all of the properties that you are looking for?

If you really need the rebase-with-history connections, you could alternatively simplify to

o - 0 -  1  - 2    ← master
    |         |
    A -  - - A2
    |         |
    B - B1 - B2
    |         |
    C -  - - C2
    |         |
    D - D1 - D2
    ↑
  v2.0

or even

o - 0 -  1  - 2    ← master
    |    |    |
    A -  + - A2
    |    |    |
    B - B1 - B2
    |    |    |
    C -  + - C2
    |    |    |
    D - D1 - D2
    ↑
  v2.0

, where you have a kind of "rebase-with-history" in both directions, but don't retain the internal commits (e.g., the parents of D1 are D and B1).

@make1980-zz
Copy link
Author

You're right - I only need this in fact:

o - 0 -  1  - 2    ← master
    |         |
    A -  - - A2
    |         |
    B - B1 - B2
    |         |
    C -  - - C2
    |         |
    D - D1 - D2
    ↑
  v2.0

I guess this also means we can use bisect for A2 and C2 so that imerge can run faster? How can we introduce this new merge/simplify mode so that bisect can be used for the inner commits? I guess it's still not that simple since without A1 generating B1 might also be difficult?

We still need the merge result from A to A2 since rebase-with-history is a better simplify result which allows us to incrementally update the master in case there is any regression observed.

@make1980-zz
Copy link
Author

Thinking about this again - is this actually achievable by calling imerge in both directions using rebase-with-history as the goal? Meaning that we imerge from community to master using rebase-with history which gives us this:

o - 0 -  1  - 2    ← master
    |         |
    A -  -  - A2
    |         |
    B -  -  - B2
    ↑
  v2.0

Then we do another imerge from master to community which gives:

o - 0 -  1  - 2    ← master
    |    |    |
    A    |    |
    |    |    |
    B - B1 - B2
    ↑
  v2.0

Now if somehow we can reparent B2 to B1/A2 then we have the desired merge result. The problem with this approach is that we might need to resolve conflicts twice?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants