

Aha Moments When Learning Git - p4bl0
http://betterexplained.com/articles/aha-moments-when-learning-git/

======
kalid
Hi all, thanks for attention! My passion is finding the "aha!" moments when
learning. In git:

    
    
      - Git has a staging area before you commit
      - Branching is "save as..." on a directory
      - Imagine virtual directories inside your physical one
      - Show the current physical & virtual directory in your prompt
      - Visualize your branching strategy
    

That's what I'd explain if I had 30 seconds to tutor git in an elevator -- the
rest can be looked up =).

Shameless plug: I'm making a general platform to share these "aha" moments,
described here:

[http://betterexplained.com/articles/share-your-insights-
aha-...](http://betterexplained.com/articles/share-your-insights-aha-
betterexplained-com-beta/)

The goal is to find and filter the best insights that really helped when
learning.

~~~
Joeboy
That's cool. Something I noticed - if you sort by 'most liked' it seems to be
the most liked entries at the bottom, which probably wasn't the intention.

~~~
kalid
_forehead slap_ Thanks, I'll be fixing that.

------
xbryanx
My Git aha! moment was when I discovered "git add -p".

I use it all the time now to break my work apart into multiple atomic commits.

~~~
zerd
The problem is that if you do a partial commit, you have to test that it
works. You may have forgotten to add one line, etc.

One way to do this is to use "git stash save --keep-index", after patching.
Then you can test that your program works with only that patch. After
committing you can "git stash pop" to patch again or commit the rest.

Maybe it's possible to make a hook to make this easier.

~~~
Pistos2
I use git add -p to split apart what I intend to eventually fully get into the
repo. Usually, this means separating whitespace cleanup from meaningful
commits. Sometimes I use it to make my commits "distinctly describable" --
instead of a big commit saying (in effect) "I did lots and lots of stuff
generally to do with BigTask", I am able to describe various steps of that
work, e.g. "Refactored models.", "Added supporting .js file for foo
mechanism.", "Migration for bar process.", "Controller method and view for baz
form."

Generally, I reserve the "make sure it works" step for just before _pushing_
(i.e. making public), rather than just before committing.

I always use either `git commit -av` or `git add -p`, so I can preview what
I'm about to do. I never use the freewheeling `git commit -m`.

~~~
zerd
Problem with not testing your "micro-commits" is that when someone is looking
at old revisions to figure out where something went wrong, there is a chance
that it won't compile, or will crash because you didn't test that exact
commit.

~~~
Pistos2
I prefer smaller commits to larger ones because it isolates things (both
improvements and breakage). I use git bisect when needed. You can always
squash small commits into larger ones if really necessary, but teasing apart
large commits into small, logical steps or components is much harder. Smaller
commits are more "mobile" in that they can be cherry picked (in or out).

------
davvid
The article describes a "parse_git_branch" shell function for use in a shell
prompt. A much better function is included with git's completion script and is
called "__git_ps1".

It can be found in /etc/bash_completion.d/git if you installed git using apt-
get or something similar. It lives at contrib/completion/git-completion.bash
in the git source tree:

[https://raw.github.com/git/git/master/contrib/completion/git...](https://raw.github.com/git/git/master/contrib/completion/git-
completion.bash)

In addition to "__git_ps1", you also get amazing tab-completion for git
commands when you source it. Here's how I use it in my prompt. There are
simpler examples in the script itself.

    
    
        export GIT_PS1_SHOWDIRTYSTATE=yes
        export GIT_PS1_SHOWSTASHSTATE=yes
        export GIT_PS1_SHOWUNTRACKEDFILES=yes
    
        git_prompt='$(__git_ps1 " \[\033[0;34m\](\[\033[00m\]\[\033[01;32m\]%s\[\033[00m\]\[\033[0;34m\])\[\033[00m\]")'
    
        PS1="\n\[\033[0;34m\]# \[\033[1;32m\]\u\[\033[0;34m\]@\[\033[1;32m\]\h\[\033[0;34m\]:\[\033[1;32m\]\w\[\033[1;32m\]$git_prompt\n\[\033[1;36m\]%\[\033[0;39m\] "

------
skybrian
This article is using "GUID" in an unusual way (for the general concept rather
than the specific format). I think that's likely to confuse people. Better to
be more precise: Git uses a sha1 hash as a unique id.

~~~
prodigal_erik
Worse, "GUID" suggests that git assigns random ids to blobs and trees, which
is not true at all. Two files with the same contents are intentionally given
colliding object ids, not unique ones. Only commits (and tags?) get ids which
are effectively unique (because they're distinguishable at least by the
embedded timestamp).

------
uast23
>>Why stage? Git’s flexible: if a, b and c are changed, you can commit them
separately or together

This is the saver I would say. More often than not it so happens that once I
am done with an implementation/issue, I get carried away (I am working on
fixing this habit) and continue writing code in order to get as much out of me
as possible and then suddenly realize that one of the earlier fixes should
already be in the repository. Thats when git staging comes into picture.

~~~
glenjamin
I find git-cola an excellent tool for separating a large commit into useful
parts.

~~~
bostonvaulter2
You can also use "git checkout -p" for this.

------
sunnyprogram
My Aha moments:

1)Discovering gitx for Mac Os. Specifically the brotherbard version.
<https://github.com/brotherbard/gitx> Use this daily.

2)Learning about git rebase <branch> to make commit history more readable

~~~
ralfd
v0.7.1 is from September 2009, a bit old. Is this the best Git App for Mac OS
X?

~~~
to3m
Plain old `git gui' is worth looking at. Better keyboard support than GitX
(though this is not saying much) and the UI for committing lines works better.
If I've made a big pile of little changes and I'm trying to split them into
multiple commits, I've found git gui way better than GitX.

The history view of GitX is better than `gitk --all', though, and of course
having both history and day-to-day duties in the same app is useful.

There is another app called GitY, which I hated immediately. It shows your
unstaged changes and your staged changes and your untracked files all in the
same window, which I found very confusing, and there doesn't appear to be any
way to add individual lines into the index, which is something I do all the
time.

------
gitmaster
My 'Aha' Git moment:

Discovering that '-d', '-D', 'rm', and ':' are all inconsistent UI behaviors
for removing things

Consistent behavior is "distributed" across multiple commands. Variety is
indeed the spice of life!

------
edoloughlin
Some good points in there but I think the section on branching strategy might
be a throwback to the old, centralised paradigm. You can have master/dev
repos, with feature branches off dev. Not sure if you need a release repo, but
the main point is that access-controlled repos can replace the branching you'd
use in older systems.

------
yxhuvud
"Easily merge changes with the original (changes tracked and never applied
twice)"

Heh, guess he never tried to merge two things that was a series of commits
that both contained renaming of something to something else. Lets see if I can
visualize the situation:

Original branch A with file foobar.foo and contents z. branch B from A with
one commit with foobar.foo renamed to foobar.foo.in and contents changed to
z'. branch C from A with two commits. The first commit changes the contents of
foobar.foo to z''. The second changes the name of foobar.foo to foobar.foo.in.

z' and z'' generate a merge conflict.

Have fun merging...

~~~
fr0sty
Can you point to a version control system that won't fall all over itself in
this case?

Also, what would an "ideal" VCS do in such a situation? Btw, have you tried
this with git? What happens?

~~~
yxhuvud
No, I was replying to the claim that merging works and that stuff is never
applied twice. Which it was. Specifically git suggested that I had a
_deletion_ of the _new_ file unmerged.

Thinking back on it, this doesn't make sense. I think the situation involved
rebase as well. That is, rebasing a rename onto a branch that already has done
the rename in question turns the second one into a deletion. (Or at least adds
the deletion of the second file to the list of unmerged changes).

