- Before we start – a Git synopsis
- SETTING UP
- MANAGING BRANCHES
- MANAGING REMOTE REPOS
- MANAGING REMOTE BRANCHES
- Git GLOBAL SETTINGS
Before we start – a Git synopsis
As with anything, knowing even a little about the mechanism behind whatever tool you’re using can go a long way. Here I’ll cover a few key things then we can move onto the commands.
Note, you’ll probably need a rough working knowledge of Git to understand what I’m on about in this article.
The Three Trees
Working Tree / Working Directory: this is where you are working. When you edit a file you’re editing the ‘Working Tree’, these are the files that show up in SourceTree.
Index: the staging area, these are the files you ‘git add’ before committing. Those edited files that showed up in SourceTree, they’re in the ‘Working Tree’. when you select them to stage: you’ve moved them into Git’s ‘Index’. they’re not committed yet, they’re just ready to commit.
HEAD: the most recent commit, if you were to commit right now the current HEAD would be your new commit’s parent. In SourceTree, it’s the commit that’s in bold. While you move between branches and commits, it’s the HEAD that’s jumping around.
Note, if (when) you find yourself with a ‘detached HEAD’ that just means your head isn’t at the most recent commit of a branch. It’s not as scary as it sounds!
- By SHA1:
git show xxxx
Note, you need a min of 4 characters as long as they are unique.
- By Branch:
git show "branch-name"
- Current branch, latest commit:
git show HEAD
- Current branch, latest commit parent:
git show HEAD~or
git show HEAD^
Note, ‘~’ and ‘^’ can be appended to any commit selector.
- Current branch, latest commit grandparent:
git show HEAD~2
- Current branch, latest commit second parent:
git show HEAD^2
Note, ‘^2’ is only useful for merge commits as they have 2 parents. First parent is the branch merged into (eg Develop), second is the branch merged from (eg a Feature).
You can also use
git reflog to see your HEAD’s history – kind of like your browser history.
Ignoring files can be done in 3 places:
Ignore in this repo:
.gitignore file – here’s a few handy templates.
These rules will be passed along when the repo is cloned.
Ignore in this local repo:
From the repo root:
cd .gitinfo and open exclude, eg
start notepad exclude
These rules apply only in this repo on your machine, they do not get passed on.
Ignore in all local repos:
git config --global core.excludesfile ~/.gitignore_global then add some rules to it.
These rules apply globally across all repos on your machine.
And now we can begin.
This is about the easiest one! For existing repos:
git clone "url"
http urls are read only & anonymous (you can’t push your changes back).
ssh urls allow you to push changes back but require authentication.
For new repos:
Note, for websites, remember to make the site root at least one up from the repo root, give yourself a little room for some extra files.
Stage everything / add everything to the Index (so when you commit, all changes in your repo are committed):
git add --all
Stage everything except deleted files:
git add .
Note, this is the one I use, it adds the extra step (‘git add –all’) to commit deleted files but I feel this is a good thing.
Stage all changes within a folder:
git add "folder-name"
Stage all changes to a file:
git add "file-name"
Stage ‘chunks’ – bits of a file:
git add -p
y = stage current chunk
n = ignore current chunk
s = split chunk
e = edit chunk
q = exit
Unstage a file (you won’t lose changes):
git reset HEAD "file-name"
Note, doing a
git status after an add will remind you of this command.
Unstage all the files (again, you won’t loose anything):
git reset HEAD -- .
Save your staged changes to a commit:
git commit -m "commit message here"
Edit your most recent commit:
git reset --soft HEAD~
Note, this will effectively undo your last commit command – those changes will be back in your staging so don’t forget to commit again when you’re finished!
Edit your most recent commit message:
Without anything staged:
git commit --amend
Note, if run with staged changes they will be merged with the most recent commit (the one you’re amending) instead of creating a new one. So un-stage your work or stash it.
Reset a file you’ve worked on back to it’s state in a specified commit:
git checkout "commit" "file-name"
Undo a commit. There are two ways:
Revert – figures out the previous state (which you select) and creates a new commit, eg 1-2-3-1 (where 3 was reverted to 1)
git revert "commit"
Reset – Steps back a commit, only in order, you lose any work in a commit you reset. So the above example would go from 1-2-3 to just 1. Read about RESET in more detail here.
See the current status of your repo, probably the most used command:
git status – shows you what’s going on in the working directory and in staging
To see a log of the commits, if you’re keen on doing it on the command line, run this first:
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%C(bold blue)<%an>%Creset' --abbrev-commit"
credit where credit is due
Then you can use
git lg instead of
git log (go on, compare them!)
space = scroll
q = quit
If you’re on BitBucket, that has a good branch graph otherwise
gitk will launch the git repo browser.
See commits on a certain file:
git log "file-name"
Viewing an old commit:
git checkout -b "new-branch-name" "xxxx"
Note, checking out an old commit will move your HEAD away from a branch tip (detached HEAD!) It’s fine to do but remember to checkout a branch before committing anything. This is why I’m checking out the old commit into a new branch, it’s like a failsafe in case I forget.
Create a new branch:
git checkout -b "folder/branch-name"
Note, ‘/’ effectively creates a new folder or adds to an existing one, in this case ‘folder’.
Move between branches:
git checkout "folder/branch-name"
Note, if you’ve made any changes to your files (in your working directory) they’ll be carried with you when you move between branches.
Rename a branch:
git checkout "folder/wrong-branch-name"
git branch -m "folder/right-branch-name"
Merge from branch ‘a’ into branch ‘b’:
git checkout "a"
If ‘a’ is public:
git merge "b"
Get a list of branches:
Get a list of branches merged into ‘a’:
git checkout "a"
git branch --merged or if you don’t want to move onto ‘a’
git branch --merged "a"
--no-merged is also a thing.
Delete a merged branch:
git branch -d "folder/branch-name"
If you’ve got a lot of local branches that need to be tidied up:
git branch --merged master | grep -v "* master" | xargs -n 1 git branch -d
here’s a good explanation of that command
Delete a branch that hasn’t been merged:
git branch -D "folder/branch-name"
You may have noticed I’ve left out
git rebase. By rebasing we’re changing the ancestry of a commit which will change the commit itself. Not a good idea if that commit is public. Personally, I’m on the “lets not use it” side of that argument, but if you’re keen on rebasing your private branches – go for it! Just, try not to do it in public.
MANAGING REMOTE REPOS
Get a list of remotes:
git remote -v
Add a remote:
git remote add "remote-name" "url"
Rename a remote:
git remote rename "old-name" "new-name"
Change a remote URL:
git remote set-url origin "url"
remove a remote:
git remote rm "remote-name"
Tiny digression: recently I had had to pull some work from a BitBucket repo onto a server that was set up with someone else’s credentials (which I didn’t know). Then an a-ha moment! I created an extra remote refrence to the BitBucket repo with my details, easy! Although the server repo gradually crept ahead of our BitBucket which was interesting given that the ‘origin’ and ‘my-remote’ were the same repo. I ended up deleting ‘origin’ and renaming my remote reference to ‘origin’. That could have been an interesting situation if someone else had gone in and tried to update the server’s repo, granted it may have been a non-event but I didn’t fancy the risk! And if another member of the team goes in and finds they don’t know my credentials, well aren’t you glad you read this! Anyway, back to the commands.
MANAGING REMOTE BRANCHES
Get a list of remote branches:
git branch -r
Bringing a remote branch to local:
git fetch "remote-name" "branch-name" or just
git fetch to get everything
Pushing a branch to remote:
git checkout "a"
git push "remote-name" "a"
Cleaning up merged remote branches.
List merged branches on the remote:
git branch -r --merged
Note, if you’re on ‘develop’ locally, this will show branches merged into the remote branch that ‘develop’ is tracking
List branches merged into a specified branch (just like local but with ‘-r’, easy!)
git branch -r --merged "branch-name"
Delete remote branch:
git push origin :"branch-name-to-delete" : pushes an empty reference to remote branch
git push --delete "remote-name" "branch-name", use which ever you prefer.
Remove tracking (the ‘reference’ you have locally which now has no remote counterpart (it’s actually a full git object stored under “remote-name/branch-name” which is the tree that gets populated by fetch but I digress, again.) ):
git remote prune "remote-name" or
git remote update --prune
Note, if working in a team everyone will need to delete their local versions of the branches and prune otherwise branches will start reappearing.
If that seems scary:
git remote prune origin --dry-run
Cleaning up all merged remote branchs
git checkout develop && git branch -r --merged | grep -v master | sed -e 's/origin//:/' | xargs git push origin: uses the push empty reference technique, here’s the breakdown
Note, You’ll have to prune after this too
For more on this type of thing, have a read around
There are two types: Lightweight is just a title attached to a commit, Annotated is a full-on git object.
Create an Annotated tag:
git tag -a "Tag-name" -m "Tag message" "xxxx" -s
Note, without specifying a commit your tag will be added to the current HEAD, also ‘-s’ signs the tag
Create a Lightweight tag:
git tag "Tag-name" "xxxx"
Note, without specifying a commit, same as above – have you spotted the theme here yet?
See all tags:
Inspect a single tag:
git show "Tag-name"
Search tags with a pattern (-l):
git tag -l 'Tag-*'
Push tag to remote:
git push "remote-name" "tag-name"
Push all tags to remote:
git push "remote-name" --tags
Pull tag from remote
git fetch "Tag-name"
Pull all tags from remote:
git fetch --tags
Changing a Tag name:
Don’t – see the discussion section here for reasons.
Resolving an edit conflict (you and your friend have changed the same line of code). Git will add a ‘conflict block’ into the relevant file. Just replace the whole lot with your preferred change and commit.
<<<<<<< HEAD Your change ======= Your friends change >>>>>>> branch-you-are-merging-into
Resolving a delete conflict (you edited a file and your friend deleted it).
To keep it:
git add "file-name"
To confirm the deletion:
git rm "file-name"
Stash your Working Directory and Staging to your stack (of stashes):
See all stashes:
git stash list
Un-stash a stash
git stash apply "stash-name"
Note, use the stash name format given by ‘git stash list’, without the name the most recent stash will be applied. Also, you can bring a stash out onto a different branch and even while there are changes in your working directory – any conflicts will result in a normal Git conflict.
Remove a stash from your stack:
git stash drop
Note, applying a stash will not remove it from your stack so you’ll have to do this too.
Remove all the stashes from your stack of stashes:
git stash clear
Apply and drop a stash:
git stash "stash-name" pop
Branch from stash (usually if you’ve forgotten to apply something useful from a stash and the project has moved on):
git stash "stash-name" branch
Note, the branch is created from the stash’s origin commit and the stash is dropped from the stack.
Git GLOBAL SETTINGS
Set your commit name:
git config --global user.name "name"
Set your commit email:
git config --global user.email "email"
Set the editor for git commands:
git config --system core.editor "editor"
Making life a little easier – the
git lg thing from before is an alias. Here’s a few more useful ones.
From the git book:
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
To unstage a file:
git config --global alias.unstage 'reset HEAD --'
git unstage "file-name"
To see the last commit:
git config --global alias.last 'log -1 HEAD'
That’s it! Of course there’s more to Git than this, but I’ve yet to find myself needing the heavier stuff. As for Git’s context (the people, the projects, the philosophy) there’s a great list of resources maintained by GitHub. Speaking of which, if you have somehow managed to avoid contact with GitHub and their amazing community, go there now! Finally I would strongly recommend you get familiar with Git Flow, as we use a slightly modified version of it here at Delphic and for me it’s been an absolute dream to work with.