Today I Learned Git Part 7

This is part 7 to my series of posts about learning git. Most of the sections here are from various TILs I've found and hand picked ones I found useful.

Staging Stashes Interactively

The -p flag can be used with git stash, just as it is used with git add, for interactively staging a stash.

So, if you have changes in your working tree that you want to stash mixed with ones that you want to keep working with, then you can simply do:

git stash -p

Read more on interactive staging.

Stashing Only Unstaged Changes

If you have both staged and unstaged changes in your project, you can perform a stash on just the unstaged ones by using the -k flag. The staged changes will be left intact ready for a commit.

git status
On branch master
...
Changes to be committed:

    modified:   README.md

Changes not staged for commit:

    modified:   app/models/user.rb
git stash -k
Saved working directory and index state ...
git status
On branch master
...
Changes to be committed:

    modified:   README.md

Stashing Untracked Files

Normally when stashing changes, using git stash, git is only going to stash changes to tracked files. If there are any new files in your project that aren't being tracked by git, they will just be left lying around.

You might not want these untracked files left behind, polluting your working copy. Perhaps these files change your projects functionality or affect the outcome of tests. You just want them out of the way, for the moment at least.

And so along comes the -u or --untracked flag.

touch new_file.js
git stash
  No local changes to stash
git stash -u
  Saved working directory and index state WIP ...

The Alpha Commit

I like to read commit logs. Today I wanted to see the first commit on a project. Here's what I used:

git rev-list --max-parents=0 HEAD

Show me the commits that led to HEAD in reverse chronological order; then limit that list to the commits with no parent.

Here's a small modification, to show the entire commit rather than the SHA alone:

git show $(git rev-list --max-parents=0 HEAD)

Undo a Git Mistake

git reflog is a record of your actions in Git. With this command, you can undo almost any Git mistake.

git reflog

4bd0090 HEAD@{0}: <bad place>
46bd839 HEAD@{1}: <bad place>
967e214 HEAD@{2}: <last good place>
46bd839 HEAD@{3}: <good place>
967e214 HEAD@{4}: <good place>

git reset --hard HEAD@{2}

Untrack a Directory of Files Without Deleting

In Untrack A File Without Deleting It, I explained how a specific file can be removed from tracking without actually deleting the file from the local file system. The same can be done for a directory of files that you don't want tracked. Just use the -r flag:

git rm --cached -r <directory>

Untrack a File Without Deleting It

Generally when I invoke git rm <filename>, I do so with the intention of removing a file from the project entirely. git-rm does exactly that, removing the file both from the index and from the working tree.

If you want to untrack a file (remove it from the index), but still have it available locally (in the working tree), then you are going to want to use the --cached flag.

git rm --cached <filename>

If you do this, you may also consider adding that file to the .gitignore file.

Update the URL of a Remote

I just changed the name of a Github repository. One of the implications of this is that the remote URL that my local git repository has on record is now out of date. I need to update it.

If I use git-remote with the -v flag. I can see what remotes I currently have.

git remote -v
origin  git@github.com:jbranchaud/pokemon.git (fetch)
origin  git@github.com:jbranchaud/pokemon.git (push)

Now, to update the URL for that remote, I can use git remote set-url specifying the name of the remote and the updated URL.

git remote set-url origin git@github.com:jbranchaud/pokemon_deluxe.git

If I check again, I can see it has been updated accordingly.

git remote -v
origin  git@github.com:jbranchaud/pokemon_deluxe.git (fetch)
origin  git@github.com:jbranchaud/pokemon_deluxe.git (push)

Verbose Commit Message

Git allows you to display a diff of the staged changes in the commit message buffer. This gives you the opportunity to carefully craft your commit message in a way that accurately describes the changes being committed. To display the diff when committing:

git commit -v

Viewing a File on Another Branch

Sometimes you want to view a file on another branch (without switching branches). That is, you want to view the version of that file as it exists on that branch. git show can help. If your branch is named my_feature and the file you want to see is app/models/users.rb, then your command should look like this:

git show my_feature:app/models/users.rb

You can even tab-complete the filename as you type it out.

See man git-show for more details.

What Changed?

If you want to know what has changed at each commit in your Git history, then just ask git whatchanged.

git whatchanged

commit ddc929c03f5d629af6e725b690f1a4d2804bc2e5
Author: jbranchaud <jbranchaud@gmail.com>
Date:   Sun Feb 12 14:04:12 2017 -0600

    Add the source to the latest til

:100644 100644 f6e7638... 2b192e1... M  elixir/compute-md5-digest-of-a-string.md

commit 65ecb9f01876bb1a7c2530c0df888f45f5a11cbb
Author: jbranchaud <jbranchaud@gmail.com>
Date:   Sat Feb 11 18:34:25 2017 -0600

    Add Compute md5 Digest Of A String as an Elixir til

:100644 100644 5af3ca2... 7e4794f... M  README.md
:000000 100644 0000000... f6e7638... A  elixir/compute-md5-digest-of-a-string.md

...

This is an old command that is mostly equivalent to git-log. In fact, the man page for git-whatchanged says:

New users are encouraged to use git-log(1) instead.

The difference is that git-whatchanged shows you the changed files in their raw format which can be useful if you know what you are looking for.

See man git-whatchanged for more details.

What is the Current Branch?

This question can be answered with one of git's plumbing commands, rev-parse.

git rev-parse --abbrev-ref HEAD

The --abbrev-ref flag tells git-rev-parse to give us the short name for HEAD instead of the SHA.

Whitespace Warnings

You can configure git to warn you about whitespace issues in a file when you diff it. This is handled by the core.whitespace configuration. Add the following to your .gitconfig file:

[core]
      whitespace = warn

By default, git will warn you of trailing whitespace at the end of a line as well as blank lines at the end of a file.