> From: Alcolo Alcolo <alcol...@gmail.com>

> My git history is complex and it's a nightmare to rebase from old commits.

I'm not sure what you're attempting to do, but this might help.  It's
a rebasing script I wrote.  It appears that it was based on something
Junio wrote.  The concept is that it rebases the current branch, but
unlike the standard git-rebase, it replicates the merging structure
between the merge-base commit and the head of the branch.  (IIRC, the
standard git-rebase creates a linear branch based on some ordering of
the commits in the old branch.)

Dale
----------------------------------------------------------------------
#!/bin/bash
#
# Copyright (c) 2010 Junio C Hamano.
# Modified by Dale R. Worley.

# Usage:  git-rebase--merge-safe <onto>
#
# Rebase the current branch to commit/branch <onto>, replicating the commit
# graph between HEAD and the merge base.

. 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 user's name
                # as committer.
                ;;
            '')
                # End of fixed fields, remainder is the commit comment.
                # Leave contents of FD 3 queued to be read later by
                # git commit-tree.
                break
                ;;
            *)
                # Ignore all other keywords.
                ;;
        esac
    done

    echo GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME"
    echo GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL"
    echo GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE"
    echo parents="$parents"
    echo first_mapped_parent=$first_mapped_parent
    echo first_mapped_parent_mapped=$first_mapped_parent_mapped

    test -n "$first_mapped_parent" || exit 1

    # Do the three-way merge.
    # Empty the index so git read-tree will merge into it.
    git read-tree --empty
    git read-tree -m --aggressive \
        $first_mapped_parent $cmt $first_mapped_parent_mapped
    git merge-index git-merge-one-file -a

    # Construct the file tree for the new commit.
    tree=$( git write-tree )

    # Create the new commit
    # Note that FD 3 contains the remainder of the commit description
    # from the git cat-file above.
    new_commit=$( git commit-tree $tree $parents <&3 )
    echo new_commit=$new_commit

    # Add the new commit to the map.
    map="$map$cmt/$new_commit,"

done

echo Final commit is $new_commit

# Update the branch pointers.
git update-ref ORIG_HEAD $orig_head
git update-ref $head_name $new_commit

# Go to the new head of the branch.
git checkout ${head_name#refs/heads/}
----------------------------------------------------------------------

-- 
You received this message because you are subscribed to the Google Groups "Git 
for human beings" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to git-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to