Hacker News new | past | comments | ask | show | jobs | submit login
Git 2.0 changes push default to ‘simple’ (nicoschuele.com)
200 points by shawndumas on Nov 2, 2013 | hide | past | favorite | 79 comments



"The new default in Git 2.x is ‘simple’. It means that when doing a git push without specifying a branch, only your current branch will be pushed to the one git pull would normally get your code from."

Hello? Why wasn't it called "current" instead of "simple"?

Why are these unintuitively named options so frequent in git?


Unfortunately, "current" was already taken with a different meaning. There are subtle differences between the different modes.

"current" means: push the current branch into a remote branch with the same name, no matter what.

"upstream" means: push the current branch into the upstream branch, that is the remote branch from which you pull the current branch.

"simple" means: push the current branch into the remote branch from which you are pulling, but only if they have the same name. This is supposed to be "more safe" for beginners than the "upstream" mode.


does 'upstream' mean push to the branch that this is tracking from?

edit: yes. http://stackoverflow.com/questions/10002239/difference-betwe...

"Tracking means that a local branch has its upstream set to a remote branch."

edit two: second question. does 'simple' mean the same as 'upstream' as long as the upstream branch (the one you are tracking from) has the same name as your local branch? I believe so but thought I'd double check.

I have been using git for 4 years. I like it a lot more than svn. I never rebase. I still don't understand some of the things, like why they call it upstream, 'the branch you are pulling from', and tracking, when to me they all seem to mean the same thing.


It's not a common use case in the github era, but you can pull from other repositories than the one you initially cloned.


Still pretty common if you contribute to repos which you don't have commit privileges to. Workflow is:

   1. Clone library's master branch, begin using software.
   2. Discover problem and commit a fix.
   3. Use the Github webui to create a fork of the original repo.
   4. git remote rename origin upstream
   5. git remote add origin https://github.com/me/awesome-library
   6. git push origin master:my-bug-fix
   7. Use Github webui to create a pull request from your 
      my-bug-fix branch into the upstream master.
Having both remotes is necessary, though, when the thing you forked is being actively developed. If there's a review period of a few days, you may need to rebase from the upstream—you want to be doing this from the command line, not bumbling around in the Github webui trying to manage it there.


On the contrary, it's quite common to need to pull down others' branches from GitHub.


(In addition to "current" already being taken...)

If it were called your "intuitive" name ("current", meaning "this is the current default"), then it would, extremely unintuitively, require renaming if it ever stopped being the default. You would be sacrificing future coherence for the sake of some very mild "intuitive sense" during the present.

Complaining about git's standard porcelain is a popular sport, but so often the people who play it don't give proposals that make more sense than the status quo.


> Complaining about git's standard porcelain is a popular sport, but so often the people who play it don't give proposals that make more sense than the status quo.

This is a classic case where the git behavior seems not just unintuitive, but far more complicated than necessary. Why do multiple modes even exist? If I were starting, I'd consider saying that "git push" with no other arguments is the same as what "current" (or even "simple") do now, but you could specify any number of branches or branch wildcards with an argument (including "*"). That is, like ls(1).

I could definitely be missing some important class of use cases, but this feels so much simpler than even having modes, let alone modes that have complex semantics, and still lets the default (very common) case work and supports pushing a manually-specified set of branches, too.


       push.default
           Defines the action git push should take if no refspec is given on
           the command line, no refspec is configured in the remote, and no
           refspec is implied by any of the options given on the command line.
           Possible values are:

           ·    nothing - do not push anything.

           ·    matching - push all matching branches. All branches having the
               same name in both ends are considered to be matching. This is
               the default.

           ·    upstream - push the current branch to its upstream branch.

           ·    tracking - deprecated synonym for upstream.

           ·    current - push the current branch to a branch of the same
               name.
(yes, my version of git on this machine is out of date)

Emphasis on: "if no refspec is given on the command line, no refspec is configured in the remote, and no refspec is implied by any of the options given on the command line"

These modes define what happens if you give no options to git-push. If these modes didn't exist, then you would instead be complaining that a plain old `git push` with no arguments didn't "do what you clearly meant" (where "clearly meant" changes from user to user... particularly since different users have different workflows, some centralized, some not.)

If git-push worked as you suggest, then you would just be complaining that you had to escape your asterisks characters to prevent the shell from expanding them (remember that glob expansion is not something that ls provides...), instead of using a far simpler --all.

You want a legitimate complaint about git-push? Try: "git-add allows either --all or -A, but git-push only allows --all."


I'm pretty sure "current" in this case is supposed to mean "branch you're currently on", which I'd definitely find more intuitive than "default".

On the other hand, apparently that meaning isn't actually as clear as I'd have guessed, so maybe my intuitions about intuitiveness are off-base.


That is what current means right now.

His proposal seems to be "call whatever happens to be the default 'current'". This of course gets you into a non-intuitive "The king is dead, long live the king" sort of situation.


You are assuming one specific reading, when there are at least two valid ones. The way I read the comment was basically to say, "Geez, what git calls 'simple' ('push the current branch into the remote branch from which you are pulling, but only if they have the same name. This is supposed to be "more safe" for beginners than the "upstream" mode.') sounds like it would be better-named as 'current'. Isn't this obvious to everyone, including the git developers? Why are these unintuitively named options so frequent in git?"

That said, I disagree with the sentiment, since there is already a 'current' mode to which the 'current' name seems to apply at least equally well.


I understand that blumentopf meant that "current" should mean "push the current branch", just like "simple" is documented to do. He didn't know that there are actually _two_ behaviors that "push the current branch", and one of them is named "current" whereas the other, new one, is named "simple" ...


A friend of mine said he contacted the people in charge and suggested some ways out of the counter-intuitiveness. He said they just didn't get it - if each feature works as specified, what's the problem?


We were burnt by this before -- someone did a "git push -f" thinking it would force push only their current branch. It ended up pushing everything including their master which was out of date, thus wiping out a whole bunch of newer commits. Luckily it was also easy to restore due to git's distributed nature.


Why would you use -f unless you are doing something really peculiar.

If you are behind git warns you and says you need to pull first. By stating -f you are saying nope, throw that crap away and just take my version.

That's pretty extreme and warrants care. I have a hard time feeling sorry for people who are burned by this.


It's not unreasonable to force push a branch. Anytime you've rebased but already pushed you would need to do so. I've worked on an open source project where every commit was required to pass all the tests, so if you submitted a PR that had any commit fail CI, you would have to alter history and force push.

I also just like pushing to a branch on Github, even if I might rebase later. Circle CI is triggered through Github, so it's nice to have that triggered for me. Our staging servers (production data, your branch's code) are deployed from Github branches as well. Plus looking at diffs on Github is really helpful for me to spot errors—I'm not sure if it's the context switch away from my text editor, Github's diff UI, or what, but it works for me.


> Anytime you've rebased but already pushed you would need to do so.

And that's also unreasonable. Pushed history should be treated as immutable. If you broke the build, push a revert commit, fix the issue, and resubmit. And then fix your tooling to recognize rollbacks.


I tend to treat the history of the master branch as immutable. Working with a remote branch that needs to stay up-to-date with master has, in my experience, required rebasing and force-pushing the branch.


In this case, the person probably thought he or she was pushing "feature-wip" or something similar after cleaning up that branch's history. Not necessarily unreasonable. If master is out of date then

    git push -f
throws away any remote commits (under old behavior).


I actually made this exact mistake yesterday:

* I made a topic branch and push it for review on github

* I immediately found a trivial typo in my code

* I amended my commit to fix the typo

* I did push -f to overwrite the topic branch on

* OOPS. I pushed all my branches. >.<

It doesn't seem peculiar at all, to me.


Even w/o the distributed stuff, git reflog has your back. Once something has been comitted, you have to try pretty hard to really lose it.


That's only if you have ssh access to the server. I don't think there's a way of seeing the reflog of a remote? I.e. if you're using github and the branch only lives there, can you undo an overwrite?


If you have the output from your `git push -f` you can, because it outputs both the old and new ref for each modified branch.


Oh, interesting, I think you're right. That's a bit unfortunate actually!


I guess I am missing something. There is no 'server'. You should have the same reflog locally, if you pulled before someone messed the repository up?


The 'server' in this case is a remote repository which has some commits you don't have. If you force push to this repository, these commits will be lost for you - only the person who pushed these or someone with access to the server can restore them.


Today I suddenly realized reflog is "ref log" not "re flog". My world just got a bit less magical.


Has someone told you about "we blog" yet?


Isn't it "web log"?


At the expense of dissecting the frog: yes, that was the joke. (And whoever downvoted you is a jerk.)


Bitbucket lets an administrator mark branches as not rebasable (or deletable). Very useful feature to avoid exactly what you describe.


That's a really nice feature, I'd like to see github offer that.


They do! But unfortunately there's no UI for this =(

You can contact their support and they will enable this for you. They have great support and usually get back to me within a few hours.


I think every large team using Git has experienced the Git Force Push Master clusterfuck at least once. It's usually right after this that a mandatory gitconfig gets passed around.


Wow, that is pretty dramatic. I'm almost surprised I haven't accidentally done that one.


Strange. I'm not a git expert, but every time I try to push outdated commits to the origin, it says that I'm behind and need to pull first.


If you had the `-f` (force) flag, it overrides any sanity checks and simply accepts whatever history you give it.


If `git config receive.denyNonFastforwards true` is configured on the remote repository then this is prevented. This is the default on modern versions of Git.


I'm really pleased by this change. In my early days of git usage the asymmetry of git pull and git push confused me on several occasions, and I'm glad to see that git is willing to make breaking changes towards a cleaner interface.


As someone in still in early days of git usage (< 1 yr), I've found it quite confusing that pull doesn't pull every matching branch. I'm now sufficiently familiar with git to understand why, but it's sure been frustrating.


Could you please explain it? Because I don't get it. Git's (current) behavior seems exactly backwards to me. It seems to me that what you want is for push to default to the current branch, but pull to default to all branches. i.e. I want push to mean "push what I'm currently working on" and pull to mean "sync my repo's state to be as close as possible to the remote repo's state".


I think what you want in a pull is really just "git fetch", which will update all remote branches. In case you haven't realized this separation, try "git branch -av" to see that your local branch names are distinct from all the "remotes/*" branches. Also, if you have more than one remote repository location, not just origin, then "git remote update" will fetch them all.

Pull is basically the combination of fetch and merge into your local branch. Since there's always a chance for merging to fail and require fixup, it would be dangerous for pull to try all your local branches at once.


Ah, that makes sense, thanks. I guess I'm having a hard time getting out of the SVN mindset. I keep forgetting that there may not be any such thing as the remote repo, I can have different branches tracking different remote repos.


I always use "fetch" instead of "pull" for this reason--I want to update all my remote branches first, and then consider merging with local branches.

It seems that a lot of new git users default to "pull" and I'm not sure why. There must be some really popular tutorial out there that starts with pull.


"git pull" is the closest equivalent to what people are used to from many other source control systems where there is no concept of a local copy of remote branches.


Offtopic: I didn't know about `git branch -v`. Not something I needed enough to go looking for it, but nifty and good to know! Always learning more Git tricks :)


You can use the remote tracking branches if you don't want to make local commits and want them to be automatically updated by fetch. I.e.,

    git checkout origin/interesting-branch
Git will warn you that you're in "detached HEAD" state. When you are making local changes or want to return to where you were, you don't want "git fetch" to have side-effects. Note that you can update local branches without switching to them:

    git push . origin/branch-name:branch-name
will fast-forward you local 'branch-name' to match 'origin/branch-name'. It's basically the equivalent of

    git checkout branch-name
    git merge --ff-only origin/branch-name # or git pull origin branch-name
    git checkout the-last-branch-I-was-on
Each remote has its own namespace for a good reason.


I like this change as well. My early days of git conditioned me to always add the remote and branch when pushing (ie git push origin master) as I did not like the implicit magic behind a bare "git push".


I never bothered to learn the difference. Anyone feel like explaining? git push origin master always seems to work.


`git push origin master` will only push your master branch to origin. `git push` will push every branch that is tracking a branch on origin; so if you had commits on develop for example, they would also go up provided it was tracking the develop branch on origin.

Git v2 changes what `git push` does by default: only the current working branch will get pushed to origin.


>`git push` will push every branch that is tracking a branch on origin;

or rather, every branch that has the same name as a branch on currently tracked remote. I.e. if you're on branch X that tracks remote A, and both you and the remote have branches called Y and Z, even if Y and Z are marked to track B they will be pushed.


It's worth following Junio Hamano's Git Blame blog[1] to keep up with changes in git of this ilk. This particular item is old news in that regard, and a very welcome change at that.

[1] http://git-blame.blogspot.com/


This is amazing. Every person I have introduced to git is constantly confused by the 'rejected' error message returned when a local branch that is not their current working branch is out of sync with the remote, and they just want to push their current working branch.


Love git, but am also glad to see that changes are being considered to make its behavior more intuitive.

Relevant: http://stevelosh.com/blog/2013/04/git-koans/


I still recommend never doing a git push without specifying the remote and branch.


Why?


You don't have to understand the difference between `simple` and `matching`.


I'd still be somewhat weary of doing a targetless force push. For example one may get used to the command and cause damage from a system with an older git.


I think you meant "wary," not "weary." :-)


Maybe dmazin finds the thought alone tiring!


These homophones have tripped me up tremendously in my English-speaking years, even though I generally great pay great attention to such matters.


When in doubt, "git push -n" (aka "--dry-run") will let you test what would happen.


Use aliases then:

    pu   = -c push.default=simple   push -v --progress
    pua  = -c push.default=matching push -v --progress
    poof = -c push.default=simple   push -v --progress --force


I'm always verbose with my push commands:

  git push origin my-feature


Same here. It's easier to just always be explicit and avoid any possible weirdness. I think it's a personality trait thing, though... some people are ok with a certain level of "magic" in the form of implicit behavior, and some people like things to be very explicit.

Put me in the camp of people who generally prefer explicit over implicit. shrug


I've gotten in that habit as well, but then, I'm a neat freak when it comes to git, always using `git add -p` and filtering out any unrelated changes into different commits. I'm often dismayed how sloppy other programmers are with git :)

Commit messages like "Ugh, I really need to commit more often. Did X, Y, Z, and maybe some other stuff." make me cringe. Those people need this change the most!


I'm the same way. People look at me like I'm a pedant because I tell them to do another revision of their patch to fix whitespace errors or typos in their commit messages.

It also annoys me when people create a new commit to fix CR feedback instead of amending their non-public commit.

But I agree the '-p' argument is amazing.


I like having `push.default=upstream` because it's like `simple`, just a little more powerful. With `upstream`, the branch you push to doesn't _need_ to have the same name as the branch you push from. With `simple`, it _does._


This is a great change. I had to learn to add the branch after a couple of bad experiences with bad git pushes. git push <repo> always seemed like it should just push the current branch.


This change of the default makes sense, and I kind of wonder why it wasn't the default in the first place. Kudos to making Git even better and easier for new people to get started.


This is why I really love the Github for Windows client, and recall Torvalds statement that git is not an SCM, but a system to build an SCM.


I'd be interested in seeing the context in which he said that. Googling for the quote didn't get me anything.


That's not a direct quote, but consider that git subcommands are split into 'plumbing' and 'porcelain' and take a look at this chapter of the git book: http://git-scm.com/book/ch9-1.html


He said approximately this when Git was about 4 days old:

http://lwn.net/Articles/131312/

I don't think it really applies to Git as we know it today, though.


Noticed this message the other day - about time! IMO this should have always been the default Git push behaviour.


BTW any ETA on GIT 2.0 or even just a roadmap of important features that are coming?


Hooray! This is the way it always should have been IMHO.


Thanks for the breakdown.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: