Re: What I want rebase to do
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
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
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
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
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