Skip to content

Useful Git Commands

Kelly Werner edited this page Oct 22, 2018 · 1 revision

This is a page to keep a list of helpful git commands and git-related tips.

NOTE: Many of these options can be used in combination! If you note two different options for git log, for example, it's likely they can be used together. In addition, flags for different commands might be compatible if they achieve similar things: for example, many flags which are valid for git diff and git log can also be used with git show.

Table of Contents

git stash

One of the big conveniences of git is the ease with which you can create and switch between different branches. However, it's not always convenient to check out a branch while you're currently working on changes in another branch. While you could commit those changes, you might not always want to if they are in an incomplete state. This is where git stash comes in!

Say you have made some changes to a couple files while on a branch:

$ git status
On branch more_git_updates
Your branch is up-to-date with 'origin/more_git_updates'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   Registry/Registry.EM_COMMON

In the middle of your changes, you want to view the state of the master branch. You could commit your changes to this branch and then switch to the master, but if your changes are incomplete and won't compile, that's often not desirable. To temporarily stash your changes away, use git stash:

$ git stash
Saved working directory and index state WIP on more_git_updates: 41430b3 Per feedback from Negin Sobhani, reducing the number of .gitignore files by adding *.f90 rule to top-level .gitignore, then adding exceptions in external/.gitignore and var/.gitignore.
HEAD is now at 41430b3 Per feedback from Negin Sobhani, reducing the number of .gitignore files by adding *.f90 rule to top-level .gitignore, then adding exceptions in external/.gitignore and var/.gitignore.

Now running git status will no longer show your changes:

$ git status
On branch more_git_updates
Your branch is up-to-date with 'origin/more_git_updates'.
nothing to commit, working tree clean

You can now check out a different branch, see what you need to see, and come back.

$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
...
...
$ git checkout more_git_updates
Switched to branch 'more_git_updates'
Your branch is up-to-date with 'origin/more_git_updates'.

To see a list of stashed changes, use git stash list

git stash list
stash@{0}: WIP on more_git_updates: 41430b3 Per feedback from Negin Sobhani, reducing the number of .gitignore files by adding *.f90 rule to top-level .gitignore, then adding exceptions in external/.gitignore and var/.gitignore.

If you have stashed more than one change, more than one line will be listed here. In this case, there is only one, so git stash apply will give us back our changes, and will automatically show us a git status output to show us what our working directory now looks like:

$ git stash apply
On branch more_git_updates
Your branch is up-to-date with 'origin/more_git_updates'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   Registry/Registry.EM_COMMON

no changes added to commit (use "git add" and/or "git commit -a")

Since the changes were successfully applied, it's a good idea to delete the old stash before continuing your work:

$ git stash drop
Dropped refs/stash@{0} (e7a3e42a42bcfbd4d2f050ee03c156149d45e085)

You can now get back to work on your changes as normal.

There are other situations where git stash might be useful, but I'd recommend using it sparingly and cautiously, as the stashed work does not become a permanent part of your repository: you can't push it or pull it anywhere, and it doesn't show up in logs or diffs. For more info, see the git documentation page: https://git-scm.com/docs/git-stash

Setting up an SSH key

Setting up an SSH key with your Github account allows you to clone, push, and pull code without having to enter your password every time. This can be very convenient, especially if you do github functions with scripts. It is machine-specific, so you must set up a different SSH key for every different machine you plan to work on.

To check if you already have an SSH key set up on your machine, issue the command ls ~/.ssh/id*pub. If there are files with this naming convention found, you probably already have an SSH key set up!

If you already have an SSH key set up on your machine, you can skip to step 3! Otherwise, go to step 2 to set one up.

Step 2: Create an SSH key on your machine

There are good instructions on creating an SSH key at this page. No need for me to copy it!

Step 3: Add the SSH key to your account

Again, Github already has some good instructions on doing this at this page.

After adding the SSH key to your account, you will no longer be prompted for your password when cloning, pushing, or pulling code on this machine. Remember you'll have to do this process individually for each machine you want to use SSH for.

git diff -w

Often (for me, at least), people make whitespace changes to files. For example, when fixing the indenting of some old code, or changing tabs (ugh) to spaces as is standard in the WRF code. However, when this is in combination with other, actually important changes, sometimes we just want to see code that changed when looking at a diff. You can achieve this with the "-w" flag:

git diff -w master

This flag can also be used with git show!

git show -w

Note that not all whitespace changes will be ignored: if you insert or delete a new line that consists entirely of whitespace, or combine two lines that used to be separate, those changes will still appear

git log --since

A lot of times we just want to see the log since a certain time or commit. This is easy to do with the "since" option in git log:

git log --since=YYYY-MM-DD

If you don't specify a time, git defaults to the last second of the day (so the list of commits it gives you will generally start after that day). If you'd rather get a different behavior, you can also specify the time:

git log --since="YYYY-MM-DD hh:mm:ss"

In addition, if you know a commit hash, you can specify that hash to see all the commits that have happened since then:

git log --since=abcd1234

git commit --amend

A common problem in version control is realizing you made a mistake in the last commit. For example, let's say I make a commit to add some new options in var/README.namelist. To do so, I make the changes, use git add to stage them, and commit them.

> vi var/README.namelist
> git add var/README.namelist
> git commit

Editing the commit message in vim

> git commit
[fix-some-stuff 53cbf76] Added a few lines for new WRFDA options in var/README.namelist (ep_para_read and rden_bin)
 1 file changed, 2 insertions(+), 2 deletions(-)

However, after I made the commit, I realized I forgot one of the new options. Luckily, with the git commit --amend option, there's no need for me to have two commits for the same change! I just edit the file again, stage it again (with git add), and use the magic of git commit --amend to "amend" my new changes to the previous commit.

> vi var/README.namelist
> git add var/README.namelist
> git commit --amend

By default your original message will appear, but you can edit it! In this case, I'm going to add mention of the new variable that I forgot.

Editing the original commit message in vim to update it

> git commit --amend 
[fix-some-stuff bc35534] Added a few lines for new WRFDA options in var/README.namelist (ep_para_read, rden_bin, and use_4denvar)
 Date: Thu Oct 27 21:12:18 2016 -0600
 1 file changed, 3 insertions(+), 3 deletions(-)

After I save and quit, I will see the updated log message on the screen. When I do a git log, I will see a single commit with both sets of changes I made!

> git log
commit bc3553488d0021117f2d1414feafd4ef0529caee
Author: Michael Kavulich <[email protected]>
Date:   Thu Oct 27 21:12:18 2016 -0600

    Added a few lines for new WRFDA options in var/README.namelist (ep_para_read, rden_bin, and use_4denvar)

commit 25c8d802fb7c0d7945aa34c6b56dc72ef290ec24
Author: smileMchen <[email protected]>
Date:   Fri Oct 21 11:04:33 2016 -0600

    FLG radiation scheme 320 -> 345 K fix (#21)

    Type: Bug fix

    Keywords: FLG radiation scheme

. . .

This process can also be used to correct a wide variety of other mistakes in your commit! Whether you made an error in the commit message, forgot to add a file, accidentally included changes that shouldn't have been included, committed as the wrong user, etc.: all of these situations can be fixed with git commit --amend. And you can amend a commit as many times as you want, so long as you don't make a new, different commit after the old one you want to correct.

git var -l

List all the git "variables", including information about where your repository came from (origin), who created the clone, what your default editor is, etc.

> git var -l
core.trustctime=false
credential.helper=osxkeychain
user.name=Michael Kavulich
[email protected]
push.default=simple
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
remote.origin.url=https://github.com/wrf-model/WRF
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.master.remote=origin
branch.master.merge=refs/heads/master
branch.AER-radiation.remote=origin
branch.AER-radiation.merge=refs/heads/AER-radiation
GIT_COMMITTER_IDENT=Michael Kavulich <[email protected]> 1476414213 -0600
GIT_AUTHOR_IDENT=Michael Kavulich <[email protected]> 1476414213 -0600
GIT_EDITOR=vim
GIT_PAGER=less

git show [hash]

Shows the log message and the text diff for a given commit.

>git show e005cab
commit e005cab00e829f842def74b9f8812b63809e3a02
Author: kkeene44 <[email protected]>
Date:   Fri Sep 23 14:16:39 2016 -0600

    Fixed restart problem for tmn_update option

    TYPE: bug fix - posted

    KEYWORDS: tmn_update, restart, Registry, Registry.EM_COMMON, regional climate

    SOURCE: Heimo Truhetz of University of Graz, Austria

    DESCRIPTION OF CHANGES:
    Added a "r" in the registry line for TLAG to add variable to restart files. Since TLAG was left out of the restart file, any restart using the tmn_update option started with 0 K as the lower boundary condition for the land model. When running long simulations (i.e, regional climate runs), this caused the soil temperature to cool gradually, eventually affecting the skin and atmospheric temperature. This change corrects the problem.

    This has been an issue since Version 3.7.

    LIST OF MODIFIED FILES : Registry/Registry.EM_COMMON

    TESTS CONDUCTED : Tests conducted to ensure problem is corrected. Regression test passed.

diff --git a/Registry/Registry.EM_COMMON b/Registry/Registry.EM_COMMON
index 1ecc596..710627c 100644
--- a/Registry/Registry.EM_COMMON
+++ b/Registry/Registry.EM_COMMON
@@ -1674,7 +1674,7 @@ state    real  TMN              ij      misc        1         -     i012rhd=(int
 state    real  TYR              ij      misc        1         -     rd=(interp_mask_field:lu_index,iswater)u=(copy_fcnm)     "TYR"        "ANNUAL MEAN SFC TEMPERATURE"           "K"
 state    real  TYRA             ij      misc        1         -     rd=(interp_mask_field:lu_index,iswater)u=(copy_fcnm)     "TYRA"   "ACCUMULATED YEARLY SFC TEMPERATURE FOR CURRENT YEAR"  "K"
 state    real  TDLY             ij      misc        1         -     rd=(interp_mask_field:lu_index,iswater)u=(copy_fcnm)     "TDLY"     "ACCUMULATED DAILY SFC TEMPERATURE FOR CURRENT DAY"  "K"
-state    real  TLAG             i&j     misc        1         -     d=(interp_mask_field:lu_index,iswater)u=(copy_fcnm)     "TLAG"         "DAILY MEAN SFC TEMPERATURE OF PRIOR DAYS"       "K"
+state    real  TLAG             i&j     misc        1         -     rd=(interp_mask_field:lu_index,iswater)u=(copy_fcnm)     "TLAG"         "DAILY MEAN SFC TEMPERATURE OF PRIOR DAYS"       "K"
 state    integer NYEAR          -       misc        1         -     r        "NYEAR"                  "ACCUM DAYS IN A YEAR"                         ""
 state    real    NDAY           -       misc        1         -     r        "NDAY"                  "ACCUM TIMESTEPS IN A DAY"                      ""
 state    real  XLAND            ij      misc        1         -     i02rhd=(interp_fcnm_imask)u=(copy_fcnm)       "XLAND"                 "LAND MASK (1 FOR LAND, 2 FOR WATER)"          ""

git diff --name-status

git diff --name-status [hash/branch] [hash/branch]

Shows the SVN-style list of changed files between two hashes and/or branches.

  • If given no arguments, it will return a list of changed files that have NOT been staged for commit

  • If given two arguments, it will show the difference between the first branch/hash given and the second. Note that the order is important: If you do this command with two arguments, it will always compare the second argument to the first argument. So if I have a branch "new_branch" where I added a file "new_file", the command to see the difference between that branch and the master should look like this:

     > git diff --name-status master new_branch
     A       README_NEW
    

If I do it backwards, it gives a (perhaps) unexpected result:

    >git diff --name-status new_branch master
    D       README_NEW

This is because it always assumes that the second argument is the branch/hash that you're interested in. So compared to that other branch, README_NEW is "deleted" in the master.

  • If given one argument, it works exactly like above, and git assumes the second argument is your currently-checked-out code plus all staged changes. So in the above example, if I have staged a file named "README_2" with git add, but have not yet committed it, I will see this:

      >git diff --name-status new_branch
      A       README_2
      D       README_NEW