On Mon, 29 Sep 2014 22:34:55 -0700 Sam Roberts <vieuxt...@gmail.com> wrote:
[...] > github, for reasons lost to me, gives you a snapshot of all upstream > branches at time of fork, but not any new branches created upstream, > nor does it ever delete them when upstream deletes, or give any way to > synchronize... not to complain. :-) I have a vague feeling you're falling in a trap rather common to Git newcomers: people, especially those coming from a centralized VCS (such as Subversion), tend to assume that their local repository and their remote repository are the same thing, just the local one might temporarily contain some unpushed changes, the remote one might temporarily contain some unfetched changes, and otherwise they are identical. Having assumed that, they in turn assume that any `git pull` (or `git fetch` or whatever) is supposed to make their local repository be much like the one they've just "synchronized with", that is, to bring in "new" branches (appeared on the remote since the last "sync") and delete those disappeared there. In short, these assumptions are completely wrong. There do indeed exist DVCS systems maintaining the model I've just described; off the top of my head I can name Fossil [1]: it has true "synching", where each branch in a remote repo "means" the same thing as in local, and should you push and/or fetch conflicting changes when synching with the remote, this just creates multiple "heads" in a branch -- meaning diverged histories, and so on. More to this, each repository contains a UUID which is checked when you try to exchange history with a remote repository, and such exchange will fail right away if UUIDs of the local and remote repos are not equal. Git has been created with a very different mindset. The main idea to absorb is that all repositories in Git are independent. Even when you have just a single remote repo configured in your local one (typically it's named "origin"), and that repo is the only one you ever communicate with, this really means nothing to Git, and Git does not treat that remote repo in any special manner. The independence of repositories has important repercussions. The most crucial is that a branch "master" in your local repo is not taken to *mean* the same thing as a branch "master" in any other repo. This is hard to grasp but bear with me. The next idea to get hold on is that Git is fine with fetching commits from any repository at all, and pushing them to any repository at all. In other words, when it's about to exchange commits with another repository, commit hierarchies is the only thing it considers: the repositories do not have any "identity" instilled in them. I mean, you can take a local repository containing one of your weekend toy projects and fetch there any branch from a repository maintaining the Linux kernel source code -- it will work, even though, say, a branch named "master" there contains commits in no way related to those on your local branch "master". Another feature to consider is that Git's branches are truly lightweight and have no identity: a branch is just a pointer to its tip commit, and all commits *reachable* from it do not record the fact they "are on that branch" in any way (which is very different from, say, Mercurial). It means at any time you might do something like: git checkout master git checkout -b foo git branch -d master and have all commits which were reachable from "master" be now reachable from "foo", and "master" is gone. Let's now try to combine the pieces of this puzzle to see the picture. * Commits in any repo form a graph (or a number of graphs). * Branches are mere pointers to single commits of that graph. They're there only for convenience of referring to them and have very little semantics on their own. * Same-named branches in different repositories mean different things. * Git is able to exchange parts of the commit graphs it maintains with any other Git repository. Its wire protocol will try to minimize the amount of objects to transfer, but when doing so it will only consider graphs of commits: it has no notion of "same project", "same branch" etc. * No matter which repositories you exchange commits with, everything in your local repository is "truly yours"; no branch might ever be created, deleted or updated there unless you told Git to do so. So how does Git implements this approach? It does via two paradigms: remote branches and tracking branches. Remote branch is *a bookmark* to the state of a branch in a remote repository last time it was seen there. They're normally created/updated (but not deleted) when you do `git fetch`. For instance, if a remote which is known locally as "origin" is fetched from, and at that time it contains branches "foo" and "dev", Git will normally create/update remote branches "origin/foo" and "origin/dev" for you. The crucial thing is that they are not "yours": they are mere bookmarks to remote state. You can't directly commit to these branches (and this has no sense). Why Git has it implemented this way? Because it also allows you to fetch "foo" and "dev" from Joe's repository, producing "joe/foo" and "joe/dev", and from Mary's, producing "mary/foo" and "mary/dev", and from many others, and have them all properly "namespaced" and ready for local inspection. Still, no matter how many branches named "foo" you did fetch from remote repos, your local branch "foo", if any, is yours and yours only: it's not subject for updating when you fetch "foo" from any remote (unless you tell Git directly to do so -- it's possible). So when you did `git fetch` and "upstream" had new branches at that time, you have remote branches for them -- run `git remote -r` to see them. Note that remote branches do not get deleted when they disappear in their remote repository -- again simply because it's you who decide when to delete what, and you might have legitimate reasons to still have a handle on that old history (you can run `git remote prune` to expunge such remote branches). Now you might legitimately say that "most of the time" you want to have your personal own local branches to closely follow those of "upstream". That's indeed quite a typical case and Git make it easier to support via its "tracking" mechanism: you might configure any local (yours) branch to track a single remote branch. When you clone, and Git creates a single branch "master", it's already made track remote branch "origin/master". Tracking enables a whole slew of convenient shortcuts including hints like "your branch X is N commits ahead of origin/X" etc. In recent versions of Git, all you have to do to create a local branch for an upstream's remote branch and start tracking it is do git checkout origin/foo Git will create a local branch "foo" which will be set to track "origin/foo". Now it's time to read [2] and [3]. 1. http://fossil-scm.org 2. http://git-scm.com/book/en/Git-Branching-Remote-Branches 3. http://longair.net/blog/2009/04/16/git-fetch-and-merge/ -- You received this message because you are subscribed to the Google Groups "Git for human beings" group. To unsubscribe from this group and stop receiving emails from it, send an email to git-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.