Can I rebase on a branch's fork point without explicitly specifying the parent?









up vote
3
down vote

favorite
1












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.










share|improve this question























  • 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














up vote
3
down vote

favorite
1












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.










share|improve this question























  • 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












up vote
3
down vote

favorite
1









up vote
3
down vote

favorite
1






1





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.










share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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
















  • 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












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.






share|improve this answer






















  • "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

















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)






share|improve this answer






















  • 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 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










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















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

























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.






share|improve this answer






















  • "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














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.






share|improve this answer






















  • "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












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.






share|improve this answer














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.







share|improve this answer














share|improve this answer



share|improve this answer








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
















  • "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












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)






share|improve this answer






















  • 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 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














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)






share|improve this answer






















  • 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 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












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)






share|improve this answer














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)







share|improve this answer














share|improve this answer



share|improve this answer








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 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
















  • 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 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















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

















 

draft saved


draft discarded















































 


draft saved


draft discarded














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





















































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







Popular posts from this blog

Use pre created SQLite database for Android project in kotlin

Darth Vader #20

Ondo