Some git commits are missing after creating git branch from commit id
I am creating a git branch from a commit id, My expectation is it should show commit ids after specified commit id while creating a new branch. But somehow few git commits are missing in the newly created git branch.
I have a main branch which has multiple commits id:
I have created a new branch from main branch using git checkout -b testBranch 3331a4b
command
But when fire git log --oneline
command on this branch, I can see that some commits ids are missing. Please click on below links to check commit id
testBranch commit ids:
Missing commit ids of main branch:
git github git-branch
add a comment |
I am creating a git branch from a commit id, My expectation is it should show commit ids after specified commit id while creating a new branch. But somehow few git commits are missing in the newly created git branch.
I have a main branch which has multiple commits id:
I have created a new branch from main branch using git checkout -b testBranch 3331a4b
command
But when fire git log --oneline
command on this branch, I can see that some commits ids are missing. Please click on below links to check commit id
testBranch commit ids:
Missing commit ids of main branch:
git github git-branch
It smells like the missing commits are on a different branch. Trygit log --oneline --graph --all
to confirm (or contradict) my suspicion.
– joanis
Nov 15 '18 at 16:39
add a comment |
I am creating a git branch from a commit id, My expectation is it should show commit ids after specified commit id while creating a new branch. But somehow few git commits are missing in the newly created git branch.
I have a main branch which has multiple commits id:
I have created a new branch from main branch using git checkout -b testBranch 3331a4b
command
But when fire git log --oneline
command on this branch, I can see that some commits ids are missing. Please click on below links to check commit id
testBranch commit ids:
Missing commit ids of main branch:
git github git-branch
I am creating a git branch from a commit id, My expectation is it should show commit ids after specified commit id while creating a new branch. But somehow few git commits are missing in the newly created git branch.
I have a main branch which has multiple commits id:
I have created a new branch from main branch using git checkout -b testBranch 3331a4b
command
But when fire git log --oneline
command on this branch, I can see that some commits ids are missing. Please click on below links to check commit id
testBranch commit ids:
Missing commit ids of main branch:
git github git-branch
git github git-branch
edited Nov 15 '18 at 14:47
Adil B
4,11892438
4,11892438
asked Nov 15 '18 at 11:49
Pratik WaghmarePratik Waghmare
22
22
It smells like the missing commits are on a different branch. Trygit log --oneline --graph --all
to confirm (or contradict) my suspicion.
– joanis
Nov 15 '18 at 16:39
add a comment |
It smells like the missing commits are on a different branch. Trygit log --oneline --graph --all
to confirm (or contradict) my suspicion.
– joanis
Nov 15 '18 at 16:39
It smells like the missing commits are on a different branch. Try
git log --oneline --graph --all
to confirm (or contradict) my suspicion.– joanis
Nov 15 '18 at 16:39
It smells like the missing commits are on a different branch. Try
git log --oneline --graph --all
to confirm (or contradict) my suspicion.– joanis
Nov 15 '18 at 16:39
add a comment |
1 Answer
1
active
oldest
votes
Side note: screenshot images are generally a bad idea as they make both searching and quoting difficult.
The problem here is that while git log --oneline
displays a linear list of commits, commits are not actually linear. You're expecting that, given some apparently-linear sequence of commits:
010 final
009 earlier
008 earlier-still
007 ...
...
001 first
that starting from 010 and counting backwards will give you 10 commits, while starting from 009 and counting backwards will give you 9 commits.
That would be true if the commits were linear, but they're not.
The actual numbers are, of course, not sequential, and not decimal—they're hash IDs, like 3331a4b
(which is already short for something much longer that simply starts with 3331a4b
). So you already know you can't just use the number as a simple index. Let's replace the sequential numbers (001 through 010) or hash IDs (3331a4b
and such) with single uppercase letters A
B
... H
, and actually draw the commits made in a small repository with ten commits, with the last commit being a merge commit:
A <-B <-C <-D <-----H
/
E <-F <-G
These arrows—the ones coming from E
pointing to C
and from H
pointing to G
do not have an arrow-tip (because it's too hard to draw in text), but those connectors are arrows as well—allow Git to work backwards from the last commit to the first.
In fact, every Git commit contains some set of parent commit hash IDs. We say that the commit points to its parent (with arrows as drawn above). Most commits have exactly one parent, and these commits form a simple, backwards, linear chain, like that going from C
back to A
. Here, everything is well-behaved: when you start at C
there is only one path back to A
.
At least one commit, the very first one ever made, has no parent hash, because it was the first commit ever made and there is no earlier commit for it to point-to. This is where the action stops. Note that the action starts at the end and works backwards.
Some commits, like commit H
in the example above, have two parents. Here, things get tricky, because Git can walk backwards from H
to either G
, or to D
. (You may see where this is going already.) The git log
command uses a specific method to linearize its backwards walk, which we will get to in a moment.
A branch name like master
or develop
actually points to a commit—one particular commit—such as commit H
, so we can draw these things a little more simply, like this, and then add the branch names on the right, at the end:
A--B--C--D------H <-- master
/
E--F--G <-- develop
knowing that the internal links between commits are backwards arrows. (So as to not require ever changing a commit, the arrows themselves are embedded inside the children, pointing back to their parent commits—nothing inside a commit can ever move or change; all of a commit's contents are fixed for all eternity. The children know who their parents are when the children are born, but until the children are born, the parents cannot know who their children will be, so the parents cannot point forward to the children.)
The tricky part here is that the names, like master
or develop
, do move: the name master
got here by way of a git merge
command, perhaps run via git pull
, or perhaps run directly by someone, or via a GitHub "merge pull request" button, or similar. At some point earlier, the drawing looked like this:
A--B--C--D <-- master
E--F--G <-- develop
Commit H
did not yet exist, and starting from master
and working backwards, git log
would enumerate commits D
, then C
, then B
, then A
; starting from develop
and working backwards, git log
would enumerate commits G
, then F
, then E
, then C
, then B
, then A
.
At this point, someone ran git checkout master; git merge develop
, or did something equivalent. This created new commit H
, pointing back to G
and D
, and giving us our now-up-to-date drawing.
Now, suppose that you attach a new branch name to commit F
or E
. If you have been viewing git log
output based on master
, and have been assuming that commit D
comes before commit E
, you might expect to see commits starting from F
or E
, working backwards through and including D
. But as soon as we draw the graph of the commits, and attach a label to F
or E
, you will see that we should not expect commit D
to show up:
A--B--C--D------H <-- master
/
E--F--G <-- develop
.
.....<-- testBranch
The fact that D
is reachable from H
(master
, at the moment) does not imply that D
will be reachable from testBranch
.
In more complex graphs, the set of commits reachable starting at some branch-tip and working backwards can be much more difficult to see: the graphs become tangled and hard to view or visualize. Still, using git log --graph
(with or without --oneline
) can help here.
How git log
linearizes a nonlinear graph
Since git log
must show one commit at a time—one per line with --oneline
, or using more than one output line without it—it needs a queue of pending commits. This queue typically starts out with just the one commit you name:
$ git log <hash>
or:
$ git log <branchname>
or with no arguments, the current (HEAD
) commit. Git will take the commit out of the queue, show it, then put its parent(s) into the queue. If the commit just shown is a merge commit, with two (or more) parents, the queue has now become deeper.
Git then takes whichever commit is at the front of the queue and shows that commit. That commit has some parent(s); if the parent(s) have not yet been shown and are not already in the queue, Git puts them into the queue.
The order in which commits are shown is therefore dependent on where they go in the queue. When the queue is empty—as it often is—and there's only a single parent, that single parent becomes the single entry in the queue, and is immediately shown, removing it from the queue so that the queue is empty again. This is how a simple linear chain shows up as a simple linear chain: we might put commit G
in the queue, then take it back out and show it and put F
in the queue, then take it back out and show it and put E
in the queue, and so on.
If you start git log
with more than one commit:
$ git log master develop
(assuming the names master
and develop
point to two different commits—it's possible to have two branch names pointing to the same commit), the queue will start out with more than one commit in it, so once again, the order of commits in the queue will matter. Git will show the front-of-queue commit first, whether that's the one that master
names, or the one that develop
names. Having shown that commit, Git will insert its parent(s) into the queue if appropriate, and only then have a chance, depending on where the other commit is in the queue now, to show the other commit you named on the command line.
The default for queue insertion order is the commit timestamp, with a newer (later timestamp) commit going to the front of the queue. You can control this order to some extent with --date-order
, --author-date-order
, --topo-order
, and --reverse
. Using --graph
forces --topo-order
. For details, see the git log
documentation.
add a comment |
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',
autoActivateHeartbeat: false,
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
});
}
});
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%2f53318835%2fsome-git-commits-are-missing-after-creating-git-branch-from-commit-id%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Side note: screenshot images are generally a bad idea as they make both searching and quoting difficult.
The problem here is that while git log --oneline
displays a linear list of commits, commits are not actually linear. You're expecting that, given some apparently-linear sequence of commits:
010 final
009 earlier
008 earlier-still
007 ...
...
001 first
that starting from 010 and counting backwards will give you 10 commits, while starting from 009 and counting backwards will give you 9 commits.
That would be true if the commits were linear, but they're not.
The actual numbers are, of course, not sequential, and not decimal—they're hash IDs, like 3331a4b
(which is already short for something much longer that simply starts with 3331a4b
). So you already know you can't just use the number as a simple index. Let's replace the sequential numbers (001 through 010) or hash IDs (3331a4b
and such) with single uppercase letters A
B
... H
, and actually draw the commits made in a small repository with ten commits, with the last commit being a merge commit:
A <-B <-C <-D <-----H
/
E <-F <-G
These arrows—the ones coming from E
pointing to C
and from H
pointing to G
do not have an arrow-tip (because it's too hard to draw in text), but those connectors are arrows as well—allow Git to work backwards from the last commit to the first.
In fact, every Git commit contains some set of parent commit hash IDs. We say that the commit points to its parent (with arrows as drawn above). Most commits have exactly one parent, and these commits form a simple, backwards, linear chain, like that going from C
back to A
. Here, everything is well-behaved: when you start at C
there is only one path back to A
.
At least one commit, the very first one ever made, has no parent hash, because it was the first commit ever made and there is no earlier commit for it to point-to. This is where the action stops. Note that the action starts at the end and works backwards.
Some commits, like commit H
in the example above, have two parents. Here, things get tricky, because Git can walk backwards from H
to either G
, or to D
. (You may see where this is going already.) The git log
command uses a specific method to linearize its backwards walk, which we will get to in a moment.
A branch name like master
or develop
actually points to a commit—one particular commit—such as commit H
, so we can draw these things a little more simply, like this, and then add the branch names on the right, at the end:
A--B--C--D------H <-- master
/
E--F--G <-- develop
knowing that the internal links between commits are backwards arrows. (So as to not require ever changing a commit, the arrows themselves are embedded inside the children, pointing back to their parent commits—nothing inside a commit can ever move or change; all of a commit's contents are fixed for all eternity. The children know who their parents are when the children are born, but until the children are born, the parents cannot know who their children will be, so the parents cannot point forward to the children.)
The tricky part here is that the names, like master
or develop
, do move: the name master
got here by way of a git merge
command, perhaps run via git pull
, or perhaps run directly by someone, or via a GitHub "merge pull request" button, or similar. At some point earlier, the drawing looked like this:
A--B--C--D <-- master
E--F--G <-- develop
Commit H
did not yet exist, and starting from master
and working backwards, git log
would enumerate commits D
, then C
, then B
, then A
; starting from develop
and working backwards, git log
would enumerate commits G
, then F
, then E
, then C
, then B
, then A
.
At this point, someone ran git checkout master; git merge develop
, or did something equivalent. This created new commit H
, pointing back to G
and D
, and giving us our now-up-to-date drawing.
Now, suppose that you attach a new branch name to commit F
or E
. If you have been viewing git log
output based on master
, and have been assuming that commit D
comes before commit E
, you might expect to see commits starting from F
or E
, working backwards through and including D
. But as soon as we draw the graph of the commits, and attach a label to F
or E
, you will see that we should not expect commit D
to show up:
A--B--C--D------H <-- master
/
E--F--G <-- develop
.
.....<-- testBranch
The fact that D
is reachable from H
(master
, at the moment) does not imply that D
will be reachable from testBranch
.
In more complex graphs, the set of commits reachable starting at some branch-tip and working backwards can be much more difficult to see: the graphs become tangled and hard to view or visualize. Still, using git log --graph
(with or without --oneline
) can help here.
How git log
linearizes a nonlinear graph
Since git log
must show one commit at a time—one per line with --oneline
, or using more than one output line without it—it needs a queue of pending commits. This queue typically starts out with just the one commit you name:
$ git log <hash>
or:
$ git log <branchname>
or with no arguments, the current (HEAD
) commit. Git will take the commit out of the queue, show it, then put its parent(s) into the queue. If the commit just shown is a merge commit, with two (or more) parents, the queue has now become deeper.
Git then takes whichever commit is at the front of the queue and shows that commit. That commit has some parent(s); if the parent(s) have not yet been shown and are not already in the queue, Git puts them into the queue.
The order in which commits are shown is therefore dependent on where they go in the queue. When the queue is empty—as it often is—and there's only a single parent, that single parent becomes the single entry in the queue, and is immediately shown, removing it from the queue so that the queue is empty again. This is how a simple linear chain shows up as a simple linear chain: we might put commit G
in the queue, then take it back out and show it and put F
in the queue, then take it back out and show it and put E
in the queue, and so on.
If you start git log
with more than one commit:
$ git log master develop
(assuming the names master
and develop
point to two different commits—it's possible to have two branch names pointing to the same commit), the queue will start out with more than one commit in it, so once again, the order of commits in the queue will matter. Git will show the front-of-queue commit first, whether that's the one that master
names, or the one that develop
names. Having shown that commit, Git will insert its parent(s) into the queue if appropriate, and only then have a chance, depending on where the other commit is in the queue now, to show the other commit you named on the command line.
The default for queue insertion order is the commit timestamp, with a newer (later timestamp) commit going to the front of the queue. You can control this order to some extent with --date-order
, --author-date-order
, --topo-order
, and --reverse
. Using --graph
forces --topo-order
. For details, see the git log
documentation.
add a comment |
Side note: screenshot images are generally a bad idea as they make both searching and quoting difficult.
The problem here is that while git log --oneline
displays a linear list of commits, commits are not actually linear. You're expecting that, given some apparently-linear sequence of commits:
010 final
009 earlier
008 earlier-still
007 ...
...
001 first
that starting from 010 and counting backwards will give you 10 commits, while starting from 009 and counting backwards will give you 9 commits.
That would be true if the commits were linear, but they're not.
The actual numbers are, of course, not sequential, and not decimal—they're hash IDs, like 3331a4b
(which is already short for something much longer that simply starts with 3331a4b
). So you already know you can't just use the number as a simple index. Let's replace the sequential numbers (001 through 010) or hash IDs (3331a4b
and such) with single uppercase letters A
B
... H
, and actually draw the commits made in a small repository with ten commits, with the last commit being a merge commit:
A <-B <-C <-D <-----H
/
E <-F <-G
These arrows—the ones coming from E
pointing to C
and from H
pointing to G
do not have an arrow-tip (because it's too hard to draw in text), but those connectors are arrows as well—allow Git to work backwards from the last commit to the first.
In fact, every Git commit contains some set of parent commit hash IDs. We say that the commit points to its parent (with arrows as drawn above). Most commits have exactly one parent, and these commits form a simple, backwards, linear chain, like that going from C
back to A
. Here, everything is well-behaved: when you start at C
there is only one path back to A
.
At least one commit, the very first one ever made, has no parent hash, because it was the first commit ever made and there is no earlier commit for it to point-to. This is where the action stops. Note that the action starts at the end and works backwards.
Some commits, like commit H
in the example above, have two parents. Here, things get tricky, because Git can walk backwards from H
to either G
, or to D
. (You may see where this is going already.) The git log
command uses a specific method to linearize its backwards walk, which we will get to in a moment.
A branch name like master
or develop
actually points to a commit—one particular commit—such as commit H
, so we can draw these things a little more simply, like this, and then add the branch names on the right, at the end:
A--B--C--D------H <-- master
/
E--F--G <-- develop
knowing that the internal links between commits are backwards arrows. (So as to not require ever changing a commit, the arrows themselves are embedded inside the children, pointing back to their parent commits—nothing inside a commit can ever move or change; all of a commit's contents are fixed for all eternity. The children know who their parents are when the children are born, but until the children are born, the parents cannot know who their children will be, so the parents cannot point forward to the children.)
The tricky part here is that the names, like master
or develop
, do move: the name master
got here by way of a git merge
command, perhaps run via git pull
, or perhaps run directly by someone, or via a GitHub "merge pull request" button, or similar. At some point earlier, the drawing looked like this:
A--B--C--D <-- master
E--F--G <-- develop
Commit H
did not yet exist, and starting from master
and working backwards, git log
would enumerate commits D
, then C
, then B
, then A
; starting from develop
and working backwards, git log
would enumerate commits G
, then F
, then E
, then C
, then B
, then A
.
At this point, someone ran git checkout master; git merge develop
, or did something equivalent. This created new commit H
, pointing back to G
and D
, and giving us our now-up-to-date drawing.
Now, suppose that you attach a new branch name to commit F
or E
. If you have been viewing git log
output based on master
, and have been assuming that commit D
comes before commit E
, you might expect to see commits starting from F
or E
, working backwards through and including D
. But as soon as we draw the graph of the commits, and attach a label to F
or E
, you will see that we should not expect commit D
to show up:
A--B--C--D------H <-- master
/
E--F--G <-- develop
.
.....<-- testBranch
The fact that D
is reachable from H
(master
, at the moment) does not imply that D
will be reachable from testBranch
.
In more complex graphs, the set of commits reachable starting at some branch-tip and working backwards can be much more difficult to see: the graphs become tangled and hard to view or visualize. Still, using git log --graph
(with or without --oneline
) can help here.
How git log
linearizes a nonlinear graph
Since git log
must show one commit at a time—one per line with --oneline
, or using more than one output line without it—it needs a queue of pending commits. This queue typically starts out with just the one commit you name:
$ git log <hash>
or:
$ git log <branchname>
or with no arguments, the current (HEAD
) commit. Git will take the commit out of the queue, show it, then put its parent(s) into the queue. If the commit just shown is a merge commit, with two (or more) parents, the queue has now become deeper.
Git then takes whichever commit is at the front of the queue and shows that commit. That commit has some parent(s); if the parent(s) have not yet been shown and are not already in the queue, Git puts them into the queue.
The order in which commits are shown is therefore dependent on where they go in the queue. When the queue is empty—as it often is—and there's only a single parent, that single parent becomes the single entry in the queue, and is immediately shown, removing it from the queue so that the queue is empty again. This is how a simple linear chain shows up as a simple linear chain: we might put commit G
in the queue, then take it back out and show it and put F
in the queue, then take it back out and show it and put E
in the queue, and so on.
If you start git log
with more than one commit:
$ git log master develop
(assuming the names master
and develop
point to two different commits—it's possible to have two branch names pointing to the same commit), the queue will start out with more than one commit in it, so once again, the order of commits in the queue will matter. Git will show the front-of-queue commit first, whether that's the one that master
names, or the one that develop
names. Having shown that commit, Git will insert its parent(s) into the queue if appropriate, and only then have a chance, depending on where the other commit is in the queue now, to show the other commit you named on the command line.
The default for queue insertion order is the commit timestamp, with a newer (later timestamp) commit going to the front of the queue. You can control this order to some extent with --date-order
, --author-date-order
, --topo-order
, and --reverse
. Using --graph
forces --topo-order
. For details, see the git log
documentation.
add a comment |
Side note: screenshot images are generally a bad idea as they make both searching and quoting difficult.
The problem here is that while git log --oneline
displays a linear list of commits, commits are not actually linear. You're expecting that, given some apparently-linear sequence of commits:
010 final
009 earlier
008 earlier-still
007 ...
...
001 first
that starting from 010 and counting backwards will give you 10 commits, while starting from 009 and counting backwards will give you 9 commits.
That would be true if the commits were linear, but they're not.
The actual numbers are, of course, not sequential, and not decimal—they're hash IDs, like 3331a4b
(which is already short for something much longer that simply starts with 3331a4b
). So you already know you can't just use the number as a simple index. Let's replace the sequential numbers (001 through 010) or hash IDs (3331a4b
and such) with single uppercase letters A
B
... H
, and actually draw the commits made in a small repository with ten commits, with the last commit being a merge commit:
A <-B <-C <-D <-----H
/
E <-F <-G
These arrows—the ones coming from E
pointing to C
and from H
pointing to G
do not have an arrow-tip (because it's too hard to draw in text), but those connectors are arrows as well—allow Git to work backwards from the last commit to the first.
In fact, every Git commit contains some set of parent commit hash IDs. We say that the commit points to its parent (with arrows as drawn above). Most commits have exactly one parent, and these commits form a simple, backwards, linear chain, like that going from C
back to A
. Here, everything is well-behaved: when you start at C
there is only one path back to A
.
At least one commit, the very first one ever made, has no parent hash, because it was the first commit ever made and there is no earlier commit for it to point-to. This is where the action stops. Note that the action starts at the end and works backwards.
Some commits, like commit H
in the example above, have two parents. Here, things get tricky, because Git can walk backwards from H
to either G
, or to D
. (You may see where this is going already.) The git log
command uses a specific method to linearize its backwards walk, which we will get to in a moment.
A branch name like master
or develop
actually points to a commit—one particular commit—such as commit H
, so we can draw these things a little more simply, like this, and then add the branch names on the right, at the end:
A--B--C--D------H <-- master
/
E--F--G <-- develop
knowing that the internal links between commits are backwards arrows. (So as to not require ever changing a commit, the arrows themselves are embedded inside the children, pointing back to their parent commits—nothing inside a commit can ever move or change; all of a commit's contents are fixed for all eternity. The children know who their parents are when the children are born, but until the children are born, the parents cannot know who their children will be, so the parents cannot point forward to the children.)
The tricky part here is that the names, like master
or develop
, do move: the name master
got here by way of a git merge
command, perhaps run via git pull
, or perhaps run directly by someone, or via a GitHub "merge pull request" button, or similar. At some point earlier, the drawing looked like this:
A--B--C--D <-- master
E--F--G <-- develop
Commit H
did not yet exist, and starting from master
and working backwards, git log
would enumerate commits D
, then C
, then B
, then A
; starting from develop
and working backwards, git log
would enumerate commits G
, then F
, then E
, then C
, then B
, then A
.
At this point, someone ran git checkout master; git merge develop
, or did something equivalent. This created new commit H
, pointing back to G
and D
, and giving us our now-up-to-date drawing.
Now, suppose that you attach a new branch name to commit F
or E
. If you have been viewing git log
output based on master
, and have been assuming that commit D
comes before commit E
, you might expect to see commits starting from F
or E
, working backwards through and including D
. But as soon as we draw the graph of the commits, and attach a label to F
or E
, you will see that we should not expect commit D
to show up:
A--B--C--D------H <-- master
/
E--F--G <-- develop
.
.....<-- testBranch
The fact that D
is reachable from H
(master
, at the moment) does not imply that D
will be reachable from testBranch
.
In more complex graphs, the set of commits reachable starting at some branch-tip and working backwards can be much more difficult to see: the graphs become tangled and hard to view or visualize. Still, using git log --graph
(with or without --oneline
) can help here.
How git log
linearizes a nonlinear graph
Since git log
must show one commit at a time—one per line with --oneline
, or using more than one output line without it—it needs a queue of pending commits. This queue typically starts out with just the one commit you name:
$ git log <hash>
or:
$ git log <branchname>
or with no arguments, the current (HEAD
) commit. Git will take the commit out of the queue, show it, then put its parent(s) into the queue. If the commit just shown is a merge commit, with two (or more) parents, the queue has now become deeper.
Git then takes whichever commit is at the front of the queue and shows that commit. That commit has some parent(s); if the parent(s) have not yet been shown and are not already in the queue, Git puts them into the queue.
The order in which commits are shown is therefore dependent on where they go in the queue. When the queue is empty—as it often is—and there's only a single parent, that single parent becomes the single entry in the queue, and is immediately shown, removing it from the queue so that the queue is empty again. This is how a simple linear chain shows up as a simple linear chain: we might put commit G
in the queue, then take it back out and show it and put F
in the queue, then take it back out and show it and put E
in the queue, and so on.
If you start git log
with more than one commit:
$ git log master develop
(assuming the names master
and develop
point to two different commits—it's possible to have two branch names pointing to the same commit), the queue will start out with more than one commit in it, so once again, the order of commits in the queue will matter. Git will show the front-of-queue commit first, whether that's the one that master
names, or the one that develop
names. Having shown that commit, Git will insert its parent(s) into the queue if appropriate, and only then have a chance, depending on where the other commit is in the queue now, to show the other commit you named on the command line.
The default for queue insertion order is the commit timestamp, with a newer (later timestamp) commit going to the front of the queue. You can control this order to some extent with --date-order
, --author-date-order
, --topo-order
, and --reverse
. Using --graph
forces --topo-order
. For details, see the git log
documentation.
Side note: screenshot images are generally a bad idea as they make both searching and quoting difficult.
The problem here is that while git log --oneline
displays a linear list of commits, commits are not actually linear. You're expecting that, given some apparently-linear sequence of commits:
010 final
009 earlier
008 earlier-still
007 ...
...
001 first
that starting from 010 and counting backwards will give you 10 commits, while starting from 009 and counting backwards will give you 9 commits.
That would be true if the commits were linear, but they're not.
The actual numbers are, of course, not sequential, and not decimal—they're hash IDs, like 3331a4b
(which is already short for something much longer that simply starts with 3331a4b
). So you already know you can't just use the number as a simple index. Let's replace the sequential numbers (001 through 010) or hash IDs (3331a4b
and such) with single uppercase letters A
B
... H
, and actually draw the commits made in a small repository with ten commits, with the last commit being a merge commit:
A <-B <-C <-D <-----H
/
E <-F <-G
These arrows—the ones coming from E
pointing to C
and from H
pointing to G
do not have an arrow-tip (because it's too hard to draw in text), but those connectors are arrows as well—allow Git to work backwards from the last commit to the first.
In fact, every Git commit contains some set of parent commit hash IDs. We say that the commit points to its parent (with arrows as drawn above). Most commits have exactly one parent, and these commits form a simple, backwards, linear chain, like that going from C
back to A
. Here, everything is well-behaved: when you start at C
there is only one path back to A
.
At least one commit, the very first one ever made, has no parent hash, because it was the first commit ever made and there is no earlier commit for it to point-to. This is where the action stops. Note that the action starts at the end and works backwards.
Some commits, like commit H
in the example above, have two parents. Here, things get tricky, because Git can walk backwards from H
to either G
, or to D
. (You may see where this is going already.) The git log
command uses a specific method to linearize its backwards walk, which we will get to in a moment.
A branch name like master
or develop
actually points to a commit—one particular commit—such as commit H
, so we can draw these things a little more simply, like this, and then add the branch names on the right, at the end:
A--B--C--D------H <-- master
/
E--F--G <-- develop
knowing that the internal links between commits are backwards arrows. (So as to not require ever changing a commit, the arrows themselves are embedded inside the children, pointing back to their parent commits—nothing inside a commit can ever move or change; all of a commit's contents are fixed for all eternity. The children know who their parents are when the children are born, but until the children are born, the parents cannot know who their children will be, so the parents cannot point forward to the children.)
The tricky part here is that the names, like master
or develop
, do move: the name master
got here by way of a git merge
command, perhaps run via git pull
, or perhaps run directly by someone, or via a GitHub "merge pull request" button, or similar. At some point earlier, the drawing looked like this:
A--B--C--D <-- master
E--F--G <-- develop
Commit H
did not yet exist, and starting from master
and working backwards, git log
would enumerate commits D
, then C
, then B
, then A
; starting from develop
and working backwards, git log
would enumerate commits G
, then F
, then E
, then C
, then B
, then A
.
At this point, someone ran git checkout master; git merge develop
, or did something equivalent. This created new commit H
, pointing back to G
and D
, and giving us our now-up-to-date drawing.
Now, suppose that you attach a new branch name to commit F
or E
. If you have been viewing git log
output based on master
, and have been assuming that commit D
comes before commit E
, you might expect to see commits starting from F
or E
, working backwards through and including D
. But as soon as we draw the graph of the commits, and attach a label to F
or E
, you will see that we should not expect commit D
to show up:
A--B--C--D------H <-- master
/
E--F--G <-- develop
.
.....<-- testBranch
The fact that D
is reachable from H
(master
, at the moment) does not imply that D
will be reachable from testBranch
.
In more complex graphs, the set of commits reachable starting at some branch-tip and working backwards can be much more difficult to see: the graphs become tangled and hard to view or visualize. Still, using git log --graph
(with or without --oneline
) can help here.
How git log
linearizes a nonlinear graph
Since git log
must show one commit at a time—one per line with --oneline
, or using more than one output line without it—it needs a queue of pending commits. This queue typically starts out with just the one commit you name:
$ git log <hash>
or:
$ git log <branchname>
or with no arguments, the current (HEAD
) commit. Git will take the commit out of the queue, show it, then put its parent(s) into the queue. If the commit just shown is a merge commit, with two (or more) parents, the queue has now become deeper.
Git then takes whichever commit is at the front of the queue and shows that commit. That commit has some parent(s); if the parent(s) have not yet been shown and are not already in the queue, Git puts them into the queue.
The order in which commits are shown is therefore dependent on where they go in the queue. When the queue is empty—as it often is—and there's only a single parent, that single parent becomes the single entry in the queue, and is immediately shown, removing it from the queue so that the queue is empty again. This is how a simple linear chain shows up as a simple linear chain: we might put commit G
in the queue, then take it back out and show it and put F
in the queue, then take it back out and show it and put E
in the queue, and so on.
If you start git log
with more than one commit:
$ git log master develop
(assuming the names master
and develop
point to two different commits—it's possible to have two branch names pointing to the same commit), the queue will start out with more than one commit in it, so once again, the order of commits in the queue will matter. Git will show the front-of-queue commit first, whether that's the one that master
names, or the one that develop
names. Having shown that commit, Git will insert its parent(s) into the queue if appropriate, and only then have a chance, depending on where the other commit is in the queue now, to show the other commit you named on the command line.
The default for queue insertion order is the commit timestamp, with a newer (later timestamp) commit going to the front of the queue. You can control this order to some extent with --date-order
, --author-date-order
, --topo-order
, and --reverse
. Using --graph
forces --topo-order
. For details, see the git log
documentation.
answered Nov 15 '18 at 16:51
torektorek
187k18236318
187k18236318
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
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%2f53318835%2fsome-git-commits-are-missing-after-creating-git-branch-from-commit-id%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
It smells like the missing commits are on a different branch. Try
git log --oneline --graph --all
to confirm (or contradict) my suspicion.– joanis
Nov 15 '18 at 16:39