From: Junio C Hamano <hidden> Date: 2016-06-15 22:51:34
Jeff King [off-list ref] writes:
... But with a commit no top, you get:
<<<<<<< HEAD
two
=======
one
>>>>>>> one
which looks like you are reverting, because of course you are building
on top of the finished series.
Exactly.
$ git rebase master topic
Applying: one
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging file
CONFLICT (content): Merge conflict in file
Failed to merge in the changes.
Patch failed at 0001 one
hint: this commit may already be in upstream as 1234abcd;
hint: the differences between that commit and this one are:
diff --git a/file b/file
--- a/file
+++ b/file
@@ -1 +1 @@
-modified one
+one
When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To restore the original branch and stop rebasing run "git rebase --abort".
Actually I do not think identifying the ones that can safely skipped is
such a big issue. The case I am most concerned about is when you see that
"two reverted back to one" (which you obviously want to avoid, to keep the
effect of the commit the upstream has to have "two" on that line), but at
the same time when you do not agree with the change that the upstream took
for the _current commit_ you are replaying (i.e. you want the final result
to have "one", not "modified one" which the upstream has applied).
The conflict resolution to come up with such an incremental change is very
painful. You have to avoid the "s/two/one/" revert, and you have to keep
the "s/modified one/one" revert, and you need to know which hunks are
conflicting due to what (i.e. the former is because a patch similar to the
one you haven't replayed in this rebase session is already in upstream,
the latter is the upstream tweaked the current patch you are looking at in
a way you do not agree with).
I do not have a good idea to solve this in mind yet.
From: Jeff King <hidden> Date: 2016-06-15 22:51:34
On Mon, Jul 11, 2011 at 04:21:54PM -0700, Junio C Hamano wrote:
Actually I do not think identifying the ones that can safely skipped is
such a big issue. The case I am most concerned about is when you see that
"two reverted back to one" (which you obviously want to avoid, to keep the
effect of the commit the upstream has to have "two" on that line), but at
the same time when you do not agree with the change that the upstream took
for the _current commit_ you are replaying (i.e. you want the final result
to have "one", not "modified one" which the upstream has applied).
I'm not sure there's a general solution to that. You can't keep the
commit you want intact, because you are rebasing and therefore building
on top of the other broken commit. So in a history like:
B'--C'
/
A--B--C
You really want to perform the transformation of B to B', but on top of
C (i.e., "git checkout C; git diff B' B | git apply"). But if B and C
are textually related, it's going to conflict horribly. And I don't
think there is a general solution short of a darcs-style patch algebra.
-Peff
... But with a commit no top, you get:
<<<<<<< HEAD
two
=======
one
>>>>>>> one
which looks like you are reverting, because of course you are building
on top of the finished series.
Exactly.
quoted
$ git rebase master topic
Applying: one
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging file
CONFLICT (content): Merge conflict in file
Failed to merge in the changes.
Patch failed at 0001 one
hint: this commit may already be in upstream as 1234abcd;
hint: the differences between that commit and this one are:
diff --git a/file b/file
--- a/file
+++ b/file
@@ -1 +1 @@
-modified one
+one
When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To restore the original branch and stop rebasing run "git rebase --abort".
Actually I do not think identifying the ones that can safely skipped is
such a big issue. The case I am most concerned about is when you see that
"two reverted back to one" (which you obviously want to avoid, to keep the
effect of the commit the upstream has to have "two" on that line), but at
the same time when you do not agree with the change that the upstream took
for the _current commit_ you are replaying (i.e. you want the final result
to have "one", not "modified one" which the upstream has applied).
The conflict resolution to come up with such an incremental change is very
painful. You have to avoid the "s/two/one/" revert, and you have to keep
the "s/modified one/one" revert, and you need to know which hunks are
conflicting due to what (i.e. the former is because a patch similar to the
one you haven't replayed in this rebase session is already in upstream,
the latter is the upstream tweaked the current patch you are looking at in
a way you do not agree with).
I do not have a good idea to solve this in mind yet.
I am not 100% sure that my solution is exactly about this problem, but
it seems to be quite relevant.
I think that if you rebase "step-by-step" by doing, for this particular
example, something like
$ git rebase master^ topic
$ git rebase master topic
You will first see the /modified one/one/ conflict that you will resolve
your "two" against and then your second rebase will apply with no conflicts.
I have a set of scripts that help me do this kind of rebases by
essentially rebasing the topic branch against every single commit on the
upstream.
This way I can clearly see every conflict as at appears and can fix it
even in cases when a rebase against the final state would give an
absolutely unbearable diff.
It definitely is much slower but in my case I find this to be the only
possible solution. rerere helps enormously here.
At the same time the less changes are in topic...master the faster it
would be and the more changes are there the more you benefit from a
gradual rebase.
Here is the actual use case in case I was not very clear above or in
case someone is interested.
I have a very longed lived branch that I have to rebase against an
actively developed master.
Besides been longed live the branch contains a lot of absolutely
unrelated changes.
The branch is a "next version branch" and contains a result of a two
year work done by two people who though that their version of the
application will become master "tomorrow", so they where not too shy to
change things.
The upstream is developed by another 6 - 8 people over 3 years and they
have no knowledge of the changes done in the "next version branch".
Here is were I step in and am told to get the changes from the next
version branch onto the master. And I am not part of either of these
two groups of people.
In addition to that one of the guys who developed the next version has
left the company before I joined and the other one is also gone by now.
While it seems pretty crazy to me, thanks to git rebase, git rerere and
the "step-by-step" rebase I am at my 9th rebase of this topic branch
through last year and had only minor problems so far.
Essentially now we have 6 people developing the application "in the
middle" of its history with one person trying to hold the rest of the
history on top.
I imagine this as a kind of an acrobatic trick worth showing in a circus :)
Ilya Bobyr
From: Jeff King <hidden> Date: 2016-06-15 22:51:34
On Mon, Jul 11, 2011 at 07:14:16PM -0500, Illia Bobyr wrote:
I am not 100% sure that my solution is exactly about this problem, but
it seems to be quite relevant.
I think that if you rebase "step-by-step" by doing, for this particular
example, something like
$ git rebase master^ topic
$ git rebase master topic
You will first see the /modified one/one/ conflict that you will resolve
your "two" against and then your second rebase will apply with no conflicts.
I have a set of scripts that help me do this kind of rebases by
essentially rebasing the topic branch against every single commit on the
upstream.
That makes a lot of sense to me as a strategy. Of course, as you
mention, it is horribly slow. And when you do have real conflicts, you
would end up looking at the same conflicts again and again (and as you
mention, rerere can be some help there, though not necessarily
perfect).
At the same time the less changes are in topic...master the faster it
would be and the more changes are there the more you benefit from a
gradual rebase.
Yeah, this seems like the real problem to me. It's one thing to rebase
on top of a single series that somebody has applied upstream. But if it
has been 2 weeks, there may be hundreds of commits, and doing hundreds
of rebases is awful. I wonder if you could do better by picking out some
"key" commits in master to rebase on top of using one of:
1. Divide-and-conquer the commit space. Try the rebase, starting on
the HEAD. If it works, great. If the user says "this is too hard",
then find the midpoint between where we tried to rebase and the
merge base, and try rebasing there. Every time it's too hard, go
back halfway to the start. Every time it's easy, try the new result
on top of HEAD.
So it's basically doing a O(lg n) search backwards for an easy
place to rebase, and then repeatedly checking if that was a good
spot (and repeating the backwards search if not). The worst case
complexity is O(n lg n) rebases. But in practice, you can hopefully
find the problematic spot in O(lg n), and then everything will just
work out after 1 or 2 problematic spots.
2. Use heuristics (like commit message content) to find related
commits. So if I have a 5-patch series, I can perhaps find the
likely commits upstream that match my patches, and those are
good places to try individual rebases. And then I don't care how
many commits are in master. If I have a 5 patch series, I won't do
more than 5 rebases.
But I've never tried this in practice. Maybe next time a rebase is ugly
I'll manually work through one of the methods and see how it fares.
-Peff
From: Clemens Buchacher <hidden> Date: 2016-06-15 22:51:34
On Mon, Jul 11, 2011 at 08:03:04PM -0400, Jeff King wrote:
On Mon, Jul 11, 2011 at 04:21:54PM -0700, Junio C Hamano wrote:
quoted
Actually I do not think identifying the ones that can safely skipped is
such a big issue. The case I am most concerned about is when you see that
"two reverted back to one" (which you obviously want to avoid, to keep the
effect of the commit the upstream has to have "two" on that line), but at
the same time when you do not agree with the change that the upstream took
for the _current commit_ you are replaying (i.e. you want the final result
to have "one", not "modified one" which the upstream has applied).
I'm not sure there's a general solution to that. You can't keep the
commit you want intact, because you are rebasing and therefore building
on top of the other broken commit. So in a history like:
B'--C'
/
A--B--C
You really want to perform the transformation of B to B', but on top of
C (i.e., "git checkout C; git diff B' B | git apply"). But if B and C
are textually related, it's going to conflict horribly. And I don't
think there is a general solution short of a darcs-style patch algebra.
FWIW, I tried this in darcs and it has exactly the same problem.
It does have better granularity when detecting changes. For
example, it will recognize the changes of B' in B, even if B
contains non-conflicting hunks on top of the changes in B'. Git
only recognizes identical commits, and this is something where we
could improve without too much difficulty (think per-hunk
patch-ids).
But if the changes in B and B' have conflicts, then darcs will
present the user with the options B' and C, which looks like we
were trying to revert C, just like it does with git.
Clemens
From: Jeff King <hidden> Date: 2016-06-15 22:51:34
On Tue, Jul 12, 2011 at 09:38:44PM +0200, Clemens Buchacher wrote:
quoted
I'm not sure there's a general solution to that. You can't keep the
commit you want intact, because you are rebasing and therefore building
on top of the other broken commit. So in a history like:
B'--C'
/
A--B--C
You really want to perform the transformation of B to B', but on top of
C (i.e., "git checkout C; git diff B' B | git apply"). But if B and C
are textually related, it's going to conflict horribly. And I don't
think there is a general solution short of a darcs-style patch algebra.
FWIW, I tried this in darcs and it has exactly the same problem.
It has been a long time since I've looked at darcs, but from my
recollection, it will only work with specific patch types. That is, it
works if B and C are commutative. For text patches that touch the same
area, that is not the case. But if "B" were a token-renaming patch, for
example, I think it might work.
Anyway, that is not really relevant to git. I think we decided long ago
that being simple and stupid about the content changes (i.e., blob A
became blob B) is better in general, even when there are a few corner
cases that might have been better off the other way.
It does have better granularity when detecting changes. For
example, it will recognize the changes of B' in B, even if B
contains non-conflicting hunks on top of the changes in B'. Git
only recognizes identical commits, and this is something where we
could improve without too much difficulty (think per-hunk
patch-ids).
I'd be curious to see an example worked out. In my experience, even if
something like patch-ids don't match, it's not a big deal for the hunks
that do match, because when we get to the actual content merge, we will
realize that both sides made the same change to that hunk. So it's not
like you are getting unrelated conflicts; whatever small part of the
diff made the patch-id different will be the part where you get the
conflict, and the should merge cleanly.
Having said something so general, I'm sure there is probably some corner
case that proves me wrong.
-Peff
From: Clemens Buchacher <hidden> Date: 2016-06-15 22:51:34
On Tue, Jul 12, 2011 at 03:45:40PM -0400, Jeff King wrote:
It has been a long time since I've looked at darcs, but from my
recollection, it will only work with specific patch types. That is, it
works if B and C are commutative. For text patches that touch the same
area, that is not the case. But if "B" were a token-renaming patch, for
example, I think it might work.
If they were commutative, we would not have a problem in git
either.
Anyway, that is not really relevant to git. I think we decided long ago
that being simple and stupid about the content changes (i.e., blob A
became blob B) is better in general, even when there are a few corner
cases that might have been better off the other way.
Yes, but that only applies to git merge. When we talk about
rebasing we are looking at individual patches rather than a single
global merge. For rebase I think "patch algebra" is very relevant,
and we have already implemented a simple patch algebra with
patch-id's.
quoted
It does have better granularity when detecting changes. For
example, it will recognize the changes of B' in B, even if B
contains non-conflicting hunks on top of the changes in B'. Git
only recognizes identical commits, and this is something where we
could improve without too much difficulty (think per-hunk
patch-ids).
I'd be curious to see an example worked out. In my experience, even if
something like patch-ids don't match, it's not a big deal for the hunks
that do match, because when we get to the actual content merge, we will
realize that both sides made the same change to that hunk. So it's not
like you are getting unrelated conflicts; whatever small part of the
diff made the patch-id different will be the part where you get the
conflict, and the should merge cleanly.
I am reading that last part as "they should not merge cleanly". And
in general I agree. We have to resolve the conflict manually and
it's just a question of how the conflict is presented and resolved.
This is already being discussed some in a different branch of this
thread.
Having said something so general, I'm sure there is probably some corner
case that proves me wrong.
Exactly. The case I am talking about is where the patch-id's are
different but there are no conflicts. I have worked out an example
for git and darcs. Below are two scripts to demonstrate. In the
example, the patch-id is different because upstream changes the
patch in a way that does not conflict with the original patch. It
simply adds another change that goes into a different hunk. Git
fails to merge cleanly because the patch-id's are different. It
presents the user with an awkward conflict that looks like a
revert. Darcs, on the other hand, merges cleanly. It recognizes the
fact that all changes from the original patch are contained
upstream and do not conflict with the upstream version. The fact
that more changes are added on top does not bother darcs.
Now, one might argue that this is a corner case. But it's actually
very common. In the example, the patch-id changes because of an
extra change in a different text area. That is indeed unlikely.
However, the same problem will occur in a much more common case.
Let's say we have a patch with 10 hunks. The patch is applied
upstream, with only one difference in one of the hunks.
Subsequently, text areas affected by any of the other hunks change
upstream. When the original patch is rebased on top of that, it
will conflict with the one hunk that was changed in the upstream
version of that patch. And that's ok. Git should not decide which
version is correct. But in addition to that conflict there will
also be conflicts for all the other hunks, which the upstream patch
did _not_ modify. And all of those conflicts will look like
reverts.
I believe that is the main reason why rebase is so painful all the
time.
But I am not saying that we necessarily need a finer granularity of
patch-id's. If we rebase the patch on top of its upstream version
before rebasing it to the upstream head, as was already suggested
elsewhere, the problem described here will also go away on its own.
Clemens
---
#!/bin/sh
#
# Darcs recognizes matching upstream changes
#
testdir=test-darcs
mkdir "$testdir" || exit 1
(
cd "$testdir"
mkdir master
cd master
darcs init
for line in $(seq 20)
do
echo $line >>file
done
darcs add file
darcs record -a -m initial
cd ..
darcs get master side
cd side
sed -i '5 s/^.*$/original change/' file
darcs record -a -m 'original change'
cd ../master
sed -i '5 s/.*/original change/' file
sed -i '15 s/^.*$/with an extra hunk/' file
darcs record -a -m 'original change'
sed -i '5 s/.*/modified change/' file
darcs record -a -m 'modified change'
darcs pull -a ../side
)
#!/bin/sh
#
# Git does not recognize matching upstream changes
#
testdir=test-darcs
mkdir "$testdir" || exit 1
(
cd "$testdir"
mkdir master
cd master
darcs init
for line in $(seq 20)
do
echo $line >>file
done
darcs add file
darcs record -a -m initial
cd ..
darcs get master side
cd side
sed -i '5 s/^.*$/original change/' file
darcs record -a -m 'original change'
cd ../master
sed -i '5 s/.*/original change/' file
sed -i '15 s/^.*$/with an extra hunk/' file
darcs record -a -m 'original change'
sed -i '5 s/.*/modified change/' file
darcs record -a -m 'modified change'
darcs pull -a ../side
)
From: Clemens Buchacher <hidden> Date: 2016-06-15 22:51:34
On Tue, Jul 12, 2011 at 11:07:16PM +0200, Clemens Buchacher wrote:
Exactly. The case I am talking about is where the patch-id's are
different but there are no conflicts. I have worked out an example
for git and darcs. Below are two scripts to demonstrate.
Oops, that was the same script twice. Here are the darcs and git
versions, respectively:
#!/bin/sh
#
# Darcs recognizes matching upstream changes
#
testdir=test-darcs
mkdir "$testdir" || exit 1
(
cd "$testdir"
mkdir master
cd master
darcs init
for line in $(seq 20)
do
echo $line >>file
done
darcs add file
darcs record -a -m initial
cd ..
darcs get master side
cd side
sed -i '5 s/^.*$/original change/' file
darcs record -a -m 'original change'
cd ../master
sed -i '5 s/.*/original change/' file
sed -i '15 s/^.*$/with an extra hunk/' file
darcs record -a -m 'original change'
sed -i '5 s/.*/modified change/' file
darcs record -a -m 'modified change'
darcs pull -a ../side
)
#!/bin/sh
#
# Git does not recognize matching upstream changes
#
testdir=test-git
mkdir "$testdir" || exit 1
(
cd "$testdir"
git init -q
for line in $(seq 20)
do
echo $line >>file
done
git add file
git commit -q -m initial
git checkout -q -b side
sed -i '5 s/^.*$/original change/' file
git add file
git commit -q -m 'original change'
git checkout -q master
sed -i '5 s/.*/original change/' file
sed -i '15 s/^.*$/with an extra hunk/' file
git add file
git commit -q -m 'original change'
sed -i '5 s/.*/modified change/' file
git add file
git commit -q -m 'modified change'
git rebase master side
git diff
)
From: Jeff King <hidden> Date: 2016-06-15 22:51:34
On Tue, Jul 12, 2011 at 11:07:16PM +0200, Clemens Buchacher wrote:
On Tue, Jul 12, 2011 at 03:45:40PM -0400, Jeff King wrote:
quoted
It has been a long time since I've looked at darcs, but from my
recollection, it will only work with specific patch types. That is, it
works if B and C are commutative. For text patches that touch the same
area, that is not the case. But if "B" were a token-renaming patch, for
example, I think it might work.
If they were commutative, we would not have a problem in git
either.
Except that git doesn't support many commutative special forms, like
token-renaming patches.
quoted
Anyway, that is not really relevant to git. I think we decided long ago
that being simple and stupid about the content changes (i.e., blob A
became blob B) is better in general, even when there are a few corner
cases that might have been better off the other way.
Yes, but that only applies to git merge. When we talk about
rebasing we are looking at individual patches rather than a single
global merge. For rebase I think "patch algebra" is very relevant,
and we have already implemented a simple patch algebra with
patch-id's.
I can buy that argument, but I think most of the benefit in darcs comes
from annotating your patch as "this is just renaming 'foo' to 'bar'" and
other special patch types. Because the algebraic properties of those
types is more interesting. And what we really don't want in git is
having to put the burden on users of making those annotations (not just
because it's annoying to do, but because when the annotation doesn't
match what's in the blobs, the results would be extremely confusing).
But if you are proposing that we could do run-time detection on those
sorts of patch properties and use the result in making a better rebase,
I don't think that's a bad idea (though I do wonder if the amount of
code will be worth it).
Again, it has been a long time since I've looked at darcs, and I was
never a serious user of it, so everything I say above may be utterly
wrong.
quoted
I'd be curious to see an example worked out. In my experience, even if
something like patch-ids don't match, it's not a big deal for the hunks
that do match, because when we get to the actual content merge, we will
realize that both sides made the same change to that hunk. So it's not
like you are getting unrelated conflicts; whatever small part of the
diff made the patch-id different will be the part where you get the
conflict, and the should merge cleanly.
I am reading that last part as "they should not merge cleanly".
Sorry, it was supposed to be "...and the rest should merge cleanly". But
I think you got my meaning.
Exactly. The case I am talking about is where the patch-id's are
different but there are no conflicts. I have worked out an example
for git and darcs. Below are two scripts to demonstrate. In the
example, the patch-id is different because upstream changes the
patch in a way that does not conflict with the original patch. It
simply adds another change that goes into a different hunk. Git
fails to merge cleanly because the patch-id's are different. It
presents the user with an awkward conflict that looks like a
revert. Darcs, on the other hand, merges cleanly. It recognizes the
fact that all changes from the original patch are contained
upstream and do not conflict with the upstream version. The fact
that more changes are added on top does not bother darcs.
Ah, OK, I see. Let me try to amend what I said earlier to make sure.
In the normal case of applying patch B on top of patch A, it doesn't
matter if we use per-hunk patch-ids or normal patch-ids. Because even if
we decide to actually go through with the merge of B on top of A, any
hunks that _would have_ had their per-hunk patch-ids match will merge
cleanly.
But in the real world, it is about applying patch Z on top of patches
A..Y, where Z has similar hunks to patch N. And then it _does_ make a
difference, because it is about skipping hunks from Z that are already
in N, but will end up applied on top of Y. And what's in Y and what's in
N may be quite different.
Does that sound right?
Now, one might argue that this is a corner case. But it's actually
very common. In the example, the patch-id changes because of an
extra change in a different text area. That is indeed unlikely.
However, the same problem will occur in a much more common case.
Let's say we have a patch with 10 hunks. The patch is applied
upstream, with only one difference in one of the hunks.
Subsequently, text areas affected by any of the other hunks change
upstream. When the original patch is rebased on top of that, it
will conflict with the one hunk that was changed in the upstream
version of that patch. And that's ok. Git should not decide which
version is correct. But in addition to that conflict there will
also be conflicts for all the other hunks, which the upstream patch
did _not_ modify. And all of those conflicts will look like
reverts.
On Mon, Jul 11, 2011 at 07:14:16PM -0500, Illia Bobyr wrote:
quoted
I am not 100% sure that my solution is exactly about this problem, but
it seems to be quite relevant.
I think that if you rebase "step-by-step" by doing, for this particular
example, something like
$ git rebase master^ topic
$ git rebase master topic
You will first see the /modified one/one/ conflict that you will resolve
your "two" against and then your second rebase will apply with no conflicts.
I have a set of scripts that help me do this kind of rebases by
essentially rebasing the topic branch against every single commit on the
upstream.
That makes a lot of sense to me as a strategy. Of course, as you
mention, it is horribly slow. And when you do have real conflicts, you
would end up looking at the same conflicts again and again (and as you
mention, rerere can be some help there, though not necessarily
perfect).
Well, I would like to comment on "horribly slow" a little bit :)
It is slower than a normal rebase, but it is much faster than, at least
in my case, me doing the conflict resolution on the final versions
without intermediate steps.
Also, in my case, it is faster than compiling every single commit of the
topic branch after the rebase. Something that I just have to do, again,
because if I do this only with the final version it gives me so much
errors all over the code, that I can hardly do anything with it.
Besides I would like the commits that were "incorrectly" merged and that
introduced the compilation error to contain the fixes, not have all the
fixes as a final "merge" commit.
In other words, while been slow, it is just one step in a general
process that, in my case, have steps that are slower.
Also, my guess is that, as rebase is an sh script it may be made faster
and a step-by-step rebase will became considerably faster as well, if it
would be rewritten in C. Though I have not looked a lot inside the
script, so I might be wrong.
I would like to note that in case of hundreds of rebases with dozens of
conflicts rerere's help is hard to overestimate. I have rebased my
topic branch through at least 500 commits for the last 6 month and as I
actually do not copy any changes into the master branch, some conflicts
stay we me for the whole time.
quoted
At the same time the less changes are in topic...master the faster it
would be and the more changes are there the more you benefit from a
gradual rebase.
Yeah, this seems like the real problem to me. It's one thing to rebase
on top of a single series that somebody has applied upstream. But if it
has been 2 weeks, there may be hundreds of commits, and doing hundreds
of rebases is awful. I wonder if you could do better by picking out some
"key" commits in master to rebase on top of using one of:
1. Divide-and-conquer the commit space. Try the rebase, starting on
the HEAD. If it works, great. If the user says "this is too hard",
then find the midpoint between where we tried to rebase and the
merge base, and try rebasing there. Every time it's too hard, go
back halfway to the start. Every time it's easy, try the new result
on top of HEAD.
So it's basically doing a O(lg n) search backwards for an easy
place to rebase, and then repeatedly checking if that was a good
spot (and repeating the backwards search if not). The worst case
complexity is O(n lg n) rebases. But in practice, you can hopefully
find the problematic spot in O(lg n), and then everything will just
work out after 1 or 2 problematic spots.
I have problems all over the upstream history :)
And the issue is not in exactly in finding a problem spot. It is about
giving the user (that is me in this case) something that he can merge in
a reasonable amount of time.
I have a topic branch with 174 commits. It takes my machine about 7
minutes to rebase it.
If I have a conflict that I do not understand, it may take me, on
average, two hours to figure out what a conflict is about and fix it.
Sometimes it may take 4 hours or even more if I have to involve other
developers.
If my machine will have to do 20 rebases or even 40 and it will present
me with simple conflicts that I can solve in seconds it would still be
better than if I will spend hours trying to figure out something and
give up by saying "this is too hard". Note that while it rebases I am
working on something else.
In my case sometimes even the most basic conflicts that arise because of
a rebase against just one commit may be hard to merge.
If I can avoid even one of these I will be happy to let one of my
machine cores run for hours :)
Obviously, my case is a wired case caused by not-the-best development
practices. But, I guess, one can still consider it as one of the points
on the curve that approximate this kind of use cases. A pretty extreme
point.
2. Use heuristics (like commit message content) to find related
commits. So if I have a 5-patch series, I can perhaps find the
likely commits upstream that match my patches, and those are
good places to try individual rebases. And then I don't care how
many commits are in master. If I have a 5 patch series, I won't do
more than 5 rebases.
But I've never tried this in practice. Maybe next time a rebase is ugly
I'll manually work through one of the methods and see how it fares.
I guess that I view this problem from a little different angle, as in my
case, it is not be exactly my own patches that are causing problems, but
an upstream changes that have other changes base on them.
Now I am guessing, but here is another idea.
I think that one can check the modification history of the lines in the
master commits we are rebasing against and in all the topic commits,
similar to what blame does.
Essentially take a set of all lines that were modified by the topic
branch (along with the context lines) and sets of lines modified by ever
single commit (without the context lines).
If a commit does not touch lines from the topic branch set it will not
cause conflicts and we can skip it. It will just cause offsets in the
line numbers when the patches will be applied.
If you are rebasing against a lot of changes that are unrelated to your
topic branch and if they are split into commits correctly, this way, I
guess, it would be possible to find those that may cause conflicts.
The actual rebase may still be able to go through some of them without
conflicts but I see this as a first approximation that might save time.
And it seems to give only false negatives.
Kind of a conflict prediction approximation.
Ilya
From: John Szakmeister <hidden> Date: 2016-06-15 22:51:34
On Tue, Jul 12, 2011 at 5:07 PM, Clemens Buchacher [off-list ref] wrote:
[snip]
Now, one might argue that this is a corner case. But it's actually
very common. In the example, the patch-id changes because of an
extra change in a different text area. That is indeed unlikely.
However, the same problem will occur in a much more common case.
Let's say we have a patch with 10 hunks. The patch is applied
upstream, with only one difference in one of the hunks.
Subsequently, text areas affected by any of the other hunks change
upstream. When the original patch is rebased on top of that, it
will conflict with the one hunk that was changed in the upstream
version of that patch. And that's ok. Git should not decide which
version is correct. But in addition to that conflict there will
also be conflicts for all the other hunks, which the upstream patch
did _not_ modify. And all of those conflicts will look like
reverts.
I believe that is the main reason why rebase is so painful all the
time.
Clemens, that's a great description of the problem. I've run into
this several times, and it is really confusing. I've spent
considerable time tracking down the real conflict... only to find the
real issue was in something non-related and easily resolved. IMHO, I
agree with you Clemens: this has been my major source of pain.
-John
From: Clemens Buchacher <hidden> Date: 2016-06-15 22:51:35
On Tue, Jul 12, 2011 at 05:36:07PM -0400, Jeff King wrote:
In the normal case of applying patch B on top of patch A, it doesn't
matter if we use per-hunk patch-ids or normal patch-ids. Because even if
we decide to actually go through with the merge of B on top of A, any
hunks that _would have_ had their per-hunk patch-ids match will merge
cleanly.
But in the real world, it is about applying patch Z on top of patches
A..Y, where Z has similar hunks to patch N. And then it _does_ make a
difference, because it is about skipping hunks from Z that are already
in N, but will end up applied on top of Y. And what's in Y and what's in
N may be quite different.
Does that sound right?
Yes, exactly.
And one possible solution would be to drop all hunks from Z which
are already somewhere in A..Y. But that undermines the whole
changeset idea.
If we detect the similarities between Z and N, then we could rebase
Z to N, make the user resolve any conflicts, which should make more
sense than what we would have between Z and Y. Then we have Z' on
top of N:
Z Z' Z"
/ / /
A--..--N--..--Y
Subsequently we rebase Z' to Y, at which point only changes remain
that we disagreed with. For those we may have to do conflict
resolution again. So, in some cases this approach could result in
more work.
Clemens
On Tue, Jul 12, 2011 at 05:36:07PM -0400, Jeff King wrote:
quoted
In the normal case of applying patch B on top of patch A, it doesn't
matter if we use per-hunk patch-ids or normal patch-ids. Because even if
we decide to actually go through with the merge of B on top of A, any
hunks that _would have_ had their per-hunk patch-ids match will merge
cleanly.
But in the real world, it is about applying patch Z on top of patches
A..Y, where Z has similar hunks to patch N. And then it _does_ make a
difference, because it is about skipping hunks from Z that are already
in N, but will end up applied on top of Y. And what's in Y and what's in
N may be quite different.
Does that sound right?
Yes, exactly.
And one possible solution would be to drop all hunks from Z which
are already somewhere in A..Y. But that undermines the whole
changeset idea.
If we detect the similarities between Z and N, then we could rebase
Z to N, make the user resolve any conflicts, which should make more
sense than what we would have between Z and Y. Then we have Z' on
top of N:
Z Z' Z"
/ / /
A--..--N--..--Y
Subsequently we rebase Z' to Y, at which point only changes remain
that we disagreed with. For those we may have to do conflict
resolution again. So, in some cases this approach could result in
more work.
This is where rerere helps, AFAIU.
And if the conflict is non-trivial there is a chance that it is really
something you would like to take a look at.