1

Suppose I say A..B. From here

The most common range specification is the double-dot syntax. This basically asks Git to resolve a range of commits that are reachable from one commit but aren’t reachable from another.

Let us say, a graph like this:

* c6ddc03 (HEAD -> topic) t6
* 11751b7 t5
*   a1c4ed2 Merge branch 'small_topic1' into topic
|\  
| * 7bc86ff s2
| * e1b1384 s1
* | 9582f60 t4
* | 815137a t3
|/  
* 648741c t2
* cfce615 t1
| * 7e46c48 (master) m2
| * 84a4dc7 m3
|/  
* 2d15aa1 1

If I say what is 7bc86ff..c6ddc03 , then I get

c6ddc037e9e67647ae69e213c0c5b8a29f5d2745
11751b72f943c4daeb9f28a8dddd93a4b98cb8dc
a1c4ed284557cde1e1474bc5e3f7ef0cd7008ba8
9582f60bf9e5464254a51cb6a085d41005f5795f
815137ac9cbe51768cdaf4c27200f51ecad27fbb

Clearly, this tells us that A..B does not take merge-base (which is 7bc86ff in figure above) into consideration. Because if we had defined A..B as - All commits of B from merge-base of A and B , then this definition wont give above answer.

enter image description here

My question is when we say git rebase --fork-point A B uses fork-point to evaluate A..B, then is not it wrong ? When even merge-base does not effect A..B selection , how can fork-point be used for it ?


For those who want above commit graph in local, run this script

#!/bin/bash

git init .
echo "10" >> 1.txt && git add . && git commit -m "1"

# Add 2 commits to master
echo "3" >> 1.txt && git commit -am "m3"
echo "2" >> 1.txt && git commit -am "m2"


#checkout topic branch
git checkout -b topic HEAD~2
echo "1" >> 1.txt && git commit -am "t1"
echo "2" >> 1.txt && git commit -am "t2"
echo "1" >> 1.txt && git commit -am "t3"
echo "2" >> 1.txt && git commit -am "t4"


#checkout small_topic
git checkout -b small_topic1 HEAD~2
echo "1" >> 1.txt && git commit -am "s1"
echo "2" >> 1.txt && git commit -am "s2"

git checkout topic
git merge small_topic1
echo "1" >> 1.txt && git commit -am "t5"
echo "2" >> 1.txt && git commit -am "t6"

git branch -D small_topic1

#Show graph
git log --oneline --all --decorate --graph
Number945
  • 4,631
  • 8
  • 45
  • 83
  • I find the `--fork-point` option difficult to comprehend, much less explain, but essentially it replaces the `` limiter inside rebase with one chosen by whatever `git merge-base --fork-point` prints. The result of `git merge-base --fork-point` depends on what is in a reflog and is therefore quite obscure. The situation is a little better today than it was earlier, because you can at least run your own `git merge-base --fork-point` command, but now that rebase is all in C code, it's not obvious what arguments to pass. – torek Nov 19 '19 at 18:18

1 Answers1

1

^A says ~no commits currently reachable from A~.

A..B is syntax sugar for, gets rewritten as, ^A B, so it's ~commits currently reachable from B, except nothing currently reachable from A~.

--fork-point effectively says "hunt through the reflogs and exclude all commits that were ever reachable using excluded refnames".

Keep in mind that Git exists to do useful things to a simple structure: a dag of snapshots. --fork-point was the name someone came up with for ~they rebased the upstream branch, I want to rebase just my stuff onto the new tip, not the stuff upstream used to refer to but abandoned, so to do that I hunt through the reflogs and snip-snip-snip at all the old tips~.

My question is when we say git rebase --fork-point A B uses fork-point to evaluate A..B, then is not it wrong ?

Why? Fork-point with those args means ~A..B and additionally truncate at any historical A tip~. Is that not what you want?


Clearly [...] A..B does not take merge-base (which is 7bc86ff in figure above) into consideration.

That is not true, because

Because if we had defined A..B as - All commits of B from merge-base of A and B , then this definition wont give above answer.

That's not the only way to take the merge base into consideration. You're layering far too much characterization and abstraction on top of a very simple underlying concrete reality: a dag of snapshots. The merge base of two tips is by definition reachable from both, so ^A B most definitely does consider the merge base. Twice, even.

jthill
  • 55,082
  • 5
  • 77
  • 137
  • What do you mean by `~` notation like `~no commits` or `A~` ? – Number945 Nov 19 '19 at 19:42
  • It's a paraphrase quote, meaning "the words are close enough to get the gist, it's close enough but might miss irrelevant details". – jthill Nov 19 '19 at 19:45
  • Thnx for the reply. I understand the concept behind fork-point. My question is how does answer change for evaluating `A..B` if we use with or without fork-point ? Is not still `A..B` means , as you said , `commits currently reachable from B, except nothing currently reachable from A.` – Number945 Nov 19 '19 at 19:52
  • "Fork-point with those args means ~`A..B` and additionally truncate at any historical `A` tip~.". – jthill Nov 19 '19 at 19:56
  • If I am getting this right, by `..truncate at any historical A tip` , you mean that `remove` all those commits where A tip has been from the sets of commit obtained by `A..B` . Correct ? – Number945 Nov 20 '19 at 09:53
  • 1
    Yep. That's it. Not just the ones currently in its history, the ones that were in its history before. – jthill Nov 20 '19 at 13:01
  • 1
    Let me run `git rebase --fork-point A B`. If there is no force push on my `A` branch , then --fork-point will behave as normal rebase only when used ? Because any historical tip of A will be in this case already excluded in `A..B`. Am I correct? – Number945 Nov 24 '19 at 10:44
  • 1
    That's correct, when you start tracking local branches there's local equivalents of force pushes, it's the abandoned-history part that fork-point's meant for. – jthill Nov 24 '19 at 13:31