Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It's complaining because I've modified README locally, which was also modified remotely. Every other reasonable version control system I've ever used will happily merge the upstream changes with my not-yet-committed local changes. But Git refuses. This is annoying.

Commit-before-merge is not a limitation, it is a feature of DVCS's. Merge-before-commit is broken by design, as you are modifying your unsaved work, and there is no way to get back to your pre-merge state (say you decide the merge conflicts are too much to deal with at the moment).

So you are correct, git will not let you merge if youmhave uncomitted work that would be affected by the merge, but it's really just trying to keep you from losing work, not trying to annoy you.

That said, this is one of the use cases for 'git stash' which will set aside your uncommitted work. You can then do the 'git pull' and then unstash (stash pop) your work. The advantage of this is that you can always undo the merge if you decide it's not what you wanted afterall. With merge-before-commit, you'd have no such option unless you manually set aside your work.

But what if I have multiple sets of locally-hacked-up changes? Like suppose I was working on one change when I realized that there's something else I should really fix first. "git stash" quickly becomes limiting, since you can't name the individual changes, so you get this list of changes that you don't know what they are or what branch/commit they were based on. In other VCS's like Perforce, you can have multiple sets of independent changes in your working tree. Not possible with Git AFAIK.

This is what branches are for. Do not be afraid to commit work in progress... you can always polish up that work before you share those changes. For example:

  git checkout -b feature origin/master
  edit, ut oh, interuption,
  git commit -a -m WIP
  git checkout -b bugfix-1234 origin/master
  fix bug, git commit -m "fixed bug"
  git push origin HEAD:master
  git checkout feature
  git reset HEAD^ # removes the WIP commit, but leaves its changes in your working copy.
I hope that gives you a better idea of how you can use branches. You might also want to spend some time reading up on rebase -i. Basically, start thinking of your localc branches as independent patch queues that you can freely edit, reorder, etc until they are ready to be shared. At which point you can push them out to the world.

HTH.



> it's really just trying to keep you from losing work, not trying to annoy you.

The same thing could be achieved by having Git automatically create an "undo" commit before performing the merge. Then you could revert to pre-merge state with "git pull --undo", just like you can abort a rebase with "git rebase --abort."

Optimize for the common case. Of probably hundreds of merge-before-commit operations I have performed with other VCS's, I can't think of a single time I have wanted to undo this operation (after all, if upstream has changed you're going to have to merge sooner or later -- it might as well be now). On the other hand, I am annoyed by commit-before-merge every single time I perform a pull.

> This is what branches are for. Do not be afraid to commit work in progress...

I'll have to try the "git reset" approach, I hadn't thought of that before. But even that has issues IMO:

1. "git reset" is a data-losing operation if you call it with certain parameters. For example, "git reset --hard HEAD^" would throw away the WIP! I'm wary of making such a command something I type all the time, because there's always the risk that I'll call it wrong.

2. I have to do this dance of "commit, checkout, pull, checkout, rebase" just to pull upstream changes. And when I want to actually push the change upstream, I have to "merge --squash" and then delete the old branch (otherwise lots of branches will build up and I won't know which ones have been committed and which haven't). It's a lot of annoying overhead. I'd rather just work on master where I can just "pull", and branch only if I really want to work on two big changes in parallel.

> HTH.

I really do appreciate that you were genuinely trying to be helpful (as opposed to other replies). But my frustration remains that Git doesn't let me work the way I want to, and makes me perform contortions to fit its way of working.


The same thing could be achieved by having Git automatically create an "undo" commit before performing the merge. Then you could revert to pre-merge state with "git pull --undo",

I'm a regular on the git mailing list, and I've never heard of anyone desiring this workflow. It is unusual to want to merge into your work-in-progress. I'd go so far as to say "you're not using git as it was intended". Perhaps this helps a little:

http://www.mail-archive.com/dri-devel@lists.sourceforge.net/...

That said, it would be straight-forward to script/alias what you describe:

  git config --global alias.cleanpull '!git stash && git pull && git stash pop'
But really, that's not how git is intended to be used.

just like you can abort a rebase with "git rebase --abort."

Actually, rebase is much stricter than merge -- it won't let you start unless your working tree is completely clean. At least merge only cares about whether the files it needs to touch are clean.

Optimize for the common case.

That's not the common case, you've just been brain-damaged by non-DVCS's into thinking it is. :-)

"git reset" is a data-losing operation if you call it with certain parameters. For example, "git reset --hard HEAD^" would throw away the WIP

  git config --global alias.popcommit "reset HEAD^"
2. I have to do this dance of "commit, checkout, pull, checkout, rebase" just to pull upstream changes. And when I want to actually push the change upstream, I have to "merge --squash" and then delete the old branch (otherwise lots of branches will build up and I won't know which ones have been committed and which haven't). It's a lot of annoying overhead. I'd rather just work on master where I can just "pull", and branch only if I really want to work on two big changes in parallel.

"merge --squash"? It really sounds like you're trying to use git as if it's subversion or cvs, and it just isn't.

You can check for merged branches with "git branch --merged origin/master"

If you just want to examine upstream changes w/o integrating them into your current work, you can use "git fetch" and then "git log master..origin/master".

And if you find you often need to be working on multiple branches at the same time, you can always make an additional clone and/or use the new-workdir script in the git.git contrib directory.

Perhaps git just doesn't fit your notion of how a VCS should work, and that's fine. But your annoyances with git seem to stem from trying to use it not as it was intended. :-(

<tangent>Git is a powerful VCS with a rather-awful CLI, but built on top of simple and elegant concepts. Trying to derive a mental-model of how git works from its CLI is fraught-with-peril and will lead you astray. It is worth learning how git works conceptually, and then mapping CLI commands to those concepts. For small projects, it doesn't really matter, but for large projects, git is extremely flexible and you can do things with it that I cannot imagine doing with any other VCS.</tangent>

Best of luck.


FWIW, Mercurial allows you to do merges against the working copy -- you just run hg update.


It does not let you merge if you have uncommitted work, just like git:

  $ hg pull
  pulling from ...
  requesting all changes
  adding changesets
  adding manifests
  adding file changes
  added 1 changesets with 1 changes to 1 files
  (run 'hg update' to get a working copy)
  $ hg update
  abort: crosses branches (use 'hg merge' to merge or use  'hg update -C' to discard changes)
  $ hg merge
  abort: outstanding uncommitted changes (use 'hg status' to list changes)


Sorry, you're right that it does refuse to merge with the working copy if the changeset you're updating to is not a direct descendent of the changeset you're currently at (this is what the error about "crosses branches" refers to).

However, if it is a direct descendent, it will try to merge for you:

  $ echo b >> a
  $ hg status
  M a
  $ hg pull ../a
  pulling from ../a
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 1 changesets with 1 changes to 1 files
  (run 'hg update' to get a working copy)
  $ hg update
  merging a
  warning: conflicts during merge.
  merging a failed!
  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
  use 'hg resolve' to retry unresolved file merges
  $ hg resolve -l
  U a
In this example, the upstream repository made a change to "a" that conflicts with my local, uncommitted change to the same file. It uses its normal merge machinery to try to resolve the conflict.

As far as I know, Git doesn't allow merges in this situation.


Oh, that's evil, you don't even need to --force it? :-)

So there's no way to back out of that right? i.e., get back "a's" state before you ran update?

(Ah, I see from update's help that you have to use --check to prevent it from touching uncommitted files.)

Hmm.


What reason could there possibly be to downvote the parent? Dogmatic Git fans, you are turning me off to Git more and more.




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

Search: