**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 ot 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._