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

If you don't understand git, then don't mess around with 'rebase', 'push -f', or any other command that tries to edit history. These commands assume that you have a strong mental model of how git works, and this is how the author of the article got into trouble.

It's possible to build a very successful git workflow using only the following commands:

    git clone git:...
    git add path/to/new_file
    git commit -a
    git pull
    git push
(Yes, commit before pulling, rather than vice versa.)

If you want to use branches, you need to add:

    # Showing branches.
    git branch -a
    gitk --all

    # Checking out branches.
    git checkout some_branch

    # Creating and pushing a new branch.
    git checkout -b new_branch_name
    git push -u origin new_branch_name

    # Checking out an existing branch.    
    git checkout -b some_branch origin/some_branch

    # Merging a branch into your current branch.
    git pull origin some_branch
This workflow is extremely safe. At worst, you might need to resolve a merge conflict.

But if you start digging around under the hood, and you start editing your commit history, you'd better be prepared to understand what you're trying to do.

A thousand times this. For my first year to year and a half of using git, I didn't even know that you could re-write your history. I just started out with a personal repo on my own machine, where I'd init, add, rm, commit, and look at logs. Then I learned how to start working with someone else's repo, which introduced me to cloning, pulling, pushing, fetching, and all that jazz. As the project sizes grew, I realized how useful branches were, so I became more familiar with tracking things upstream, merging, and some more advanced workflows. Finally I started poking around in the git object model, and learning how I can clean up my local history before a push. It was baby steps all the way.

Trying to use rebase the same week or month that you learn git (or version control period), while doable for some, is just crazy for everyone else. You don't jump into riding a bike before you learn to walk.

I really don't like "git commit -a". I've seen people new to git add every file in the project directory without checking what files are there. This includes merge conflicts, SQL dumps and random backups.

If you do a "git status" immediately before then "git commit -a" can be a slight time saver; though It's better to use "git add -u", which stages all files that are in the repo and ignores any new files. Personally, I like adding files either 1 or 2 at a time or even with "git add -p".

While I'm at it, lots of people seem to have poor (single line) commit messages, stemming from the use of "git commit -m". Whilst this is OK on some occasions, commit messages are often better thought of as an email with a subject and a body.

(Yes, I know sometimes a single line commit is all you need and you can write multi-line commits directly on your shell but some people don't know this and it limits their ability to write a good commit message.)

Feel better getting that out my system. Now I need to tell my co-workers.

"git commit -a" actually does not add every file in the directory. It only adds those that are already in the repo.

That's embarrassing. I meant "git add -A" or "git add .". I guess "git add -a" or "git commit -a" isn't so bad but I still think care should be taken when adding files to the index, especially with newcomers to git, who pick up habbits from tutorials like this.

If I follow your workflow, my coworkers go ballistic. The problem is that the "git pull" messes up the revision history. I finally learned to use "git pull --rebase".

Technically speaking it doesn't "mess up" the revision history, it preserves it. Rebasing instead of merging creates a linear revision history, which is simpler and more similar to centralized version control, but at the cost of losing information about the actual path of development.

All true. Certainly nothing is "messed up".

That said, reading through a bunch of fine-grained and uninformative merge commits when looking at a log is really annoying. Serious projects expect submissions to be in the form of clean patch sets that apply in series and "look like" an instantaneous change. No one cares about per-developer histories.

If you look at the kernel history, for example, the only merge commits you see are Linus's and the subsystem maintainers'.

Right, because the kernel patches are emailed and never pushed to a public location. In essence, the "committers" of the kernel are the only ones who do rebases (either explicitly or implicitly).

Frankly, git was not designed for a lot of the use cases it's finding itself in. Medium sized agile teams (5-10 devs) with multiple people mucking around in the same files will be removing bullets from their feet repeatedly for a few weeks when first switching.... OR they will have an incomprensible tangle of branches and merge commits that no person will ever be able to figure out.

That may be overstating things. The incomprehensible tangle is really just an annoyance. Look at it with "git log --no-merges" and it looks exactly like the tangle you'd get with CVS or subversion: lots of independent unsequenced changes by different developers.

Git by default shows this stuff, which in the kernel's use case is useful data. But in this case it's just useless chaff, and an annoyance. But hardly a serious problem.

It only "messes up revision history" if you do your development on your remote tracking branch that multiple people are pushing to. If you keep your remote tracking branch clean (e.g. master), you can pull into master whenever you want, merge your changes from a local branch, and push it right back out. Done this way, you never have to use pull --rebase (which will rewrite local history).

This workflow actually works pretty well with your desired outcome as well - you can rebase your local branch against newly pulled changes on master. This will make all of your local branch merges look like fast forward merges on master (i.e. no merge commit, linear history).

My workflow is like this:

- Create a feature branch off of development. - Commit lots of times until that's ready. - Switch back to development. Do "git merge --squash featurebranch" This introduces all the changes from the feature branch as uncommitted changes. View the diff to ensure it's exactly what I wanted, and commit them with a single, coherent commit message.

As a SVN user who is not afraid to use feature branches, this "no branching" workflow of many git users amuses me to no end.

I have found that branches are the default for git users working on any project larger than a toy.

You're kindly assuming people never enter the wrong command by accident.

Undoing a commit, for example, is very common and necessary, especially for beginners.

The easiest way to undo a commit is to treat git just like you would Subversion or Mercurial: Use 'git revert $COMMIT_ID' to reverse the generated commit, or simply fix things by hand and commit again.

Sure, you can edit the broken commit, if you value a pretty commit history. But again, this requires understanding git well enough to predict the effects. What happens if you run 'git commit --amend -a' after pushing the original commit? What if another user already pulled? How will you fix the resulting mess?

If you can answer these questions, then you'll have no problems editing history. If you can't, then you may be signing up for a lot of pain.

Good advice. I would add the following use case:

    # Oh no! Those changes should have been on their own branch, not dev...
    git stash
    git checkout -b changes_on_their_own_branch
    git stash apply
    git commit -a

That is overly verbose. Try this:

    git checkout -b changes_on_their_own_branch
    git commit -a
You can always create a new branch at HEAD (where you are currently) and switch to it without having to stash.

On the other hand, stashing may be required if you wanted your branch to start elsewhere:

    git checkout -b changes_branch start_point

But doesn't git stop you from switching to another branch when there are uncommitted changes?

Git only prevents switching branches if the act of switching branches would modify the files you have uncommitted changes to. In the case of fr0sty's suggestion, the new branch cannot have any changes because it's being created from the current HEAD. But even if you're switching to a pre-existing branch, it may still work depending on what files git will need to touch.

In other words, no harm in trying the `git checkout other_branch` first, and only stashing if that fails.

As GP said, not if you're creating a new branch. There's no history in the new branch to potentially overwrite your uncommitted changes in the working tree, so nothing for it to object to.

Couldn't agree more! I have lead quite a successful 'git life' without using any complex commands and therefore have never found git hard!

Why the -u on push?

It allows you to use `git pull/push' without specifying the remote. see: http://mislav.uniqpath.com/2010/07/git-tips/ Section "Push a branch and automatically set tracking"

It creates a tracking branch, and so git knows how far a branch is relative to a remote branch.

       -u, --set-upstream
           For every branch that is up to date or successfully pushed, add upstr
           eam (tracking) reference, used by argument-less git-pull(1) and other
           commands. For more information, see branch.<name>.merge in git-config
--man git-push

To checkout an existing branch, you need only do 'git checkout new_branch_name' - if the branch exists on the remote, git will automatically create a local version and set it to track the upstream version.

Applications are open for YC Summer 2018

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