Re: What I want rebase to do

2013-03-07 Thread Thomas Rast
wor...@alum.mit.edu (Dale R. Worley) writes:
[...snip...]

Isn't that just a very long-winded way of restating what Junio said
earlier:

  It was suggested to make it apply the first-parent diff and record
  the result, I think.  If that were an acceptable approach (I didn't
  think about it through myself, though), that would automatically
  cover the evil-merge case as well.

You can fake that with something like

git rev-list --first-parent --reverse RANGE_TO_REBASE |
while read rev; do
if git rev-parse $rev^2 /dev/null 21; then
git cherry-pick -n -m1 $rev
git rev-parse $rev^2 .git/MERGE_HEAD
git commit -C$rev
else
git cherry-pick $rev
fi
done

Only tested very lightly.  Dealing with octopii, conflicts and actually
preserving the commit's attributes is left as an exercise to the
reader[1].

I still think that the _right_ solution is first redoing the merge on
its original parents and then seeing how the actual merge differs from
that.  --preserve-merges has bigger issues though, like Junio said.

Perhaps a new option to git-rebase could trigger the above behavior for
merges, who knows.  (It could be called --first-parent.)


[1]  If you don't get the sarcasm: that would amount to reinventing
large parts of git-rebase.

-- 
Thomas Rast
trast@{inf,student}.ethz.ch
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: What I want rebase to do

2013-03-07 Thread Johannes Sixt
Am 3/7/2013 9:48, schrieb Thomas Rast:
 wor...@alum.mit.edu (Dale R. Worley) writes:
 [...snip...]
 
 Isn't that just a very long-winded way of restating what Junio said
 earlier:
 
 It was suggested to make it apply the first-parent diff and record
 the result, I think.  If that were an acceptable approach (I didn't
 think about it through myself, though), that would automatically
 cover the evil-merge case as well.
 
 You can fake that with something like
 
 git rev-list --first-parent --reverse RANGE_TO_REBASE |
 while read rev; do
 if git rev-parse $rev^2 /dev/null 21; then
 git cherry-pick -n -m1 $rev
 git rev-parse $rev^2 .git/MERGE_HEAD
 git commit -C$rev
 else
 git cherry-pick $rev
 fi
 done
 
 Only tested very lightly.  Dealing with octopii, conflicts and actually
 preserving the commit's attributes is left as an exercise to the
 reader[1].

I proposed this long ago, but by modifying preserve-merges rather than
with a new option (--first-parent):
http://thread.gmane.org/gmane.comp.version-control.git/198125

It works very well. I'm using it frequently in the field.

-- Hannes
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: What I want rebase to do

2013-03-07 Thread Junio C Hamano
Thomas Rast tr...@student.ethz.ch writes:

 I still think that the _right_ solution is first redoing the merge on
 its original parents and then seeing how the actual merge differs from
 that.

I think that is what was suggested in

http://article.gmane.org/gmane.comp.version-control.git/198316

 Perhaps a new option to git-rebase could trigger the above behavior for
 merges, who knows.  (It could be called --first-parent.)

Yeah, I think that is what the old thread concluded to be the way to
move forward:

http://thread.gmane.org/gmane.comp.version-control.git/198125

I'll throw it in to the leftover bits.

http://git-blame.blogspot.com/2013/02/more-leftover-bits.html


--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: What I want rebase to do

2013-03-07 Thread Dale R. Worley
 From: Thomas Rast tr...@student.ethz.ch
 
 wor...@alum.mit.edu (Dale R. Worley) writes:
 [...snip...]
 
 Isn't that just a very long-winded way of restating what Junio said
 earlier:
 
   It was suggested to make it apply the first-parent diff and record
   the result, I think.  If that were an acceptable approach (I didn't
   think about it through myself, though), that would automatically
   cover the evil-merge case as well.

Well, I believe what I said was a fleshed-out way of saying what I
*think* Junio said, but...

 You can fake that with something like
 
 git rev-list --first-parent --reverse RANGE_TO_REBASE |
 while read rev; do
 if git rev-parse $rev^2 /dev/null 21; then
 git cherry-pick -n -m1 $rev
 git rev-parse $rev^2 .git/MERGE_HEAD
 git commit -C$rev
 else
 git cherry-pick $rev
 fi
 done

This code doesn't do that.  I don't want something that rebases a
single thread of the current branch, I want something that rebases
*all* the commits between the head commit and the merge base.  Which
is what is illustrated in my message.

 [1]  If you don't get the sarcasm: that would amount to reinventing
 large parts of git-rebase.

Yes, that is the point of the exercise.

I've done a proof-of-concept implementation of what I want to see,
calling it git-rebase--merge-safe.  But I'm new here and likely that
is a pretty crude solution.  I suspect that a real implementation
could be done by inserting this logic into the framework of
git-filter-tree.  Following is git-rebase--merge-safe, and the script
I use to test it (and explore rebase problems).

Dale
--
git-rebase--merge-safe

#!/bin/bash

. git-sh-setup

prec=4

set -ex

# Ensure the work tree is clean.
require_clean_work_tree rebase Please commit or stash them.

onto_name=$1
onto=$(git rev-parse --verify ${onto_name}^0) ||
die Does not point to a valid commit: $1

head_name=$( git symbolic-ref HEAD )
orig_head=$(git rev-parse --verify $head_name) ||
exit 1

echo onto=$onto
echo head_name=$head_name
echo orig_head=$orig_head

# Get the merge base, which is the root of the branch that we are rebasing.
# (For now, ignore the question of whether there is more than one merge base.)
mb=$(git merge-base $onto $orig_head)
echo mb=$mb

# Get the list of commits to rebase, which is everything between $mb and
# $orig_head.
# Note that $mb is not included.
revisions=`git rev-list --reverse --ancestry-path $mb..$orig_head`
echo revisions=$revisions

# Set up the list mapping the commits on the original branch to the commits
# on the branch we are creating.
# Its format is ,old-hash1/new-hash1,old-hash2/new-hash2,...,.
# The initial value maps $mb to $onto.
map=,$mb/$onto,

# Export these so git commit can see them.
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE

# Process each commit in forward topological order.
for cmt in $revisions
do
# Examine the commit to extract information we will need to reconstruct it.
# First parent of the commit that has a mapping, i.e., is part of the
# branch (and has thus been rebuilt already.
first_mapped_parent=
# The new commit that was made of $first_mapped_parent.
first_mapped_parent_mapped=
# List of -p options naming the parent commits, or their new commits if they
# are in the branch.
parents=
   # Dissect the old commit's data.
# Output the commit data into FD 3.
exec 3 ( git cat-file commit $cmt )

while read keyword rest 3
do
case $keyword in
tree)
# Ignored
;;
parent)
# See if the parent is mapped, i.e., is in the
# original branch.
if [[ $map == *,$rest/* ]]
then
# This parent has been mapped.  Get the new commit.
parent_mapped=${map#*,$rest/}
parent_mapped=${parent_mapped%%,*}
if test -z $first_mapped_parent
then
first_mapped_parent=$rest
first_mapped_parent_mapped=$parent_mapped
fi
else
# This parent has not been mapped.
parent_mapped=$rest
fi
# $parent_mapped is a parent of the new commit.
parents=$parents -p $parent_mapped
;;
author)
# Extract the information about the author.
GIT_AUTHOR_NAME=${rest%% *}
GIT_AUTHOR_EMAIL=${rest##* }
GIT_AUTHOR_EMAIL=${GIT_AUTHOR_EMAIL%% *}
GIT_AUTHOR_DATE=${rest##* }
;;
committer)
# Ignored:  The new commit will have this use's name
# as committer.
;;
'')
# End of fixed fields, remainder is the 

What I want rebase to do

2013-03-06 Thread Dale R. Worley
This is how I see what rebase should do:

The simple case for rebase starts from

   P---Q---R---S master
\
 A---B---C topic

Then git checkout topic ; git rebase master will change it to

   P---Q---R---S master
\
 A'--B'--C' topic

A' is created by a three-way merge that can be symbolized

Q--S
|   |
v   v
A--A'

That is, Q, applying the changes from Q to A and the changes from Q to
S, becomes A'.

After that

A--A'
|   |
v   v
B--B'
|   |
v   v
C--C'

A more complex case is when there is a merge from an external source

   P---Q---R---S master
\
 A---M---C topic
/
---X

We want to produce

   P---Q---R---S master
\
 A'--M'--C' topic
/
---X

So we have to merge

Q--S
|   |
v   v
A--A'
|   |
v   v
M--M'
|   |
v   v
C--C'

Any evil changes in M will be in the changes A-M (along with the
changes introduced from X), and so they will be reincorporated in
A'-M'.  M' lists A' and X as its parents.  (And not M!)

If there is an internal merge in the topic branch, things look like
this

   P---Q---R---S master
\
 \   B
  \ / \
   A   M---D topic
\ /
 C

and we want to produce this

   P---Q---R---S master
\
 \B'
  \  /  \
   A'M'--D' topic
 \  /
  C'

Which can be done with these merges


Q--S
|   |
v   v
A--A'A--A'
|   | |   | 
v   v v   v 
B--B'C--C'

There are two choices for constructing M' (which ought to produce the
same results under ordinary circumstances)

B--B'C--C'
|   | |   | 
v   v v   v 
M--M'M--M'

and finally

M--M'
|   |
v   v
D--D'

Dale
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html