>> No, the arrows are in the right direction. Git maintains a Directed Acyclic Graph (DAG) of commits. Each new commit created points to 0..n parents. So let's say you were on the first commit, and then you created a new commit. The newly created commit is the "child" of the first commit, and has a "parent" pointer that is the SHA of the parent. The parent cannot have pointers to the children since they don't know that one will be created!
Now, if you were to do a merge between branch a and b, assuming it's not a fast-forward merge, a new child commit is created that will have 2 "parent" pointers - the SHA of a and b.
A commit will have 0 parents if it the first commit in a repo.
What happens to a commit after 'git reset' abandons it; can the commit be accessed ever again?
>> That commit is "lost". I put that in quotes because git dosen't immediately throw away the commit (rather there is a garbage collection process that will eventually remove that commit). For the same reason if you happen to know the SHA of the commit you just reset, then you can do a "git checkout <new_branch> <sha>" and create a branch off that commit.
One way to get to that "lost" commit is to use "git reflog" - the reflog (that is ref-log) is a lot that updates each time the "HEAD" moves. Since when you do a "git reset" the HEAD moves from the current commit to another one, the reflog will record that.
What's the difference between reset and revert?
>> "reset" is "destructive" in that it can leave dangling commits. A dangling commit is a commit that nothing else points to. Remember the DAG? Well that means that every commit (except) the last commit has a child pointing to it. And who's pointing to the last commit? The branch, and the HEAD. If a commit has NO ONE pointing to it, it is eligible for garbage collection.
Furthermore, since a reset can "delete" commits, one usually never "reset"s public commits - that is commits that have been pushed upstream (to Github or what-have-you). The reason being - if someone else pulled that branch and started working off that commit, and you reset it, it leaves the other person in an unknown state when you do a push.
If you wish to "undo" a public commit, you should revert. If you see the visualization you will notice that "reset" blurs out the last commit, while "revert" creates an "anti-commit" - that is a commit that undoes everything the other commit did.
How can merge and rebase be trusted?
>> I am not sure what you mean by that? Do you mean will Git screw up? Or how do you know that a git pull (which could merge or rebase) pull in commits that have been verified?
What are the best practices for how many branches a project should have and how & when should they be merged?
>> This is a "depends on" answer IMO.
ONE approach is that all "work" should happen on short topic branches off the "integration" branch (which in many cases is master). Commits are made, and things are reviewed, and tested they are merged into master.
This also means that each developer on the team merges one branch per feature into master. Every time you are ready to merge your code into master you first rebase master (which plays your commits on top of master) and then merge into master.
FWIW I am giving you the 30 second answer, but this needs a detailed explanation.
Thanks for answering so many questions. And thank you bodhi and rcxdude as well! I had an idea that best practices might still be an emerging topic. My unclear question about merging was indeed about conflicts, so I'm glad to hear that the merge philosophy is to be "stupid" as rcxdude said. I'll do some more research and check those links.
The best resources I've found for understanding git workflows are by Vincent Driessen[1] and Atlassian[2].
The Atlassian page is much more comprehensive, and in fact derives the 'GitFlow' workflow from Driessen. However, I prefer his original top-to-bottom representation (a left-to-right time axis seems less intuitive to me - they remind me of a football playbook rather than a waterfall).
Aren't the arrows all facing the wrong way?
>> No, the arrows are in the right direction. Git maintains a Directed Acyclic Graph (DAG) of commits. Each new commit created points to 0..n parents. So let's say you were on the first commit, and then you created a new commit. The newly created commit is the "child" of the first commit, and has a "parent" pointer that is the SHA of the parent. The parent cannot have pointers to the children since they don't know that one will be created!
Now, if you were to do a merge between branch a and b, assuming it's not a fast-forward merge, a new child commit is created that will have 2 "parent" pointers - the SHA of a and b.
A commit will have 0 parents if it the first commit in a repo.
What happens to a commit after 'git reset' abandons it; can the commit be accessed ever again?
>> That commit is "lost". I put that in quotes because git dosen't immediately throw away the commit (rather there is a garbage collection process that will eventually remove that commit). For the same reason if you happen to know the SHA of the commit you just reset, then you can do a "git checkout <new_branch> <sha>" and create a branch off that commit.
One way to get to that "lost" commit is to use "git reflog" - the reflog (that is ref-log) is a lot that updates each time the "HEAD" moves. Since when you do a "git reset" the HEAD moves from the current commit to another one, the reflog will record that.
What's the difference between reset and revert?
>> "reset" is "destructive" in that it can leave dangling commits. A dangling commit is a commit that nothing else points to. Remember the DAG? Well that means that every commit (except) the last commit has a child pointing to it. And who's pointing to the last commit? The branch, and the HEAD. If a commit has NO ONE pointing to it, it is eligible for garbage collection.
Furthermore, since a reset can "delete" commits, one usually never "reset"s public commits - that is commits that have been pushed upstream (to Github or what-have-you). The reason being - if someone else pulled that branch and started working off that commit, and you reset it, it leaves the other person in an unknown state when you do a push.
If you wish to "undo" a public commit, you should revert. If you see the visualization you will notice that "reset" blurs out the last commit, while "revert" creates an "anti-commit" - that is a commit that undoes everything the other commit did.
How can merge and rebase be trusted?
>> I am not sure what you mean by that? Do you mean will Git screw up? Or how do you know that a git pull (which could merge or rebase) pull in commits that have been verified?
What are the best practices for how many branches a project should have and how & when should they be merged?
>> This is a "depends on" answer IMO.
ONE approach is that all "work" should happen on short topic branches off the "integration" branch (which in many cases is master). Commits are made, and things are reviewed, and tested they are merged into master.
This also means that each developer on the team merges one branch per feature into master. Every time you are ready to merge your code into master you first rebase master (which plays your commits on top of master) and then merge into master.
FWIW I am giving you the 30 second answer, but this needs a detailed explanation.
A few resources to get you started -
http://git-scm.com/book
ftp.newartisans.com/pub/git.from.bottom.up.pdf
http://eagain.net/articles/git-for-computer-scientists/
Good luck!