Contents
- Theory
- Configuring
- Using Git on the CLI
- Initialize a Project
- Add extra remote hosts
- Clone A Project
- Commit to a Project
- Check Status
- Pull (Sync) A Repository
- Fetch A Repository
- Merge
- Other Commands To Know
- Branches
- Inspect Commit Logs
- Revert a Commit
- Deleting a commit
- Finding commits relative to a particular file
- Finding a string in a repository (grep)
- Finding a string in a commit/log
- Example CLI Workflow
- GUIs
- Errors
- One-off scenarios
- See Also
Theory
- The first time you work on a project, you will download that project's repository code to your machine by doing a clone.
- To update the code on your machine from the repository, you perform a pull.
- A checkout is how you switch from one branch of a project to another.
- Changes are commited on your machine.
- To submit your changes to the repository, you push them back.
- You merge data from one branch to another - to combine/diff them into one.
See Also
Configuring
Configuring Basic git Options
Most IDEs or git GUIs will handle this step
Configure user:
git config --global user.name "David Pocock" git config --global user.email "dpocock@dayid.org" git config --global core.editor vim git config --global merge.tool vimdiff git config --global merge.conflictstyle diff3 git config --global mergetool.prompt false git config --global diff.tool vimdiff git config --global color.interactive auto git config --global color.diff auto git config --global color.status auto git config --global color.branch auto git config --global color.ui true
Perhaps cache our password for an hour: (although preferable would be to use ssh-keys)
git config credential.helper cache git config credential.helper 'cache --timeout=3600'
Alias 'lol' to give slightly better logs:
git config --global alias.lol "log --pretty=oneline --abbrev-commit --graph --decorate"
Or just create ~/.gitconfig
[user] name = David Pocock email = dpocock@dayid.org [core] editor = vim [merge] tool = vimdiff [color] status = auto branch = auto diff = auto interactive = auto
Verify config:
git config --list
Configuring git to sign commits with gnupg
Add to .gitconfig:
[commit] gpgsign = true [user] signingkey = ______________________ [log] showSignature = true
You may need to also specify [gpg] program = if your gpg program is not in your user's $PATH
It is also wise to then modify your ~/.gnupg/gpg.conf to point "default-key" to the key you'd like to use for signing and enable "use-agent".
You may also need to modify your login files to set/export GPG_TTY.
Once those are all working you will be prompted to unlock your GPG private key when you do a commit so the commit can be signed.
Using Git on the CLI
Initialize a Project
mkdir project cd project git init echo "This is my new project" > README git add README git commit -m 'Initialized project'
Add extra remote hosts
Add another remote host to a project
cd /home/git/myproject.git git remote add hostname user@host:/remote/path/
e.g.,
cd /home/dpocock/git/nagios-plugins git remote add bedrock dpocock@bedrock:~/git/nagios-plugins.git
Clone A Project
Cloning a project to your own machine:
git clone youruser@remote:/path/to/project.git
Commit to a Project
To update a file in a project:
cd /path/to/myproject/ vim file git add file git commit git push
- If you modified many files, see also "git add ." or "git add -A"
- If you only want to add some-line-changes, see also "git add -p"
- You may also specify the commit message using:
git commit -m 'This is my commit message'
Again; this time using shortcuts:
git commit -a git push
Again; this time using long formatting:
git commit -am 'ISSUE-31 I fixed this' git push origin MYBRANCH
Amending a Commit
If you did your commit wrong the first time, you may need to amend it.
Simply:
git commit --amend
Stashing Changes
If you've been working on some changes, but aren't ready to commit them yet, then you may stash them for now (saving your current state on a branch without committing it), and apply those stashed changes later.
git stash Saved working directory and index state \ "WIP on master: 049d078 added the index file" HEAD is now at 049d078 added the index file (To restore them type "git stash apply")
See available stashes
git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051 Revert "added file_size" stash@{2}: WIP on master: 21d80a5 added number to log
Apply a stashed-state
git stash apply
For a particular older state:
git stash apply stash@{2}
You may also stash into a new branch if you've already been doing work (say you've been working on master accidentally, but would like your changes to be in the branch testchanges):
git stash branch testchanges
Drop (remove) a stash by id:
git stash drop stash@{0}
When dropping, the index number {#} of the available stashes (shown from 'stash list') will re-index to 0. Thus, be careful as repeat 'git stash drop stash@{0}' will continue dropping each next-stash that fills index-place 0.
Partially Stash Changes
You can also choose to stash only some files/portions of files you've been working on:
git stash -p
Resetting changes
Say you've checked out master and made changes, then realized you wanted to make those changes under a different branch:
- Revert changes to modified files.
git reset --hard
- Remove all untracked files and directories.
git clean -fd
Ignoring files
The best way is to create a .gitignore file within the project.
Some examples: (Note, **/ matches a dir, not just */)
comment filename filepattern* partial*pattern /filename dir/filename
See also: http://git-scm.com/docs/gitignore
Check Status
See the status of what you have changed:
git status -s
I'm personally fond of using the --porcelain option to git status to involve in hooks/scripting:
git status --porcelain
Also - to see what has already changed from past logs:
git whatchanged git whatchanged -p
Pull (Sync) A Repository
This essentially does a fetch with an automatic merge
git pull
Fetch A Repository
git fetch
Merge
Switch to the branch you want to merge to:
git checkout test
Merge the other code/branch
git merge dev
Other Commands To Know
Other:
git commit git rm file git mv file newfile git log git commit -am 'Comment for update here' git push origin master git log --full-history -- 'file'
Branches
Verify the Current Branch
You will know which branch you are commiting to if you do a regular git commit because it will show in the comments: "# On branch branchname".
To verify which branch you are on use git branch. If your terminal supports colors it will highlight which you are currently on: either way it will place an asterisk to show which you are on.
[dpocock@hummus test]$ git branch feature * master
Verify upstream/tracking for Current Branch
Your local branch may be tracking a different name upstream - to verify use git branch -vv. This will report your local branch name as well as the remote/remote-branch it is tracking:
git branch -vv * local_master e2e6424 [origin/production] Merge branch 'master' into production
Checkout/Switch Branches
Doing a code checkout is how you'll switch between different branches of the same code:
git checkout master Switched to branch 'master' git checkout dev Switched to branch 'dev'
Create a Branch
Branching a project presumes you already have a project created (whether it is a new project, a clone'd project, etc). First, make sure to do a checkout of the branch you'd like to branch.
To create a new branch based on master, then switch to (checkout) it:
git checkout master git branch newbranch git checkout newbranch
Shorthand for this is available with checkout -b, which creates a branch and does a checkout of it at the same time:
git checkout master git checkout -b newbranch
Diff a Branch
To see changes to a file:
git diff file
To do a diff between two different branches:
git diff master otherbranch
To do a diff between the branch you are on and another branch:
git diff otherbranch
With a specific file in mind:
git diff branch1 branch2 filename
Also useful: 'R'everse the diff using -r to show the +/- opposite
git diff -r filename
Merge a Branch
See also the git-merge manpage.
Checkout the branch you want to merge to:
git checkout master Switched to branch 'master'
Do a merge with the other branch:
git merge dev Updating c8aa593..07dec67 Fast-forward test.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
Rebase
See also the git-rebase manpage.
git rebase -i upstream branch
So, I've done 20 commits of "Trying to fix X" - and now I finally - fixed X on my current branch. I want to merge it back into another branch; but do not want 20 backmessages of nothingness.
git rebase -i HEAD~X # where X is the amount of back-commits I want to work from
- This launches my editor, where I'll change the commits I don't want anymore to say "squash" instead of "pick" and write-out
- This will re-base and open a commit screen with the X number of commits that I am combining (squashing).
Rebase to condense commits
git init echo '1' > 1 git add 1 git commit -m '1' echo '2' > 2 git add 2 git commit -m '2' git echo '3' >> 2 git add 2 git commit -m '3' git echo '4' >> 2 git add 2 git commit -m '4' git log # verify how-many commits back you want to rebase/condense git rebase -i HEAD~3
Opens in your editor:
pick 105a640 2 pick f05e996 3 pick 7a2f1e4 4
See the useful tooltips git provide:
# Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
Here, I want to keep all of the commits, but make it just one big commit rather than all my sequentials. So change the lines to:
pick 105a640 2 squash f05e996 3 squash 7a2f1e4 4
This gives the opportunity to clarify the commit messages:
# This is a combination of 3 commits. # The first commit's message is: 2 # This is the 2nd commit message: 3 # This is the 3rd commit message: 4
Change this
Added file b and now have a useful commit message summarizing what all of my smaller commits accomplished.
Inspect the 'git log' to see that your commits have been condensed.
Reset an in-progress rebase
If you accidentally choose the wrong amount of HEAD~# to go back or otherwise choose to not continue a rebase, quit out and rm -rf .git/rebase-merge
Delete a Branch
Delete a branch by being in a different branch and doing: git branch -d branch
You cannot delete a branch if (a) You're currently on it (b) It has not been merged
If that branch hasn't been merged
git branch -d bug415fix error: The branch 'bug415fix' is not fully merged. If you are sure you want to delete it, run 'git branch -D bug415fix'.
If you're on that branch:
git branch -d bug415fix error: Cannot delete the branch 'bug415fix' which you are currently on.
If you want to delete a branch which has not been merged, use -D
Delete a Remote Branch
git push origin :branch
or - the style I find clearer:
git push origin --delete branch
See also: http://www.gitguys.com/topics/adding-and-removing-remote-branches/
Recover a Deleted (local) Branch
I've erroneously deleted a local branch before pushing it (for a myriad of reasons). Since a branch name is simply a pointer to a ref/shasum though, it can be recovered. First: Find the shasum you want using reflog. This is multitudes easier if you more-recently deleted the branch or remember the name of the removed branch (or a portion of it).
git reflog
Now, using the shasum, recreate the branch from that refpoint - just as you would create a branch from any other refpoint:
git checkout -b 'branch-name' <shasum>
Inspect Commit Logs
Short version:
git log --oneline
More verbose:
git log
With stats:
git log --stat
From a particular user:
git log --author=Author
With diffs:
git log -p
Only if they affected a particular path/file:
git log -- path
Search for a string in commit message/diff::
git log -Sstring # yes, the lack of space here is correct
Do the same, but create diffs for each:
git log -p -Sstring # again, lack of space is correct
Do the same, but only for a particular file:
git log -p -Sstring file
Search for a regular expression in commit message/diff:
git log -G'regex'
Do the same, but only for particular lines of a particular file:
git log -L 1,1:file
Similar: Finding who last-modified a line of a file and in what commit:
git blame
Revert a Commit
For any various reasons you may wish to revert back to an older commit-point. Say you hired a new developer who wrote a bunch of commits to a project all willy-nilly like, and you've now sacked them and would like to get back to your old, usable code.
First, you'll want to Inspect Commit Logs, ultimately to find the commit-hash of the last/prior good-commit (one not done by A. Fictitious Hire):
commit c8aa593f35b48c3837247992431f4cb4764d80a2 Author: A. Fictitious Hire <ahire@dayid.org> Date: Fri Apr 25 12:25:28 2014 -0400 Rewriting all this code in csh because csh has C commit 6b7e945c78c6e72084423b3a8d750c39a1a89e63 Author: A. Fictitious Hire <ahire@dayid.org> Date: Fri Apr 25 12:24:18 2014 -0400 Removed all comments from code commit 07dec67a9c784534f8af9ab3f66864acf8b72a58 Author: David Pocock <dpocock@dayid.org> Date: Fri Apr 20 14:01:04 2014 -0400 Wrote massive application to do all of support's work for them automatically
Do a "revert" for those last two bad-commits:
git revert c8aa593f35b48c3837247992431f4cb4764d80a2 git revert 6b7e945c78c6e72084423b3a8d750c39a1a89e63
Each of these actually does a commit that reverts the commit-hash that you provide.
Skipping back many Commits
What if A. Fictitious Hire did more than just 2 commits? In that case, we'd like to "go back" to a prior-point in time when things were still good - perhaps without having to do 20 "git reverts" to get there:
For this example, we'll want to checkout David's last commit:
user@server:~/git/thisproject/ $ git checkout 07dec67a9c784534f8af9ab3f66864acf8b72a58
You'll get a nice warning screen about being in 'detached HEAD' state. If you try to add/commit right now you'll receive an error that you are "Not currently on any branch".
If you want to just pull all the files from that commit in to your current branch:
git checkout master git checkout 38f1b9d2e651b9498299c7dafaffd5cd84c683b6 *
Deleting a commit
Say you did something silly like putting the wrong Jira Issue-ID into your commit-message. Thus, associating a change to the nagios-configs branch with another project's issue. Whoops!
If you *have* made changes, copy those out somewhere safe for the moment:
cp ~/git/project/myfile ~/myfile.safe
Check git log for the commit that you want to be on:
git log
Get that commit's hash and reset to it:
git reset --hard 55bc0dce9d67f1bb65bb91a336d0758117ac6b4c
Force send that (if you already pushed):
git push origin HEAD --force
Put your changes back and do the commit right this time:
cp ~/myfile.safe ~/git/project/myfile git add myfile git commit -m 'ISSUE-#### added this feature' git push
Finding commits relative to a particular file
git log filename
git log --all -- filename
e.g.,
git log --all -- databugverifonecardsummary.php
This will show all commits relevant to a particular filename. This is useful for seeing the history of a single file rather than commits for the project as a whole.
Finding a string in a repository (grep)
This is very similar to using the system's grep - I have not found it to be any faster nor slower when doing quick tests on small repositories. This returns any file (and the contents of the matching-line) with your string.
git grep 'string'
Finding a string in a commit/log
Using git log with -G allows you to specify a regex to search within commit-contents/diffs
git log -G'hostname.*10.1.1.5$'
Example CLI Workflow
Normal Development Flow
For the most part, projects for developers will already exist everywhere that they need to. Their workflow will mostly use checkout, add, and commit; with the occasional branch and merge.
- Go to where that project's parent is:
cd /var/www/htdocs/base/bin/incab
- Make sure you're on the branch you want to branch from:
git checkout master
- Verify that branch has the newest code from the origin:
git pull
- Branch off your new branch to develop on:
git checkout -b INC-32dev
- Edit whatever is being fixed/improved/created:
vim dispatch/index.php
- Add that file's changes to git:
git add dispatch/index.php
- Commit the changes with comment
git commit -m 'INC-32 Fixed issue with prepending mileage'
- Push that branch to origin:
git push origin INC-32dev
GUIs
There are many out there, see this.
- gitk ships with git, so it's what I use when I need a GUI.
- SourceTree is what most people I know currently use. It is from Atlassian and nicely ties in with Stash and Jira, and it is free (even for business use).
Errors
fatal: remote origin already exists.
You can't remote add origin if one already exists - this one is pretty simple.
Here, a project already had a remote defined, but I needed to change it. So set-url instead
[dpocock@hummus nagios-plugins]$ git remote add origin dpocock@git:/home/git/nagios-plugins.git fatal: remote origin already exists. [dpocock@hummus nagios-plugins]$ git remote -v origin git@hummus:~/nagios-plugins.git (fetch) origin git@hummus:~/nagios-plugins.git (push) [dpocock@hummus nagios-plugins]$ git remote set-url origin dpocock@git:/home/git/nagios-plugins.git [dpocock@hummus nagios-plugins]$ git remote -v origin dpocock@git:/home/git/nagios-plugins.git (fetch) origin dpocock@git:/home/git/nagios-plugins.git (push)
Deleting a commit
Typically will not be done, but in the event of erroneous commits, refer to this post from StackOverflow.
(pre-receive hook declined)
A pre-receive hook on the destination is refusing to allow your commit.
Check pre-receive hooks on the destination.
You will most likely need to amend your commit.
CONFLICT (content): Merge conflict
A good recommendation to avoid merge conflicts is to continuously pull (or fetch and merge) against your parent branch - particularly before pushing up your own branch!
I created a conflict with a file named file. I did this by branch master to dev, and also branching master to test. I then edited file in both branches before doing a commit. Then, while on the test branch, I did:
git merge dev Auto-merging file CONFLICT (content): Merge conflict in file Automatic merge failed; fix conflicts and then commit the result.
Anatomy of a merge conflict
- Imagine that "master" file has the content of: 1
- I branch "dev" off of "master" and file has a content of: 1
I change the content of file on the branch "dev" to be "2" (and commit my changes to the "dev" branch)
You can think of this change being processed as (very basically):
In file file, change "1" to "2" on line 1
- I branch "test" off of "master" and file has a content of: 1 (because that is still the content on the "master" branch)
I change the content of file on the branch "test" to be "7" (and commit my changes to the "test" branch)
You can think of this change being processed as (very basically):
In file file, change "1" to "7" on line 1
- If I checkout branch "test" and attempt a git merge dev, then git is attempting to apply the delta-differences of:
In file file, change "1" to "7" on line 1
However, on the branch "dev", the file file doesn't have a "1" on line 1 to be changed!
Since what is being looked for does not exist, git cannot change it: Now you have a merge conflict.
Resolving the merge conflict
To resolve a conflict (I used to prefer 'meld' but now mostly just use vim):
git mergetool
-OR-
edit the file in conflict manually: Normally, search for <<< and >>> and choose which section should be correct and delete/remove the other
Once the conflict is resolved: commit or continue the merge.
git commit -m 'Resolved conflict'
git merge --continue # my preferred for most scenarios
- http://stackoverflow.com/questions/161813/fix-merge-conflicts-in-git/7589612#7589612
- http://www.rosipov.com/blog/use-vimdiff-as-git-mergetool/
- https://help.github.com/articles/resolving-a-merge-conflict-from-the-command-line
Windows
Case Insensitivity
Not git's fault, but Windows filesystems are normally not case sensitive. It will not recognize that "THISFILE" and "thisfile" are unique files. This may mean that particular Windows clients may tell you that you have uncommitted changes from the initial time that you checkout a project. Check on the server side to see if there are any files with a similar (case-INsensitive) name. This should not happen, but has already been found "in the wild" here.
Bad files paths
If when cloning/fetching a project you have errors that the file may not be created, check the name of the file. In a project I worked on before, someone had named a file "IF" (including the quotes): Windows could not handle this.
Windows also could not handle a file named: c:\test.xls (as in: ~/git/project/c:\test.xls as a filename on a Linux system). Please use sane names for things.
Linux
gnome SSH_ASKPASS
You're trying to do a git push/pull/clone/whatever and get an error with X.
I'd bet if you do the following you'll get back some gnome BS:
echo $SSH_ASKPASS
Use unset to remove this crap
unset $SSH_ASKPASS
One-off scenarios
Remove a whole bunch of wasteful branches
git fetch --all git pull --all git branch -a > FILE
edit FILE to show only branches you want to delete
while read BRANCH; do git push origin --delete $BRANCH; done < FILE
See Also
Documentation
- http://git-scm.com/book/en/Git-Basics-Recording-Changes-to-the-Repository
- http://kovshenin.com/2011/howto-remote-shared-git-repository/
- http://git-scm.com/book/en/Git-Branching-Basic-Branching-and-Merging
- http://krisjordan.com/essays/setting-up-push-to-deploy-with-git
- Atlassian: Basic git commands
- http://git-scm.com/book/
- http://gitref.org/inspect/
- http://stackoverflow.com/questions/15736564/why-after-merge-does-git-say-already-upto-date-but-differences-between-branch
- https://jwiegley.github.io/git-from-the-bottom-up/
- http://www.rosipov.com/blog/use-vimdiff-as-git-mergetool/
- http://www.wei-wang.com/ExplainGitWithD3/
- http://pcottle.github.io/learnGitBranching/