**Simple git Recipes**
2020-05-01
Maybe you're new to revision control and need to know the basic
commands for `git` to get on with your project. Maybe you're
experienced with subversion and it worked well for you, but the new
engineer/faculty/students/IT folks like git. Almost certainly you want
the free hosting that comes with github.
Here's my cheat sheet of common git commands. This idiomatic approach
to git is useful for students and researchers working in small teams
on rapid prototypes and experiments under short deadlines (or hobbyists working
alone). That is, a development scenario
where you want to _maximize the speed_ of integrating changes between
developers and minimize time spent with tools, because the
cost of recovering from the rare accidentally-committed bug is much lower
than the cost of avoiding that problem in the first place.
In this case, it makes sense to favor sweeping tool commands and use direct communication between
people instead of fine-grained tools process to avoid mistakes. This
is in contrast to production development. In production, code reviews,
automated tests, releases, continuous integration, and more complex
versioning strategies are designed to _slow down_ code integration,
because committing a bug can affect many people and be expensive to recover from.
I'm assuming that you're using github or your institution's gitlab
installation, so there's a website for creating and managing your
repositories. I'll call that "the website".
Most of the operations below are standard concepts across all version
control systems. I mention some more git-specific features that are
occasionally handy but not too hardcore at the bottom. If you want to
get hardcore, then there's as much depth as you could want in git, but
this is the wrong article for you to read.
All commands I give below are identical on Windows, macOS, and Linux,
except for installing git itself and the repo initialization "ignore"
line.
Overview
=================================================================================
If you're completely new to version control, then I recommend
the Version Control Basics chapter of the [Graphics Codex](http://graphicscodex.com/)
that I wrote covering svn, git, and other systems.
If you're already familiar with version control as a concept, then
the essential differences of git for you are:
- *Use the command line.* Git has a complicated model and complicated
commands, and you can get into some nasty states that are hard to
recover from if using a GUI blindly. Really, the command line is
_less_ confusing in this situation.
- Most git commands are not sensitive to where in the local workspace
directory tree they are run. There's no notion of a per-file version,
so most commands affect the entire workspace.
- Pushed commits are generally permanent. Once anything is in the repository,
it is visible to everyone and takes up space forever.
If you accidentally commit a password or humongous file then you either
have to delete and recreate the entire repo or engage some very hardcore
commands. Search for "editing history", `git rebase`, and `git filter-branch`.
If it was a large or binary file in LFS, then deleting the repo is the only
choice.
- Read access control is per-repository. Write access control is per-branch.
Both are managed via the website. There is no per-directory access control.
- There are lots of options for commands and other commands. I'm
giving idioms for sweeping changes appropriate for small, usually
academic groups who just want to synchronize files with each other.
People have strangely strong feelings about git. Some of them will
be happy to tell you how misinformed and heretical my idioms are.
- You may hear about different ways of handling large files and
repositories. "LFS" is the current practice. `git-annex` is obsolete.
"[Partial Clone](https://about.gitlab.com/blog/2020/03/13/partial-clone-for-massive-repositories/)"
and
"[Sparse Checkout](https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/)"
are compatible with LFS and intended to eventually replace it, but
have inconsistent support (and billing, on github) as of the year
2020 and git 2.26.
- Git refers to versions (aka "commits") by long hash values. There is no notion
of linearly numbered revisions or dates, although the website features and log
let you find the revisions that happened on a specific date and
also let you flag "releases" using names similar to svn tags.
- Git branches are different realities of the repository. They are not
in a hierarchical namespace, are all equally authoritative, and can
be arbitrarily merged into each other in any direction. You see only
one at any time in your workspace, as a view of the repo. Most large
projects impose a hierarchical worklow by convention. When you have
a local workspace, you generally have _all_ branches stored on your
machine but are only looking at one at a time. Everyone can see all
[remote] branches that have been pushed.
- The default branch is called "`master`" (which will hopefully be
renamed to "`main`" soon; everyone is waiting on github to fully support
this on their website). If you have to supply a
branch name to a command and don't know what is going on, that's the
one that you want. Simple branching in git is not too hard, although
for solo work or small teams, you can probably work directly in
master. I usually do.
- The all-capitals "`HEAD`" is the latest revision ("commit") in the branch that your
local workspace is viewing. _A_ lower-case `"head"` is that revision for any branch.
A `"detached HEAD"` means that you're looking at some older revision
in a branch, and should probably return to the latest revision before
doing new work there.
- github currently offers free hosting for unlimited numbers of _small_ (by my standards) public and private repositories.
In each repository, the [current limits](https://help.github.com/en/github/managing-large-files/what-is-my-disk-quota) are:
- Practical 1 GB limit for the whole repository, including history and branches
- Theoretical 75 GB limit
- Definitive 100 GB limit
- 100 MB per non-LFS file
- 1 GB total [LFS](https://help.github.com/en/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-git-large-file-storage) across all repositories, and 1 GB monthly bandwidth for free
- Additional LFS in increments of $5/month per 50 GB
- gitlab is an alternative site with different pricing. It also allows and you to download their
installation to make your own institutional github equivalent.
- Bitbucket, Sourceforge, etc. are other hosted git sites with other pricing schemes.
Install
=================================================================================
Once per machine, you need to install git and LFS (Large File Storage).
macOS ("OS X")
---------------------------------------------------------------------------------
1. Use the version of git that comes with Xcode, or install via GUI pkg from https://sourceforge.net/projects/git-osx-installer/
2. Download LFS from https://git-lfs.github.com/
3. Double-click to unzip the LFS package and run `sudo ./install.sh` from it
4. Run `git lfs install` (yes, this is different from step #3)
5. Optionally run `git config --global credential.helper osxkeychain` for password caching
6. Optionally install https://github.com/github/hub manually or via `brew install hub`
Windows
---------------------------------------------------------------------------------
1. Install git from https://git-scm.com/download/win
2. Install LFS from https://git-lfs.github.com/
3. Run `git lfs install` (yes, this is different from step #2)
4. Optionally install hub.exe by downloading from https://github.com/github/hub/releases/latest and manually placing somewhere in your PATH
Ubuntu or Debian
---------------------------------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
sudo apt install git
sudo apt install git-lfs
git lfs install
# Optional, for password caching
git config --global credential.helper store
# Optional, for easier branch management
sudo apt install hub
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Other Linux
---------------------------------------------------------------------------------
1. Download and install git according to https://git-scm.com/download/linux
2. Install LFS with according to https://github.com/git-lfs/git-lfs/wiki/Installation
3. Run `git lfs install` (this is different from step #2)
4. Optionally install hub according to https://github.com/github/hub
Fetch
=================================================================================
Download a repo.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git clone
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
where `` is a URL that ends with `.git`. You can find it on your
git website from the green "Clone or download" button on the right
side of the page. If using your institution's gitlab, you may find that the
"SSH" variant fails silently if they have not configured it properly--I stick with
the `https` versions of the git URLs.
(Equivalent to `svn checkout`)
Initialize
=================================================================================
Initialize a new repository that was _just_ created and cloned. Perform
this from the root of the workspace only once per repository ever
(not per user or per machine).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
# Mark large/binary files by extension
git lfs track "*.pdf" "*.doc" "*.docx" "*.zip" "*.rar" "*.tar" "*.gz" "*.class" "*.dll" "*.so" "*.psd" "*.ico" "*.png" "*.jpg" "*.exr" "*.tga" "*.bmp" "*.gif" "*.ppm" "*.tif" "*.m4a" "*.mpg" "*.mp4" "*.mp3" "*.aiff" "*.avi" "*.wav" "*.mov" "*.dxf" "*.fbx" "*.max" "*.mb" "*.ma" "*.ply" "*.obj" "*.3ds" "*.pk3" "*.md2" "*.md3" "*.ttf" "*.woff"
# Ignore certain file patterns. Add your own to this
echo $'*~\nlog.txt\n*.log\n*.pyc' > .gitignore
git add .gitignore .gitattributes
git commit -am "Initial config" && git push
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
On Windows, there's no simple way to echo a newline, so create the `.gitignore` file
in a text editor.
(Equivalent to a lot of `svn propset` commands)
Add
=================================================================================
You've created a new file or directory that you want in the repo.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git add
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The command is automatically recursive if adding a directory.
You still have to upload the file(s).
Note that running `git add` on a file that is already in the repo does
something _different_, but relatively innocuous.
(Equivalent to `svn add`)
Upload
=================================================================================
Take all of the changes that you've made locally and upload them to the server.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git commit -am "log message" && git push
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The `-a` part of the command is specifically including all of your changes. Git
has ways of commiting only individual changes if you need a scalpel instead of
a hammer.
Beware that your push may fail if there are changes that you haven't downloaded
yet with `git pull`. Git will give you a clear message in this case, but it is
easy to ignore in all of the progress spam that it prints, so be sure to look
for confirmation that you've actually pushed.
(Equivalent to `svn commit -m "message"` in the workspace root)
Download
=================================================================================
Download the changes that others have made to your local machine.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
# Get changes to all branches:
hub sync
# or, to get changes to only the branch that you are currently on:
git pull
# (and then clean up any merge conflicts)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Equivalent to `svn update` in the workspace root)
Status
=================================================================================
Show me the names of all files that I've changed since my last commit/push:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git status
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Equivalent to `svn status` in the workspace root)
Compare
=================================================================================
Show me what I've changed in a specific file since my last commit:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git diff
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Equivalent to `svn diff`)
Move
=================================================================================
Relocate or rename a file or directory within the repo:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git mv
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Commit/push soon afterward. The way that this command actually works can cause
some unpleasant surprises during a merge if someone else edited the file.
(Equivalent to `svn mv` or `svn rename`)
Delete
=================================================================================
Remove a file from the repo, (it is still visible and recoverable in the history):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git rm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Equivalent to `svn delete`)
Undo
=================================================================================
This is one place where the complexity of git can really bite
you. There isn't a straightforward "revert" because committing isn't
as straightforward as I've made it seem.
For simplicity, I'm assuming that you followed my above idioms and
never used the "staging" feature of git separately from the `git
commit` command, or `git commit` separately from `git push`.
If you get really stuck, beware that it may be faster and safer to
delete and re-clone the repo than to fix local git state if you aren't
experienced. Look at the Help! section first, though.
All Files
--------------------------------------------------------------------------------
You just edited or deleted some files, issued no git commands except maybe
`git checkout`, and now have regrets. Go back to your last commit:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git reset --hard
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is the same as running `git checkout -- .` in the workspace root. The
`--hard` makes the command seem a little scarier, but it is just another ill-named
git option and not doing anything extreme.
(Equivalent to `svn revert ` in the workspace root, but works offline)
Only One File
--------------------------------------------------------------------------------
You just edited or deleted one file, issued no git commands, and now have regrets. Go
back to your last commited version of that file:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git checkout --
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Equivalent to `svn revert `, but works offline)
Already Pushed
--------------------------------------------------------------------------------
Someone (maybe you) made changes and committed and pushed them, and
now you want the repository to go back to the state that it was in
before those pushed commits happened:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
# Look at the log to find the commit hash
git log
# Go back to the hash
git revert && git commit -am "reverted to " && git push
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The hash will be a long, hexadecimal number such as `cf80d8dbacc6fc90e3c86a5f1dac615bd9aab1ae`.
Revert My Whole Branch
--------------------------------------------------------------------------------
You want to keep your branch, but want to reset to the the state of
the main branch.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git checkout my_branch
git reset --hard master
git push --force origin my_branch
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Repo URL
=================================================================================
You need the original repo URL, for example, to share it with a colleague or
to delete and re-clone on your machine:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git config --get remote.origin.url
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Equivalent to `svn info --show-item repos-root-url`)
Offline
=================================================================================
When working without internet access, you might appreciate that git lets you separate
the commit and push, so that you can undo to a checkpoint.
Checkpointing while offline:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git commit -am "log message"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To undo uncommitted changes that you've made since your last commit while offline:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git reset --hard
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To roll back both uncommitted changes _and_ the last commit that you made
while offline (`~1` means "go back _one_ commit"; you can do "~2" etc. The default
is `"HEAD~0"`, which is just "go back to the state of the last commit" from
the previous idiom):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git reset --hard HEAD~1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When back online:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git push
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Partial Clone
=================================================================================
You have a large repo on the server, and only want to clone some
subset of directories. For example, maybe you keep all of your
homework assignments or blog entries in the same repo and only want
one.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git clone --no-checkout
cd
git sparse-checkout init --cone
git sparse-checkout set <...>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each of the `` entries may be an immediate child of the root or a more complicated
path into the repo.
For more information, see https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/
(Equivalent to `svn checkout --depth files; svn update --set-depth infinity <...>`)
Help!
=================================================================================
Bad Merge Conflicts
---------------------------------------------------------------------------------
You ran `git pull` and there were more conflicts than you are willing to deal with.
If you want to undo the pull, immediately run:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git merge --abort
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You will now be at your last commit, where you were immediately before you pulled.
You're going to have to deal with that merge at _some_ point, though. You've only
kicked the problem down the road.
If you instead want to discard all of your own unpushed changes and have clean
state matching a fresh clone of the main branch, use:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git merge --abort && git reset --hard origin/master && git pull
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Equivalent in subversion to deleting the conflicted files and then running
`svn update`)
Detached HEAD
---------------------------------------------------------------------------------
You encounted "detached HEAD" and didn't expect this, weren't
intentionally using branches, don't have changes that you want, and
just want everything to go back to the last commit:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git reset --hard
git checkout master
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Really, only one is probably necessary, but you're in a bad way here)
Untracked Files after Pull
---------------------------------------------------------------------------------
You pulled or cloned and git got confused along the way. Now you have
tons of "untracked" files that git itself just dumped into your
workspace and it refuses to let you merge until _you_ clean up after
it. You see something like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ none
error: The following untracked working tree files would be overwritten by merge:
...
Please move or remove them before you merge.
Aborting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These aren't files that you created. They just showed up during the
pull. Maybe there was a complicated LFS issue such as "smudge filter
lfs failed", a process crashed while writing to disk, or your network
went out at the wrong time. You just want everything to work again.
My solution in this case is to bring out the big hammer: wipe it all
out, rather than spend time on cleanup.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
# Print the repo url
git config --get remote.origin.url
cd ..
# Delete your workspace
rm -rf
# Clone again
git clone
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Static Web Hosting
=================================================================================
If you're specifically using github for repository hosting, then you
can also use the feature called "github pages" to host a version of
your repository directly on their website. This is very convenient for
making project pages, friendly indexes of your content, or even blogs
and static web applications. This article is hosted on github pages.
1. On the repository git website, click on "Settings".
2. Scroll down to "GitHub Pages".
3. Under "source", select either "main branch" or "main branch /docs folder",
as you prefer.
The page will then display the URL at which your repo is hosted. Note that it
is on github.io, not github.com, for security reasons. It updates nearly instantaneously
after a push to master.
You can host any static web content using this feature. There are some limited
features for dynamic content and database access as well.
This does _not_ work with LFS files. They will simply appear as the underlying git
references to the file.
Stash
=================================================================================
_We're now into somewhat more advanced commands_
Undo to the last `git push` so that you can fix a new problem in the middle of your
other work, but save the changes were were working on to return to:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git stash save "message"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Later, get back to what you stashed:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git stash pop
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(The last command is "pop" because stashes form a stack. The first
command is not called "push" presumably because that name was already
used for "upload".)
Branches
=================================================================================
I'm describing what git calls "remote" branches, which are what you want if you're
only using one kind. There are also "local branches" and "local nontracking branches".
Subversion branches are lazy-copied entire directories. That's such a different concept
from git branches that I chose not to include any SVN analogous commands here.
Git branches are fairly easy to use for simple cases. For many of my changes, I still just
work directly on the main branch on research and prototyping projects because "fairly easy" isn't the same as "zero cost tooling".
But when I'm making a "build breaking" change such as a refactor or there are a large number of collaborators, I start using
branches in lightweight way. Once a branch is created, my typical workflow is:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
# Go to my branch
git checkout
# ... make changes
# Commit
git commit -am "" && git push
# ... repeat until reaching a good stopping point
# Bring in the latest changes from master to my branch
git merge master && git push
# Move my changes to master
git checkout master
git merge && git push
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create
---------------------------------------------------------------------------------
Create a branch, switch to it, and push the branch to the server:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git checkout -b
git push -u origin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can only do this if you have no changes. So, plan ahead and make the branch
before you do the work. It is possible to make the branch retroactively, but
tricky.
Discover
---------------------------------------------------------------------------------
List all branches:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git branch
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You are currently on the one that prints with an asterisk.
For scripting purposes, you can generate just that branch name with
the command `git rev-parse --abbrev-ref HEAD`.
Change
---------------------------------------------------------------------------------
Switch your workspace to another branch (including one that someone
else created or `master`), losing any uncommitted changes:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git checkout
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Return to the main branch with `git checkout master`.
Merge (Manual CLI)
---------------------------------------------------------------------------------
Merge another branch _into the current one_ (which might be `master`):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git merge
# Fix conflicts manually...
git commit -am "merge" && git push
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Merge (Web Pull Request)
---------------------------------------------------------------------------------
To submit a "pull request" for someone else to approve and merge
from your branch to another (usually `master`) using the website:
1. On the website, click on "Pull Requests"
2. Select the green "New pull request" button.
3. Select the source and destination branches
Merge a pull request on the website:
1. Click on "Pull Requests"
2. Select a request
3. Follow the GUI options
Delete
---------------------------------------------------------------------------------
Delete a branch:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash
git push --delete origin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
_Thanks to Zander Majercik for checking my git commands and debating the safest idioms._