- 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
Shameless plug: I'm making a general platform to share these "aha" moments, described here:
The goal is to find and filter the best insights that really helped when learning.
I use it all the time now to break my work apart into multiple atomic commits.
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.
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`.
git add -p
The base UI is really for picking hunks (the sections you see in a `diff`), although git extended it to allow for splitting chunks into sub-hunks and ghetto-editing hunks.
Other VCS and more details:
* Darcs was probably the first VCS to implement this, it's the default behavior for `darcs record` (which is darcs's `commit` command). Much like git, darcs provides for full hunk edition (splitting hunks into sub-hunks, and removing or changing sections of hunks before recording them).
* Mercurial provides this function through the (built-in) `record` extension. It currently gives no way to split or edit hunks, only to record or ignore them. The crecord third-party extension has these features (and/but a more complex, curses-based, UI)
* `-p` is actually a subset of the much more complex `-i`/`--interactive`, which is a special staging-manipulation sub-shell.
Note however that this is a powerfully dangerous tool, as it lets you commit untested revisions (then again, so does file-selection at commit, which has been available since SVN). Revisions created this way should be tested one by one for breakage, and rewritten if needed.
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:
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.
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\] "
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.
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
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.
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!
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...
Also, what would an "ideal" VCS do in such a situation? Btw, have you tried this with git? What happens?
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).
If you merge B and C you will only have a conflict if the changes in B and C touch the same regions of the file.
If not the merge is made automatically and the changes from both branches are reflected in the new file.
I just did. As long as the changes in branches B and C are to different regions of the file the merge is completed automatically. If not, conflict markers are places in foobar.foo.in for you to resolve there.
Not sure what more you could ask of your Version Control System...