0

I've been doing a lot of reading, but am still not too clear on how to work on a git branch in different places. How to do that?

First of all, the reason that I have to work in a branch is that I do have an "upstream" repo, which I need to rebase into my master from time to time. So to keep my addons clean from upstream, I need to work on a git branch.

UPDATE2:

Ok. It's far more complicated than I thought now. The way I did from the source location was, git checkout -b newfeature then pushed wth git push -u origin newfeature, which was done on top of a "upstream" repo, plus my own master repo, setup following the direction of

The problem is that I want to work on such git branch just as working in a normal git -- i.e., when I do some kind of git push, I'm expecting to do some kind of git pull from another location to get the latest updates.

When I'm doing git pull from my second place, I got Already up-to-date. I.e., the remote branch that I want to work on is not available for me.

PS. Info I've found, from http://longair.net/blog/2009/04/16/git-fetch-and-merge/

If you want to create a local branch based on a remote-tracking branch (i.e. in order to actually work on it) you can do that with git branch –track or git checkout –track -b, which is similar but it also switches your working tree to the newly created local branch. For example, if you see in git branch -r that there’s a remote-tracking branch called origin/refactored that you want, you would use the command:

git checkout --track -b refactored origin/refactored

However, this is what I got from git branch -r on the source location.

$ git branch -r
  origin/HEAD -> origin/master
  origin/master

I.e., there is no remote-tracking branch called origin/something, but I'm clearly working in a branch:

$ git status . 
On branch newfeature
nothing to commit, working directory clean

$ git branch -vv
  master     55e1d6f [origin/master] Remove ...
* newfeature 8c4266a - [+] add ...

Since that -r means for remote-tracking branches, I got same result for git branch -r from my second place as well.

UPDATE 3:

Finally, I can confirm that the problem is at my source end, I.e., the remote tracking branch is not listed. Once again, listing them side-by-side.

First, my current bad source:

$ git branch -r
  origin/HEAD -> origin/master
  origin/master

$ git branch -vv
  master     55e1d6f [origin/master] Remove ...
* newfeature 8c4266a - [+] add ...

Second, what it should be:

$ git branch -r
  origin/HEAD -> origin/master
  origin/master
  origin/newfeature

$ git branch -vv
  master     7b1fc0f [origin/master] Add readme
* newfeature 7b1fc0f [origin/newfeature] Add readme
                     ^^^^^^^^^^^^^^^^^^^

the output of git config --get-all remote.origin.fetch is the same, +refs/heads/master:refs/remotes/origin/master, on both sites:

$ git config --get-all remote.origin.fetch
+refs/heads/master:refs/remotes/origin/master

Final UPDATE:

Thanks to @torek's tireless help, I finally get things straight. Here are the last steps after fixing the .git/config file as directed by @torek:

git checkout master
# then fix the `.git/config` file
git config --edit

$ git branch -vv
* master     55e1d6f [origin/master] Remove ...
  newfeature 8c4266a [origin/newfeature: gone] - [+] add ...
                                         ^^^^

$ git branch -r
  origin/HEAD -> origin/master
  origin/master
  upstream/master
# The "origin/newfeature" is missing

$ git checkout newfeature
Switched to branch 'newfeature'
Your branch is based on 'origin/newfeature', but the upstream is gone.
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  (use "git branch --unset-upstream" to fixup)

$ git branch --set-upstream-to origin/newfeature
error: the requested upstream branch 'origin/newfeature' does not exist
hint: 
hint: If you are planning on basing your work on an upstream
hint: branch that already exists at the remote, you may need to
hint: run "git fetch" to retrieve it.
hint: 
hint: If you are planning to push out a new local branch that
hint: will track its remote counterpart, you may want to use
hint: "git push -u" to set the upstream config as you push.

$ git push -u origin newfeature
Branch newfeature set up to track remote branch newfeature from origin.
Everything up-to-date

$ git branch -r
  origin/HEAD -> origin/master
  origin/master
  origin/newfeature
  upstream/master
# The "origin/newfeature" is now listed

$ git branch -vv
  master     55e1d6f [origin/master] Remove ...
* newfeature 8c4266a [origin/newfeature] - [+] add ...

# Horay!!!

So to recap, to get things correct in the first place, follow the instructions in http://www.gitguys.com/topics/adding-and-removing-remote-branches. I was able to get above correct result with that small demo. I.e., the direction I've been following

Might or might not be the cause. However, now we have a way to fix it, if you get into the similar situation.

Final UPDATE End

So, let me ask the final question -- is it possible to correct my bad source?

According to
checkout tracked remote branch, I think the problem is at my source end, I.e., I don't see my branch listed in the tracked remote branch section. Here is mine:

$ git remote show origin
* remote origin
  Fetch URL: git@github.com:me/myproj.git
  Push  URL: git@github.com:me/myproj.git
  HEAD branch: master
  Remote branch:
    master tracked
  Local branches configured for 'git pull':
    master     merges with remote master
    newfeature merges with remote newfeature
  Local refs configured for 'git push':
    master     pushes to master     (up to date)
    newfeature pushes to newfeature (up to date)

When I do git remote show origin from my second place, I don't see the newfeature listed there.

UPDATE:

I see the newfeature in the git ls-remote from my second place:

$ git ls-remote
From git@github.com:me/myproj.git
55e1d6fd9048336c7f0b178fbbf78231ca28ff06        HEAD
55e1d6fd9048336c7f0b178fbbf78231ca28ff06        refs/heads/master
8c4266a6a98f498c129a2a9e806e00e6c6d196b1        refs/heads/newfeature
8c4266a6a98f498c129a2a9e806e00e6c6d196b1        refs/tags/v1

However, I don't know how to use it from my second place:

$ git checkout --track -b newfeature
Branch newfeature set up to track local branch master.
Switched to a new branch 'newfeature'

and git log is not showing the commits I've published to github.

Maybe I shouldn't have used the -b? Well, I must:

$ git checkout --track newfeature
fatal: Missing branch name; try -b

$ git checkout newfeature
error: pathspec 'newfeature' did not match any file(s) known to git.
Community
  • 1
  • 1
xpt
  • 20,363
  • 37
  • 127
  • 216
  • What do you mean by *places*? I see two obvious possibilities. Perhaps you would like to have, on your laptop or whatever, two different *work trees*. Or, perhaps you have a laptop you sometimes take with you and want to work on branch `newfeature`, but at other times you are at work and not using your laptop and want to continue working on `newfeature`. – torek Jun 13 '16 at 20:59
  • @torek, yep, your second scenario is exactly what my situation is. – xpt Jun 14 '16 at 02:39
  • Please follow this; http://stackoverflow.com/questions/67699/clone-all-remote-branches-with-git/20783081#20783081 – Sam Jun 14 '16 at 04:01
  • 1
    Short answer, post last edit: `git fetch` should create or update the remote-tracking branch in your local repo, as obtained from the branch in the repo on GitHub. It will then show up in `git branch -r` output, and you can then `git checkout newfeature` to *create* the local branch based on the remote-tracking branch, automatically setting the new local branch's upstream; or, if you already have `newfeature`, you can `git checkout newfeature` to get *onto* it, then `git branch --set-upstream-to origin/newfeature` to set the remote-tracking branch as its upstream. – torek Jun 14 '16 at 12:59
  • Hm, there's no `newfeature` remote-tracking branch (`git branch -r`) listed even *after* `git fetch` or `git fetch origin`, even though `git ls-remote` shows that they (github) have it? That would imply a missing or broken `remote.origin.fetch` line. Normally `origin` has this set to `+refs/heads/*:refs/remotes/origin/*`, but if it is wrong or missing, that would explain the problem. That might come about if you used `git remote add` to create the `origin` remote. – torek Jun 14 '16 at 19:42

2 Answers2

1

Here are the last (I hope) pieces of the of puzzle, based on comment below. I requested the output from:

git config --get-all remote.origin.fetch

and that was a single line:

+refs/heads/master:refs/remotes/origin/master

This output is not the normal setting (although it is allowed). The normal setting is:

+refs/heads/*:refs/remotes/origin/*

I think the easiest way to fix it is to run:

git config --edit

which brings up your editor on the Git configuration file for this repository (normally .git/config). In there you will see these three lines:

[remote "origin"]
    url = [whatever the URL is]
    fetch = +refs/heads/master:refs/remotes/origin/master

That third line should read:

    fetch = +refs/heads/*:refs/remotes/origin/*

i.e., both occurrences of master should be replaced with *.

These fetch = lines control the behavior of git fetch. Each line provides a refspec, which is little more than a pair of reference names. The existing (dysfunctional) refspec says that your Git should pick up branch master from origin (GitHub, in this case) and copy it to your remote-tracking branch origin/master

The corrected refspec says that your Git should pick up every branch (*) from origin, and copy each one to a corresponding remote-tracking branch (origin/*).

(The plus sign at the front tells Git that these references should be copied even if the copy operation is not a fast-forward. For remote-tracking branches, this is what you want.)

Once this is fixed, git fetch origin will pick up all of the GitHub branches, and all that remains is to make sure that newfeature, in each of your repositories, has origin/newfeature set as its upstream. In the repository where newfeature has master set as its upstream, run:

git checkout newfeature
git branch --set-upstream-to origin/newfeature

(In any repository that does not yet have a newfeature branch, see below.)


New answer based mostly on new question in the latest update (which probably really should be a new question). Here's the (new) question again, since otherwise I can't keep straight which part is which:

UPDATE:

I see the newfeature in the git ls-remote from my second place:

$ git ls-remote
From git@github.com:me/myproj.git
55e1d6fd9048336c7f0b178fbbf78231ca28ff06        HEAD
55e1d6fd9048336c7f0b178fbbf78231ca28ff06        refs/heads/master
8c4266a6a98f498c129a2a9e806e00e6c6d196b1        refs/heads/newfeature
8c4266a6a98f498c129a2a9e806e00e6c6d196b1        refs/tags/v1

However, I don't know how to use it from my second place:

$ git checkout --track -b newfeature
Branch newfeature set up to track local branch master.
Switched to a new branch 'newfeature'

and git log is not showing the commits I've published to github.

Maybe I shouldn't have used the -b?

Indeed, that is the source of the (new) problem.

We can tell because of the second and third line of output:

Branch newfeature set up to track local branch master.
Switched to a new branch 'newfeature'

I have added three different kinds of emphasis here (italic, bold, and bold-italic) so that I can talk about three different points.

First, Git tells us that this is a new branch. That's fine: that's what we wanted! We want to create, in this repository, a new (and local) branch-name.

But we don't want the new local branch to track another local branch. Git us telling us that it's tracking (local) branch master. We want it to track a remote-tracking branch, probably origin/newfeature.

The command git checkout -b newbranch tells Git to create a new local branch newbranch, with no upstream set. Adding --track modifies the command to tell Git to create newbranch with some upstream set, but the upstream that Git sets is whatever the current branch is.

Using git checkout --track newbranch, without -b, does something quite different; and using git checkout newbranch, with neither --track nor -b, does a third different thing. How we got here is another of those long boring historical-mistake kinds of thing, but in fact, it's usually that third different thing that we want.

Normally we just use git checkout somebranch to switch to some existing branch somebranch. This is mostly straightforward: we might be on master now, when we should be on develop before we start working, so we git checkout develop and switch from master to develop. (But then Git tries to be helpful, and lets us start changing code first, then switch to develop. This mostly actually works, and is helpful, but it leads to beginners' confusion: why is it that Git lets me switch now, but if I make another change, it won't let me switch? See Git - checkout another branch when there are uncommitted changes on the current branch for details.)

In yet another attempt to be helpful, Git added a "do what I mean" (DWIM) option to git checkout so that if you write git checkout newfeature when there is no newfeature yet, it will create newfeature for you, with an upstream set, based on origin/newfeature. (This is oversimplified; see below.) Hence:

$ git checkout newfeature

invokes the DWIM code ("do what I meant to say, not what I actually said"): Git guesses you meant to use the command:

git checkout -b newfeature --track origin/newfeature

which is the fully spelled out request: "create newfeature, based on the commit to which origin/newfeature points right now, and with origin/newfeature set as the upstream for newfeature.

That is, it usually does all this for you. The DWIM code is clever, but not perfect. For this to work, the following conditions must all be true:

  • Your repository has at least one remote. It can have more than one remote, e.g., you can have github and laptop as your two remotes (in this example neither is named origin; origin is merely the standard remote name).
  • Your repository has at least one remote-tracking branch that is tracking a branch named newfeature on that remote. In this example, if remote-tracking branch github/newfeature exists, that satisfies this requirement.
  • Finally—and this is where things get messed up when you have multiple remotes—your repository must have only one such remote-tracking branch. In this case, if both github/newfeature and laptop/newfeature exist, the DWIM code fails!

Make sure one, and only one, remote-tracking branch exists

It's not clear at this time whether origin/newfeature actually exists as a remote-tracking branch, in your current repository.

Remember that every Git repository is quite independent of every other Git repository. This means that whether repository G on GitHub has newfeature does not tell us whether repository L on Laptop has newefature and/or origin/newfeature. Moreover, knowing any of these does not tell us whether repository W, on Work-machine, has any of them.

If we're on Work-machine now, in repository W, and we have set up two remotes github and laptop, we can run git fetch github to contact GitHub and pick up newfeature from repository G (it shows up in the git ls-remote output) and create or update remote-tracking branch github/newfeature.

We can also run git fetch laptop to contact the laptop (assuming the laptop is turned on and connected to the network and reachable). If there is a newfeature in repository L on the laptop, we acquire laptop/newfeature.

If we do this, we defeat the DWIM code in git checkout, because now we have two possible upstreams for git checkout newfeature.

There's nothing wrong with having multiple remotes and two or more possible upstreams

This setup, with repository W having remotes github and laptop, is fine. It just breaks the DWIM code. You can write out what you really meant, and have Git do what you said, rather than relying on Git to guess what you meant to say:

git checkout -b lapfeature --track laptop/newfeature
git checkout -b hubfeature --track github/newfeature

Now, in your work-machine W repository, local branch lapfeature tracks laptop/newfeature, which is the code you have committed on your laptop, and local branch hubfeature tracks github/newfeature, which is the version you are pushing to github. (These do not have to stay in sync, either ... though if you let them get too far apart, you may make yourself unhappy. :-) )

The DWIM code is convenient though

It's nice to be able to just git checkout newfeature. To do that, you have to be careful about how many remotes you have. Having just one remote—the usual origin—makes more of these convenience features work. It's also much clearer when there's just a single Source of Truth, such as "the latest version is always whatever is on GitHub".

The drawback is that you then have to shovel everything back and forth through GitHub. (It's usually up and working, but remember the day GitHub went down?)

Git is confusing, because...

The folks who maintain Git seem to like to shove as much functionality as possible into one command like git checkout, even when that leads to weird and confusing results like this. Note that git checkout can:

  • switch to an existing branch
  • switch to a specific commit ("detached HEAD" mode)
  • create a new branch
  • create a new "orphan" branch (a branch that is not yet created)
  • create a branch's reflog
  • extract files from a commit
  • extract files from the index/staging-area
  • restore a merge conflict (undo the merge resolution from git add)
  • interactively patch file(s) in the work tree

These are all related—but they are also related to actions like moving a branch (git reset) and even making diffs (git checkout --patch has to make a diff). So why are they all in one single git checkout command? Especially when that leads to complicated situations like "sometimes branch switching is allowed, and sometimes it's not, and git checkout otherbranch is nondestructive but git checkout otherbranch path/to/file is highly destructive".


The answer below is for the original question, not the update.

Based on your comment reply, you are looking for methods to work with a remote, with remote-tracking branches, and with branches on a remote. Seems like these should be similar things, if not the same, doesn't it? But they are all rather different, and I think many introductions explain them fairly poorly too. Some of this is no doubt due to history (Git's remotes and remote-tracking branches were new inventions back in 2008-2012; they settled properly in mid to late 2013). If your Git version is at least 1.8.4, and preferably 2.0 or higher, you get to ignore some of the historical problems. (If not, show your Git version—run git --version—and there will be workarounds, or configuration knobs to set, or something.)

First, some definitions:

  • A remote is just a name, like origin, under which your Git can store some items. The most important one is a URL, naming where to push to and fetch from.
  • A branch is ambiguous (see What exactly do we mean by "branch"?).

    A branch name is a name like master or newfeature; it contains the ID of a (single) Git commit. A regular (local) branch name is a specific form of a more generic Git reference. References have prefixes: local branches are all prefixed with refs/heads/, so that master is really refs/heads/master, and so on.

    For the other meaning of "branch", see the linked question. Note that the branch name is updated automatically: making a new commit, while on that branch, causes the branch name to point to the new commit. This is the mechanism by which branches grow.

  • A remote-tracking branch is a reference that, internally at least, starts with refs/remotes/. These references are further qualified with the name of the remote, so that all of the remote-tracking branches for origin start with refs/remotes/origin/. As with regular branch names, Git normally strips off the prefix, showing you just origin/master or origin/newfeature.

Next, with Git—or indeed, any distributed version control system; the same holds for Mercurial, for instance—you must occasionally remember that there are multiple, independent1 repositories, that are not always synchronized. In Git, the synchronization between two repositories happens during git fetch and git push. These two commands are the main points2 at which you (and your Git) can actually see the two different repositories at the same time. I think it's educational to run git ls-remote origin, which reads from the remote but makes no local changes: you should try that now, just to see what it shows you.

A remote-tracking branch has one big purpose: it keeps track, in your own Git repository, of where a branch—a regular, local branch name—pointed, the last time your Git synchronized with theirs. When you run git fetch origin, your Git contacts their Git, downloads everything necessary, then updates all your remote-tracking branches named origin/* to match the branches it just saw on origin.

A git push origin yourcommit:theirname tells your Git to call up their Git, send to them any commits and other objects required, and then ask them (politely) to please set their branch theirname to point to the specific commit you identified by yourcommit. That is, you can git push origin HEAD:name or—assuming your HEAD commit is a1234567git push origin a1234567:name and your Git will send commit a1234567, plus whatever else is necessary, for them to set name to point to a1234567. (Your Git and their Git have a conversation about what objects are necessary, right at the start of the push. Note that your Git, their Git, and in fact every Git in the world, always agree on the ID of every commit! This is one of the trickier parts of writing a distributed VCS.)

Usually you will use one of your own branch names, and in this case, you can omit the :theirname part and your Git will ask their Git to set their branch with the same name. That is, you might git push origin newfeature, and your Git will send them the tip commit of your branch newfeature, plus any additional commits and files needed to complete the branch—and then request, politely, that they set their newbranch.

In general, polite requests are obeyed if this setting is an update that is a fast forward operation, or if it creates the name, or, for that matter, deletes the name. The receiving Git has the chance to say no for any reason, though. (See other SO postings for details.) If the push creates a branch on the remote, this should also create a new remote-tracking branch in your repository, since there is now a new (local, to the remote) branch on the remote, that your Git should track. If the branch already exists on the remote, and the update is accepted, your Git should update your remote-tracking branch.

The push operation can, but does not by default, set your local branch's upstream. See that linked answer for much more about this.

Once the branch exists on the remote, you can use git push from wherever you have updated it (laptop or work or whatever) to push new commits to the remote, and git fetch followed by either git merge or git rebase to copy commits from the remote. If you have set an upstream, and otherwise configured all the historical baggage configuration knobs correctly, you need not use a lot of arguments to these. (Though you will need to use --force-with-lease, or --force / -f, or the + plus-sign prefix syntax in refspecs, to force-push updates that are not fast-forward operations.)

Note too, you can have more than one remote. If you set your laptop as a remote for your work machine, and vice versa, you can transfer commits directly between those machines whenever they are connected to each other on the network (provided you have ssh or similar access—from a Linux box this is generally pretty easy). In other words, you do not have to go through GitHub or some other centralized location (though you can still do that whenever it is convenient).


1In Git's case, they are very independent. In Mercurial, repositories are forced closer together, because branch names are global, in much the same way that Git's tags are global.

2The other ways you can "see both at once", as it were, are git ls-remote and git remote show, but both of these are strictly read-only. The fetch step writes to your repository, and the push step writes to theirs.

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • Indeed, so many questions regarding such simple task. I promise to ask a new question next time, but before that, let me ask the final question, because finally I know what question to ask -- is it possible to correct my bad source? Please see my *update3* for details. Thx again for your arduous help! – xpt Jun 16 '16 at 00:36
  • Re update-3: please run `git config --get-all remote.origin.fetch` and include its output. There may be two things to fix. PS: *arduous* is the wrong word; I think you mean *extensive* or *comprehensive* (or possibly *detailed*) in this case. They are similar, but *arduous* would be used for the process of *producing* help, not for the help-output. :-) – torek Jun 16 '16 at 00:39
  • OK: `+refs/heads/master:refs/remotes/origin/master` is part of the problem. – torek Jun 16 '16 at 03:34
  • Fix it the same way in both places, using your editor and `git config --edit` as in the edited answer. The `master`-only setting means that the repo that's currently working will fall behind as soon as you start pushing from any other repo. – torek Jun 16 '16 at 17:04
  • O.M.G.!! I can't believe the ordeal is FINALLY OVER! Thanks a thousand! Thanks for your tireless help. Yep, *tireless*, that's the better word I found (than *arduous*) as well. :-) There are still some breadcrumb to pick up, but I managed to get though on myself, and will document it in OP. THANKS AGAIN!! – xpt Jun 16 '16 at 17:35
0

Try this

git push --set-upstream origin BRANCHNAME
faety_sal
  • 62
  • 4
  • welcome aboard @faety_sal, but no, the problem is not on the push side. I can clearly see my newfeature branch being pushed successfully on github. – xpt Jun 14 '16 at 02:41
  • On second though, maybe you are right, but the culture here is to explain clearly the reason behind the suggestions. Ahh.., no, the way I pushed was `git push -u origin newfeature`, exactly as yours. – xpt Jun 14 '16 at 02:56