First, --fork-point
is meant for remote-tracking names. The way it works is that it uses the reflog for the supplied upstream. For more about this, see, e.g., Git rebase - commit select in fork-point mode.
Second—but maybe more important—you can run git rebase upstream
, git rebase --onto newbase upstream
, or even just git rebase
. When using the two argument form, you gain a lot of freedom:
The upstream
argument limits which commits are going to be copied. We'll have more to say about this in a moment.
The newbase
argument chooses which commit is the point after which the copies are to be added. The actual hash ID here is very important.
That is, when you do use --onto
, you must pick an exact commit for the --onto
newbase
argument. This means you can be very loose about what you use as the upstream
argument. But when you don't use --onto
, upstream
gets used for both purposes, so the upstream
parameter is the one that requires a lot of careful preciseness. One purpose is loose and free, and the other isn't.
What this means that you can use two arguments to regain any extra looseness you'd like (but may not need), or no arguments to gain convenience.
(The next part is constructed oddly due to an update to the question.)
git show-branch --merge-base X Y
= git merge-base X Y
The git show-branch
command now takes --merge-base
or --independent
when given multiple arguments. With exactly two arguments, it does the same thing as git merge-base X Y
, i.e., it finds the merge base(s) of revisions X
and Y
. (git merge-base
also now takes --independent
, rather than just assuming the octopus strategy, but this only applies when using three or more commit specifiers.)
I prefer git merge-base
here, as I think it's more obvious (and of course a little shorter to type in).
With no arguments, the upstream comes from your branch settings
Every branch can have one (but only one) upstream setting.
The upstream setting of any branch B is usually origin/B
, because we tend to push them to GitHub or Bitbucket or some corporate web server, whose URL we stick under the name origin
so that we don't have to type it out all the time. If you've used up your one upstream on origin/
, and want to have a second setting that git rebase
will use automatically, you're sort of out of luck (but read on). But if you haven't used up the one upstream, just set the upstream to what you'd like git rebase
to use automatically. For instance, if you're on feature-X
now and want it to rebase on develop
:
$ git branch --set-upstream-to=develop
and now feature
has develop
as its one upstream. Running git rebase
(with or without -i
) will rebase as if you ran the same command with develop
as its upstream
argument.
If you have used up the one upstream, you can make your own alias:
alias.name = !git rebase "$@" $(git config --get branch.$(git symbolic-ref --short HEAD).base) #
(pick some name): this lets you configure, using git config
, an extra name, branch.feature-X.base
, to develop
. The $(git symbolic-ref --short)
extracts the current branch name, the git config --get
gets the setting, and the rebase then uses that as its one upstream argument.
The drawback to a single limiter-and-target / newbase argument
The drawback here is that, given a graph of the form:
o--o <-- develop
/
...--o--o--o
\
A--B--C <-- feature-X (HEAD)
you wind up with copies that come after the tip of develop
:
A'-B'-C' <-- feature-X (HEAD)
/
o--o <-- develop
/
...--o--o--o
\
A--B--C [abandoned]
when you want to keep the copies in the same place, just fuss with their commit text or maybe squash two together or something:
o--o <-- develop
/
...--o--o--o--A'-B'-C' <-- feature-X (HEAD)
\
A--B--C [abandoned]
Using --onto
With the two-argument form, the --onto
parameter picks the target commit:
o--o <-- develop or whatever
/
...--o--o--* [pick this commit as target]
\
A--B--C <-- feature-X (HEAD)
The copies will now go after *
. The set of commits to be copied is determined by using, in effect, upstream..feature-X
: that is, the commits reachable by starting at feature-X
and working backwards, but excluding commits reachable by starting at upstream
and working backwards.
Now you need only find commit *
. If you have two names, such as feature-X
and develop
, you can use the gitrevisions three-dot syntax, develop...feature-X
or feature-X...develop
(the syntax is symmetric when there is only one merge base like this), to specify commit *
. This only works in fairly new versions of Git: in older ones, use git show-branch
or git merge-base
(with two commit hash IDs they both behave the same way).
Having specified commit *
as the --onto
target, you can again allow the branch's upstream to work as the limiter. That is, you can omit the explicit upstream
because it defaults to the actual upstream. And, since you can use the @{upstream}
or @{u}
syntax, you can make a very short and simple alias, as you did in your own answer.
If you want to keep a separate upstream (origin/feature-X
) and base, you can go back to the idea of configuring an extra value per branch name. In this case, you'll need to use it twice, so instead of an alias, you might want a full blown script, where you can do error checking:
#! /bin/sh
# git-base - rebase on the current branch's base setting
. git-sh-setup
branch=$(git symbolic-ref --short HEAD) || exit
base=$(git config --get branch.$branch.base) || die "branch.$branch.base is not set"
mbase=$(git merge-base $base HEAD) || die "there is no merge base"
git rebase --onto $mbase "$@" $base
Name this git-base
, put it in your path, and you can now run git base
.