Can I rebase on a branch's fork point without explicitly specifying the parent?
up vote
3
down vote
favorite
I often use git rebase -i
to clean up my history before publishing it. Usually I want to edit commits back to wherever the current branch forked off, without changing its fork point. I do it something like this: git rebase -i $(git show-branch --merge-base $PARENT_BRANCH HEAD)
It's an ugly command and I'm trying to find a better way. As long as I'm at it, I'd like to have git automatically figure out the right parent.
I think what I want is an alias for git rebase -i --fork-point $(something)
, where something
finds the branch with the most recent common ancestor of the current branch. It doesn't need to be bulletproof. If it works for a linear topic branch, that's good enough for my purposes.
git
add a comment |
up vote
3
down vote
favorite
I often use git rebase -i
to clean up my history before publishing it. Usually I want to edit commits back to wherever the current branch forked off, without changing its fork point. I do it something like this: git rebase -i $(git show-branch --merge-base $PARENT_BRANCH HEAD)
It's an ugly command and I'm trying to find a better way. As long as I'm at it, I'd like to have git automatically figure out the right parent.
I think what I want is an alias for git rebase -i --fork-point $(something)
, where something
finds the branch with the most recent common ancestor of the current branch. It doesn't need to be bulletproof. If it works for a linear topic branch, that's good enough for my purposes.
git
If you want to squash everything back to the merge-base: git reset --soft $(git merge-base HEAD @u); git commit -m 'squash!'
– G. Sylvie Davies
Nov 10 at 5:37
Squash or no squash, I suspect "git merge-base HEAD @u" would be helpful here...
– G. Sylvie Davies
Nov 10 at 5:42
add a comment |
up vote
3
down vote
favorite
up vote
3
down vote
favorite
I often use git rebase -i
to clean up my history before publishing it. Usually I want to edit commits back to wherever the current branch forked off, without changing its fork point. I do it something like this: git rebase -i $(git show-branch --merge-base $PARENT_BRANCH HEAD)
It's an ugly command and I'm trying to find a better way. As long as I'm at it, I'd like to have git automatically figure out the right parent.
I think what I want is an alias for git rebase -i --fork-point $(something)
, where something
finds the branch with the most recent common ancestor of the current branch. It doesn't need to be bulletproof. If it works for a linear topic branch, that's good enough for my purposes.
git
I often use git rebase -i
to clean up my history before publishing it. Usually I want to edit commits back to wherever the current branch forked off, without changing its fork point. I do it something like this: git rebase -i $(git show-branch --merge-base $PARENT_BRANCH HEAD)
It's an ugly command and I'm trying to find a better way. As long as I'm at it, I'd like to have git automatically figure out the right parent.
I think what I want is an alias for git rebase -i --fork-point $(something)
, where something
finds the branch with the most recent common ancestor of the current branch. It doesn't need to be bulletproof. If it works for a linear topic branch, that's good enough for my purposes.
git
git
edited Nov 10 at 2:42
asked Nov 10 at 0:01
Andrew
1,5402923
1,5402923
If you want to squash everything back to the merge-base: git reset --soft $(git merge-base HEAD @u); git commit -m 'squash!'
– G. Sylvie Davies
Nov 10 at 5:37
Squash or no squash, I suspect "git merge-base HEAD @u" would be helpful here...
– G. Sylvie Davies
Nov 10 at 5:42
add a comment |
If you want to squash everything back to the merge-base: git reset --soft $(git merge-base HEAD @u); git commit -m 'squash!'
– G. Sylvie Davies
Nov 10 at 5:37
Squash or no squash, I suspect "git merge-base HEAD @u" would be helpful here...
– G. Sylvie Davies
Nov 10 at 5:42
If you want to squash everything back to the merge-base: git reset --soft $(git merge-base HEAD @u); git commit -m 'squash!'
– G. Sylvie Davies
Nov 10 at 5:37
If you want to squash everything back to the merge-base: git reset --soft $(git merge-base HEAD @u); git commit -m 'squash!'
– G. Sylvie Davies
Nov 10 at 5:37
Squash or no squash, I suspect "git merge-base HEAD @u" would be helpful here...
– G. Sylvie Davies
Nov 10 at 5:42
Squash or no squash, I suspect "git merge-base HEAD @u" would be helpful here...
– G. Sylvie Davies
Nov 10 at 5:42
add a comment |
2 Answers
2
active
oldest
votes
up vote
3
down vote
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
.
"it may be the case that you aren't actually using a parent branch name here, or are running git show-branch --merge-base HEAD $PARENT_BRANCH." It probably varies, honestly, depending on whether I remember the HEAD part on any given day. Fixed that line in the question so it actually makes sense.
– Andrew
Nov 10 at 2:47
I saw (and edited my answer accordingly).
– torek
Nov 10 at 2:48
I'm not sure which answer to mark as accepted. Mine concisely answers exactly the question I asked, but yours is much more informative in general. Is there established etiquette on this?
– Andrew
Nov 14 at 18:46
Might as well take your own, I guess. I'm not sure what if anything is standard here.
– torek
Nov 14 at 22:34
Okay, done. But I added a link to yours for a more detailed explanation of the mechanics behind the solution.
– Andrew
Nov 15 at 0:51
add a comment |
up vote
2
down vote
accepted
After plugging at this for an hour or so, I came up with something good-enough. This command does what I want if the branch has an upstream set, and if said upstream is the one I want to compare against:
$ git rebase -i --onto @upstream...HEAD
Those triple dots do not do the same thing as in git-log and similar commands. From the git-rebase documentation:
As a special case, you may use "A...B" as a shortcut for the merge base of
A and B if there is exactly one merge base.
So this is saying "find the merge-base of HEAD and its own upstream, and rebase against that". Normally local branches have no upstream, but it can be set, and there is a gitconfig option (autoSetupMerge) that will do it automatically for new branches. Hence:
$ git config --global branch.autoSetupMerge always
$ git config --global alias.fixup 'rebase -i --onto @upstream...HEAD'
$ git branch childbranch -u parentbranch # Repeat for other branches as needed.
After this I can edit history back to the branch point easily with:
git fixup
And it works for all future branches.
(note: See torek's answer for a detailed explanation of what's going on under the hood here)
Using--onto
is probably the way to go. I've been working on a long-form answer. That three-dot syntax is the correct syntax for specifying the (well, a) merge base; see gitrevisions.
– torek
Nov 10 at 2:08
Can you quote where gitrevisions says that? All the references I can find to triple-dots use it to mean roughly "the set of commits going back to the merge-base" not "the merge-base commit itself". I was surprised to find that it worked that way with --onto.
– Andrew
Nov 10 at 2:20
It's a little sneaky, because A...B really is a range syntax for anything that uses ranges. The trick is that Git commands that want a single number pass that togit rev-parse
(or the C code equivalent), which spits out the two end points as positive references and the merge base(s) as negative references.git diff
and, in modern Git,git rebase
, know how to handle that.
– torek
Nov 10 at 2:26
In this particular case, if you examine the old shell-script rebase setup code, it explicitly checks for the three-dot syntax and runsgit merge-base
. The new code is very similar.
– torek
Nov 10 at 2:38
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
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
.
"it may be the case that you aren't actually using a parent branch name here, or are running git show-branch --merge-base HEAD $PARENT_BRANCH." It probably varies, honestly, depending on whether I remember the HEAD part on any given day. Fixed that line in the question so it actually makes sense.
– Andrew
Nov 10 at 2:47
I saw (and edited my answer accordingly).
– torek
Nov 10 at 2:48
I'm not sure which answer to mark as accepted. Mine concisely answers exactly the question I asked, but yours is much more informative in general. Is there established etiquette on this?
– Andrew
Nov 14 at 18:46
Might as well take your own, I guess. I'm not sure what if anything is standard here.
– torek
Nov 14 at 22:34
Okay, done. But I added a link to yours for a more detailed explanation of the mechanics behind the solution.
– Andrew
Nov 15 at 0:51
add a comment |
up vote
3
down vote
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
.
"it may be the case that you aren't actually using a parent branch name here, or are running git show-branch --merge-base HEAD $PARENT_BRANCH." It probably varies, honestly, depending on whether I remember the HEAD part on any given day. Fixed that line in the question so it actually makes sense.
– Andrew
Nov 10 at 2:47
I saw (and edited my answer accordingly).
– torek
Nov 10 at 2:48
I'm not sure which answer to mark as accepted. Mine concisely answers exactly the question I asked, but yours is much more informative in general. Is there established etiquette on this?
– Andrew
Nov 14 at 18:46
Might as well take your own, I guess. I'm not sure what if anything is standard here.
– torek
Nov 14 at 22:34
Okay, done. But I added a link to yours for a more detailed explanation of the mechanics behind the solution.
– Andrew
Nov 15 at 0:51
add a comment |
up vote
3
down vote
up vote
3
down vote
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
.
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
.
edited Nov 10 at 2:47
answered Nov 10 at 2:24
torek
178k16228305
178k16228305
"it may be the case that you aren't actually using a parent branch name here, or are running git show-branch --merge-base HEAD $PARENT_BRANCH." It probably varies, honestly, depending on whether I remember the HEAD part on any given day. Fixed that line in the question so it actually makes sense.
– Andrew
Nov 10 at 2:47
I saw (and edited my answer accordingly).
– torek
Nov 10 at 2:48
I'm not sure which answer to mark as accepted. Mine concisely answers exactly the question I asked, but yours is much more informative in general. Is there established etiquette on this?
– Andrew
Nov 14 at 18:46
Might as well take your own, I guess. I'm not sure what if anything is standard here.
– torek
Nov 14 at 22:34
Okay, done. But I added a link to yours for a more detailed explanation of the mechanics behind the solution.
– Andrew
Nov 15 at 0:51
add a comment |
"it may be the case that you aren't actually using a parent branch name here, or are running git show-branch --merge-base HEAD $PARENT_BRANCH." It probably varies, honestly, depending on whether I remember the HEAD part on any given day. Fixed that line in the question so it actually makes sense.
– Andrew
Nov 10 at 2:47
I saw (and edited my answer accordingly).
– torek
Nov 10 at 2:48
I'm not sure which answer to mark as accepted. Mine concisely answers exactly the question I asked, but yours is much more informative in general. Is there established etiquette on this?
– Andrew
Nov 14 at 18:46
Might as well take your own, I guess. I'm not sure what if anything is standard here.
– torek
Nov 14 at 22:34
Okay, done. But I added a link to yours for a more detailed explanation of the mechanics behind the solution.
– Andrew
Nov 15 at 0:51
"it may be the case that you aren't actually using a parent branch name here, or are running git show-branch --merge-base HEAD $PARENT_BRANCH." It probably varies, honestly, depending on whether I remember the HEAD part on any given day. Fixed that line in the question so it actually makes sense.
– Andrew
Nov 10 at 2:47
"it may be the case that you aren't actually using a parent branch name here, or are running git show-branch --merge-base HEAD $PARENT_BRANCH." It probably varies, honestly, depending on whether I remember the HEAD part on any given day. Fixed that line in the question so it actually makes sense.
– Andrew
Nov 10 at 2:47
I saw (and edited my answer accordingly).
– torek
Nov 10 at 2:48
I saw (and edited my answer accordingly).
– torek
Nov 10 at 2:48
I'm not sure which answer to mark as accepted. Mine concisely answers exactly the question I asked, but yours is much more informative in general. Is there established etiquette on this?
– Andrew
Nov 14 at 18:46
I'm not sure which answer to mark as accepted. Mine concisely answers exactly the question I asked, but yours is much more informative in general. Is there established etiquette on this?
– Andrew
Nov 14 at 18:46
Might as well take your own, I guess. I'm not sure what if anything is standard here.
– torek
Nov 14 at 22:34
Might as well take your own, I guess. I'm not sure what if anything is standard here.
– torek
Nov 14 at 22:34
Okay, done. But I added a link to yours for a more detailed explanation of the mechanics behind the solution.
– Andrew
Nov 15 at 0:51
Okay, done. But I added a link to yours for a more detailed explanation of the mechanics behind the solution.
– Andrew
Nov 15 at 0:51
add a comment |
up vote
2
down vote
accepted
After plugging at this for an hour or so, I came up with something good-enough. This command does what I want if the branch has an upstream set, and if said upstream is the one I want to compare against:
$ git rebase -i --onto @upstream...HEAD
Those triple dots do not do the same thing as in git-log and similar commands. From the git-rebase documentation:
As a special case, you may use "A...B" as a shortcut for the merge base of
A and B if there is exactly one merge base.
So this is saying "find the merge-base of HEAD and its own upstream, and rebase against that". Normally local branches have no upstream, but it can be set, and there is a gitconfig option (autoSetupMerge) that will do it automatically for new branches. Hence:
$ git config --global branch.autoSetupMerge always
$ git config --global alias.fixup 'rebase -i --onto @upstream...HEAD'
$ git branch childbranch -u parentbranch # Repeat for other branches as needed.
After this I can edit history back to the branch point easily with:
git fixup
And it works for all future branches.
(note: See torek's answer for a detailed explanation of what's going on under the hood here)
Using--onto
is probably the way to go. I've been working on a long-form answer. That three-dot syntax is the correct syntax for specifying the (well, a) merge base; see gitrevisions.
– torek
Nov 10 at 2:08
Can you quote where gitrevisions says that? All the references I can find to triple-dots use it to mean roughly "the set of commits going back to the merge-base" not "the merge-base commit itself". I was surprised to find that it worked that way with --onto.
– Andrew
Nov 10 at 2:20
It's a little sneaky, because A...B really is a range syntax for anything that uses ranges. The trick is that Git commands that want a single number pass that togit rev-parse
(or the C code equivalent), which spits out the two end points as positive references and the merge base(s) as negative references.git diff
and, in modern Git,git rebase
, know how to handle that.
– torek
Nov 10 at 2:26
In this particular case, if you examine the old shell-script rebase setup code, it explicitly checks for the three-dot syntax and runsgit merge-base
. The new code is very similar.
– torek
Nov 10 at 2:38
add a comment |
up vote
2
down vote
accepted
After plugging at this for an hour or so, I came up with something good-enough. This command does what I want if the branch has an upstream set, and if said upstream is the one I want to compare against:
$ git rebase -i --onto @upstream...HEAD
Those triple dots do not do the same thing as in git-log and similar commands. From the git-rebase documentation:
As a special case, you may use "A...B" as a shortcut for the merge base of
A and B if there is exactly one merge base.
So this is saying "find the merge-base of HEAD and its own upstream, and rebase against that". Normally local branches have no upstream, but it can be set, and there is a gitconfig option (autoSetupMerge) that will do it automatically for new branches. Hence:
$ git config --global branch.autoSetupMerge always
$ git config --global alias.fixup 'rebase -i --onto @upstream...HEAD'
$ git branch childbranch -u parentbranch # Repeat for other branches as needed.
After this I can edit history back to the branch point easily with:
git fixup
And it works for all future branches.
(note: See torek's answer for a detailed explanation of what's going on under the hood here)
Using--onto
is probably the way to go. I've been working on a long-form answer. That three-dot syntax is the correct syntax for specifying the (well, a) merge base; see gitrevisions.
– torek
Nov 10 at 2:08
Can you quote where gitrevisions says that? All the references I can find to triple-dots use it to mean roughly "the set of commits going back to the merge-base" not "the merge-base commit itself". I was surprised to find that it worked that way with --onto.
– Andrew
Nov 10 at 2:20
It's a little sneaky, because A...B really is a range syntax for anything that uses ranges. The trick is that Git commands that want a single number pass that togit rev-parse
(or the C code equivalent), which spits out the two end points as positive references and the merge base(s) as negative references.git diff
and, in modern Git,git rebase
, know how to handle that.
– torek
Nov 10 at 2:26
In this particular case, if you examine the old shell-script rebase setup code, it explicitly checks for the three-dot syntax and runsgit merge-base
. The new code is very similar.
– torek
Nov 10 at 2:38
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
After plugging at this for an hour or so, I came up with something good-enough. This command does what I want if the branch has an upstream set, and if said upstream is the one I want to compare against:
$ git rebase -i --onto @upstream...HEAD
Those triple dots do not do the same thing as in git-log and similar commands. From the git-rebase documentation:
As a special case, you may use "A...B" as a shortcut for the merge base of
A and B if there is exactly one merge base.
So this is saying "find the merge-base of HEAD and its own upstream, and rebase against that". Normally local branches have no upstream, but it can be set, and there is a gitconfig option (autoSetupMerge) that will do it automatically for new branches. Hence:
$ git config --global branch.autoSetupMerge always
$ git config --global alias.fixup 'rebase -i --onto @upstream...HEAD'
$ git branch childbranch -u parentbranch # Repeat for other branches as needed.
After this I can edit history back to the branch point easily with:
git fixup
And it works for all future branches.
(note: See torek's answer for a detailed explanation of what's going on under the hood here)
After plugging at this for an hour or so, I came up with something good-enough. This command does what I want if the branch has an upstream set, and if said upstream is the one I want to compare against:
$ git rebase -i --onto @upstream...HEAD
Those triple dots do not do the same thing as in git-log and similar commands. From the git-rebase documentation:
As a special case, you may use "A...B" as a shortcut for the merge base of
A and B if there is exactly one merge base.
So this is saying "find the merge-base of HEAD and its own upstream, and rebase against that". Normally local branches have no upstream, but it can be set, and there is a gitconfig option (autoSetupMerge) that will do it automatically for new branches. Hence:
$ git config --global branch.autoSetupMerge always
$ git config --global alias.fixup 'rebase -i --onto @upstream...HEAD'
$ git branch childbranch -u parentbranch # Repeat for other branches as needed.
After this I can edit history back to the branch point easily with:
git fixup
And it works for all future branches.
(note: See torek's answer for a detailed explanation of what's going on under the hood here)
edited Nov 15 at 0:50
answered Nov 10 at 2:00
Andrew
1,5402923
1,5402923
Using--onto
is probably the way to go. I've been working on a long-form answer. That three-dot syntax is the correct syntax for specifying the (well, a) merge base; see gitrevisions.
– torek
Nov 10 at 2:08
Can you quote where gitrevisions says that? All the references I can find to triple-dots use it to mean roughly "the set of commits going back to the merge-base" not "the merge-base commit itself". I was surprised to find that it worked that way with --onto.
– Andrew
Nov 10 at 2:20
It's a little sneaky, because A...B really is a range syntax for anything that uses ranges. The trick is that Git commands that want a single number pass that togit rev-parse
(or the C code equivalent), which spits out the two end points as positive references and the merge base(s) as negative references.git diff
and, in modern Git,git rebase
, know how to handle that.
– torek
Nov 10 at 2:26
In this particular case, if you examine the old shell-script rebase setup code, it explicitly checks for the three-dot syntax and runsgit merge-base
. The new code is very similar.
– torek
Nov 10 at 2:38
add a comment |
Using--onto
is probably the way to go. I've been working on a long-form answer. That three-dot syntax is the correct syntax for specifying the (well, a) merge base; see gitrevisions.
– torek
Nov 10 at 2:08
Can you quote where gitrevisions says that? All the references I can find to triple-dots use it to mean roughly "the set of commits going back to the merge-base" not "the merge-base commit itself". I was surprised to find that it worked that way with --onto.
– Andrew
Nov 10 at 2:20
It's a little sneaky, because A...B really is a range syntax for anything that uses ranges. The trick is that Git commands that want a single number pass that togit rev-parse
(or the C code equivalent), which spits out the two end points as positive references and the merge base(s) as negative references.git diff
and, in modern Git,git rebase
, know how to handle that.
– torek
Nov 10 at 2:26
In this particular case, if you examine the old shell-script rebase setup code, it explicitly checks for the three-dot syntax and runsgit merge-base
. The new code is very similar.
– torek
Nov 10 at 2:38
Using
--onto
is probably the way to go. I've been working on a long-form answer. That three-dot syntax is the correct syntax for specifying the (well, a) merge base; see gitrevisions.– torek
Nov 10 at 2:08
Using
--onto
is probably the way to go. I've been working on a long-form answer. That three-dot syntax is the correct syntax for specifying the (well, a) merge base; see gitrevisions.– torek
Nov 10 at 2:08
Can you quote where gitrevisions says that? All the references I can find to triple-dots use it to mean roughly "the set of commits going back to the merge-base" not "the merge-base commit itself". I was surprised to find that it worked that way with --onto.
– Andrew
Nov 10 at 2:20
Can you quote where gitrevisions says that? All the references I can find to triple-dots use it to mean roughly "the set of commits going back to the merge-base" not "the merge-base commit itself". I was surprised to find that it worked that way with --onto.
– Andrew
Nov 10 at 2:20
It's a little sneaky, because A...B really is a range syntax for anything that uses ranges. The trick is that Git commands that want a single number pass that to
git rev-parse
(or the C code equivalent), which spits out the two end points as positive references and the merge base(s) as negative references. git diff
and, in modern Git, git rebase
, know how to handle that.– torek
Nov 10 at 2:26
It's a little sneaky, because A...B really is a range syntax for anything that uses ranges. The trick is that Git commands that want a single number pass that to
git rev-parse
(or the C code equivalent), which spits out the two end points as positive references and the merge base(s) as negative references. git diff
and, in modern Git, git rebase
, know how to handle that.– torek
Nov 10 at 2:26
In this particular case, if you examine the old shell-script rebase setup code, it explicitly checks for the three-dot syntax and runs
git merge-base
. The new code is very similar.– torek
Nov 10 at 2:38
In this particular case, if you examine the old shell-script rebase setup code, it explicitly checks for the three-dot syntax and runs
git merge-base
. The new code is very similar.– torek
Nov 10 at 2:38
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53234798%2fcan-i-rebase-on-a-branchs-fork-point-without-explicitly-specifying-the-parent%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
If you want to squash everything back to the merge-base: git reset --soft $(git merge-base HEAD @u); git commit -m 'squash!'
– G. Sylvie Davies
Nov 10 at 5:37
Squash or no squash, I suspect "git merge-base HEAD @u" would be helpful here...
– G. Sylvie Davies
Nov 10 at 5:42