The goal of this guide is to make you git literate. Be the friend in git.txt
You will see staged and unstaged changes. Staged changes are what will be commited when running git commit
- You can stage changes using
git add <file/directory>
- You can unstage changes using
git reset HEAD <file/directory>
- You can permanantely undo unstaged changes using
git checkout -- <file/directory>
- Uncommited changes cannot be retrieved.
git diff
Shows the lines added/removed locally since your last commit. Visual Studio's diff tool is also good.
You can also use git diff to compare between branches
git add <dir/file/wildcard>
e.g., git add ./assets/
or git add readme.md
or git add *.txt
git commit
: take a snapshot of the current state of the repository and adds it to the current branch
git commit
e.g., git commit
Opens your chosen editor so you can create your commit message. Save and exit when finished.
You should commit early and often, you can not have too many commits. Ideally you should commit once you've made one logical change. This keeps your commit history easy to read and understand at a glance (and when you look back trying to figure out what you were doing).
Commit messages should be detailed. Use this opportunity to make your changesets clear for review. Reviewing by commit is simpler than reviewing by the entire changeset if the commit messages are detailed and clear.
Don't do this
HEAD
is a ref that points to the currently checked out commit. Typically this is the tip of the current branch, but that is not always the case. git reset
and git checkout
move HEAD.
'detached HEAD' state simply means that HEAD is not pointing to the tip of a branch
A tag is like a branch that cannot be moved. It is a ref to a commit hash.
- list tags with
git tag
, search withgit tag -l "<search-params>"
e.g.,git tag -l "1.*"
- create tags with
git tag <name>
. Tags current commit - create annotated tag with
git tag -a <name> -m <message>
e.g.,git tag -a v1.0 -m "Launch of Product"
- it is advised to tag launches with annotated tags because the timestamp of the commit AND the timestamp of the tag are both saved. In case you want to know when a version was given to a commit, and not just when the commit itself was made.
git branch <branch name>
e.g., git branch css-tree-fix
Branch names should be readable, and descriptive of the branch's goal. "css-tree-fix" not "my-branch-3"
This creates a ref which points to a particular commit hash. Namely whichever commit you were on when you created the branch.
Important: This command does not change you to your newly created branch. See git checkout
below
Important: We do not do work directly on master, use this command or git checkout -b <branch name>
explained below
git checkout <commit hash/ref(branch name/tag/etc...)>
e.g., git checkout ef32eo
or git checkout feat-branch
- If you want to make a new branch and move to it in one step use
git checkout -b <branch name>
This command cannot be made with changes in your working directory.
- If you do not want them anymore, run
git checkout -- .
to permanently discard these changes.- You can also do this with the reset command, see below
- If you do want them, commit them.
- If you do want them, but not that bad, run
git stash
git checkout can be used to pull a branch that is not available locally if it exists on a remote
git reset <commit hash/ref(branch name/tag/etc...)> <option>
This is used primarily with 3 settings
--soft
: Any differences between your current state and the ref you're moving to are retained. Staged changes remain staged.--mixed
(default): Any differences between your current state and the ref you're moving to are retained. Staged changes are unstaged.--hard
: The working directory is set to exactly model the ref in question, any uncommited/unstashed changes are lost
Situations where you may use git reset
:
- "I accidentally staged the password file!":
git reset HEAD ./passwords.txt
(please don't make a passwords file) - "I've made a bunch of changes but I don't want them anymore because the mocks changed! Please help":
git reset HEAD --hard
helpful resource for --soft vs. --mixed vs. --hard
git log
This is helpful if you're looking in the past.
I would recommend making an alias for this variant of git log:
git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
It presents more information, cleaner, and in less space.
git show <commit hash/ref(branch name/tag/etc...)>
git push <remote> <branch>
or git push <remote> <local branch>:<remote branch>
e.g., git push origin dev
or git push origin feature:feature
The latter command only needs to be done if you wish to push your local branch to a remote branch with a different name, otherwise just specifying the branch name is sufficient.
Pushing is one of the prime benefits of git. You can have copies of your work off of your local machine. In the event that you Blue Screen of Death, forget your laptop, or have any other computer-tragedy you can pull on a new machine without having lost anything!
Important: Do not "git push"! Always specify all parameters for push/pull or you may end up in git hell. You do not want to be there.
git pull --rebase <remote> <branch>
or git pull <remote> <remote branch>:<local branch>
e.g., git pull --rebase origin dev
or git pull origin develop:develop
The latter command only needs to be done if the branch does not already exist at the destination.
(--rebase
is an option which puts any changes that only exist locally after any remote changes. This prevents local history from becoming different from remote's history. If the workflow is followed properly this shouldn't be necessary, and I know that everyone will follow the workflow perfectly, right?)
Important: Do not "git pull"! Always specify all parameters for push/pull or you may end up in git hell. You do not want to be there.
git merge <branch>
merge the specified branch onto the branch you currently have checked out.
e.g.,
git checkout dev
git merge css-hack
We are not using merge in our workflow, this is just for reference. This is why we don't merge
git cherry-pick <commit>...
cherry-pick puts the specified commits onto the branch you currently have checked out.
git checkout big-feature
git cherry-pick ef23t9e e67e2e...
Puts commits ef23t9e and e67e2e onto branch big-feature. You may want to use this if there is a fix to a bug on a different branch to get that change/those changes onto your current working branch.
git rebase
: move a branch's root to a different place on the tree, 'rebase' it. Cherry-pick all nodes on a branch to the specified location
git rebase <upstream> <branch>
or git rebase <branch>
The first option is syntactic sugar for:
git checkout <upstream>
git rebase <branch>
git checkout <branch>
either use that syntax or be sure to explicitly checkout your rebase target first:
git checkout develop
git rebase big-feature
If you prefer the latter it is recommended that you have you command prompt display your current git branch if a git repo is present.
When a rebase is initiated you will use do the following steps:
- Resolve any merge conflicts
- Stage resolved files with
git add
- Use
git rebase --continue
to move to the next commit being rebased - Repeat until git tells you you're done
At any point you can type git rebase --abort
to pretend none of this ever happened and return your working directory to the state before you began the rebase.
Visual Studio has a good rebase tool.
This takes the same options as git rebase, however it will open a new window where you can choose to take/leave/squash commits. Follow the onscreen directions.
This can be used as a faster cherry-pick if you want to take most of the commits in a branch.
This is also how you squash.
git reflog
Shows all changes made to HEAD in reverse chronological order, each row is displayed as:
<commit hash AFTER THE ACTION ON THE RIGHT WAS TAKEN> HEAD@{#} <action description>
e.g.,
8cc679a HEAD@{0} checkout moving from 1628937e98u79a61287eu98eyp13 to master
620a698 HEAD@{1} commit: (add) .css file for trees
...
This can be used to undo destructive actions taken on your local branch, or to recover dangling commits that lack a tag/branch/etc...
Remember: The hash on the left references HEAD AFTER the action on the right was taken
-d
: directory. Also remove untracked directories
-n
: dry run. Run to see what git would remove without actually removing anything
-f
: force. This flag must be included to run the clean operation unless you've changed your config
e.g.,
git clean -dn
: show me what files and directories you will delete
git clean -df
: delete files and directories
Important: You should always run with the -n
flag once before you go about wildly deleting things
- Get latest from from origin develop
git checkout develop
git pull --rebase origin develop
- Create a feature branch for your work item
git checkout -b <branch name>
- Commit your work early and often. One commit per logical change.
git add
to stage your changesgit commit
to commit
- Repeat step 3. until work is complete.
git push origin <branch>
occasionally to keep a copy of your work backed up
- Re-pull latest onto develop
git checkout develop
git pull --rebase origin develop
- Rebase onto develop
git rebase develop <branch name>
(is is recommended to do this as you go to save yourself from a huge merge at the end of the line. Visual Studio has a good rebase tool). - Checkout your feature branch and push to origin:
git checkout <branch-name>
git push origin <local-branch-name>:<remote-branch-name>
e.g.,git push origin css-tree-fix:css-tree-fix
- If you have already pushed your branch once you can just do
git push origin <branch name>
- If you have already pushed your branch once you can just do
- Use Visual Studio Online to create a pull request
- You can add commits locally and push again in order to update your PR. No need to create a new code review
- Reviewers can see you notes on each commit and follow your progression.
- Provided you are rebased on the latest develop VSO will squash and merge for you automatically
git config --global alias.<alias-name> <command>
e.g., git config --global alias.co checkout
Or, modify the .gitconfig file directly:
.gitconfig:
[alias]
<alias-name> = <command>
co = checkout
See the docs (My bash command comments below are relevant here too)
I prefer to use bash commands because I don't have to preface my alias with "git". i.e., "git co" can instead be "gco"
.bash_profile:
gs () { git status; }
ga () { git add $1; }
gc () { git commit $1 $2; }
gd () { git diff $1; }
gb () { git branch $1 $2; }
gco () { git checkout $1 $2; }
gcob () { git checkout -b $1 $2; }
gh () { git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short; }
gpull () { git pull $1 $2 $3; }
gpush () { git push $1 $2 $3; }