Hacker News new | past | comments | ask | show | jobs | submit login

> But I certainly wouldn't call the interface or syntax "inconsistent" or "random".

Uhm, NO WAY.

git checkout works on both branches and files.

I can `checkout remote-name/branch` but I have to `push remote-name branch`. Deleting a local branch is `branch -D` but deleting a remote branch is (I still can't fucking believe it) `push remote :branch`. It's surprisingly hard to unstage a file.

If a rebase fails, it gives you exactly one warning and then forever complains about a dirty index tree. The error messages are borderline incomprehensible for someone who doesn't understand git internals (refs/head say what?). The reset command is far to easy to typo into oblivion.

What else. I haven't spent a lot of time thinking about it, but obtaining an exact list of differences between branches is hard (git `log sha1..sha1` compared with `git log sha1...sha1`).

This is just off the top of my head. Git is great, it's fast, I don't understand people who are still on svn, but the user interface is abysmal.




Git checkout is actually pretty consistent and only really works on branches. When you say it works on files, you actually mean it has a shorthand that works on files:

  git checkout my-file
... is actually using a lot of smart defaults. Namely, you're actually running ...

  git checkout HEAD -- my-file
If you don't specify a branch or identifier, git uses HEAD by default in most cases. (When you run git diff other-branch, you're actually running git diff HEAD other-branch.) And the dash-dash syntax is mostly optional in modern git.

Not inconsistent at all, as far as I can tell. In fact, most features of git that I use on a regular basis are completely consistent and orthogonal. The exceptions are submodules and remote cache branches...


So, in the end it worked on a file. :-)


It worked on a branch, filtered by a file name. I think that's an important distinction. That distinction unifies the definition of `git checkout`.


But

But it doesn't lead to an obvious, consistent command syntax, and that was the point.


It's a consistent syntax with smart defaults you can leave out. You wouldn't say Unix's wc is inconsistent, would you, just because it has default behavior?


Apples to Oranges. wc is a bad example.

Better example is tar vs cp, where you go

  cp files you want to copy destination/
and

  tar destination.tar files you want to archive
And yes, I would say that's inconsistent.


hum... both are wrong.

wc is "word count" it's not a sane default. it's a obviously explicitly behavior. if git checkout were called 'git checkbranch' then you would have a point.

tar does not have sane defaults. it requires explicit flags. your example (besides not working because as i said, it does not have defaults) could very well be `tar -c files you want to copy -f destination.tar` and all would be well. you actually still repeat the very first tar command line example you ever saw to this day, even not agreeing with it :)


> for someone who doesn't understand git internals

Looks like that's the difference. As someone who does understand Git internals, everything seems perfectly logical and consistent to me. Perhaps it's the same with the person you are replying to.

And really, using Git without understanding the internals is a suicide, quite a few people broke their repos and asked me to recover them. The internals are very easy to learn.


No. I should not have to understand how git works internally in order for me to use it.

This is called usability.


  > I can `checkout remote-name/branch` but I have to
  > `push remote-name branch`
So I can use the same command in svn/cvs to switch branches and publish changes?

  > Deleting a local branch is `branch -D` but deleting a
  > remote branch is (I still can't fucking believe it)
  > `push remote :branch`.
It's not that hard to understand. Most git commands only affect the local repo, but git-push and git-remote are used to affect the remote repo. It makes sense not to break that convention because some people feel it should be organized differently.

In any case, writing to remote repos was never part of the core design of git. It was designed to be a distributed group of people that would pull down remote branches, make changes, and then submit pull requests to the code maintainer (or use the various 'email patch' commands). Remember that it was developed with Linux kernel development in mind, where there is a hierarchy of developers responsible for sections of the codebase.


>So I can use the same command in svn/cvs to switch branches and publish changes?

No, it's about the notation for remotes and branches. It's confusing that sometimes when I refer to a branch I call it remote/branch and other times I call it remote branch. It would've been way more consistent if it were always remote/branch.

As a result, git push sometimes gets confusing. Suppose I'm on staging. I then say, git push heroku staging:master (push the staging branch to the master branch on heroku) whereas git push staging:heroku/master is imho easier to understand.


  > git push heroku staging:master
Without the shortcuts, this command would be:

  git push heroku refs/heads/staging:refs/heads/master
"refs/heads/master" is literally the branch location in the $GIT_DIR on the remote. You could do something like:

  git push heroku staging:refs/personal/phillmv/staging
to stick the branch in a non-standard location, or you could do:

  git push ssh://hostname/path/to/repo staging:staging
or:

  git push ~pyre/work/repo staging:pillmv-staging
to push to a location that you don't have specifically defined as a remote.

E.g.

  % cd /tmp
  % mkdir test1 test2
  % pushd test1
  % git init .
  Initialized empty Git repository in /tmp/test1/.git/
  % popd
  % pushd test2
  % git init .
  Initialized empty Git repository in /tmp/test2/.git/
  % git ci --allow-empty -m 'ini commit'
  [master (root-commit) 54ec2a3] ini commit
  % git push ../test1 master:test2-master
  Counting objects: 2, done.
  Writing objects: 100% (2/2), 168 bytes, done.
  Total 2 (delta 0), reused 0 (delta 0)
  Unpacking objects: 100% (2/2), done.
  To ../test1
   * [new branch]      master -> test2-master


Huh. Out of curiosity, why would I ever want to do this?

> git push heroku staging:refs/personal/phillmv/staging

Will it still appear in git branch lists?

I get what you're saying and I appreciate the lesson :).

Is there a reason for the syntax in checkout being remote/branchname then? I care about consistency more than the specific syntax.


  > Will it still appear in git branch lists?
By default, git will only pull branches out of refs/heads/* on the remote repo. You can configure it to do otherwise though. This allows you to store branches on the repo that not everyone will necessarily pull down into their repo. This is obviously less useful with a smaller group of developers and/or a private repository.

  > Is there a reason for the syntax in checkout
  > being remote/branchname then? I care about
  > consistency more than the specific syntax.
In git-checkout, you're resolving the branch name locally. When you sync to a remote, a copy of all branches from refs/heads on the remote repo are pulled down and stored in refs/remotes/$remote_name/branch_name. So, refs/heads/master on the remote repo is refs/remotes/origin/master locally.

The right side of the ':' in the git-push command resolves on the remote repo, but git-checkout resolves in your local repo.

Also look at the git-rev-parse manpage for info on the resolve order/etc for branch name shortcuts. It's the 3rd bullet point under 'Specifying Revisions.'


Yeah, deleting a remote branch by "git push remote :branch" is pretty terrible, one colon between heaven and hell.


If you understand how git push works, it sort of makes sense. But fortunately the devs added a --delete option to git push which is much more clear.


That is indeed clearer, thanks for the tip!


  It's surprisingly hard to unstage a file.
Is there a case when it's not a simple git reset my-file away?


Yes there is. When you have a new repository without any commits, there is no HEAD, so you can't unstage by resetting.

    mkdir try
    cd try
    git init
    touch file
    git add file
    git status
Still, the git status command shows you what you need to do to unstage.


Indeed no. That's exactly what git reset does: "copy entries from <commit> to the index". I got that from the unusably bad man page. It was the first sentence.


My question was a rhetorical one :)


I know. But you were needlessly agreeable. I thought some salt needed to be rubbed into the GP post.


And in every git status message.


I forget my exact edge case. Something to do with wanting to unstage a folder, or unstaging a folder while wanting to preserve work in some of the files.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: