-
Understanding git
- Anatomy of git
- HEAD
- Think of it as the snapshot of your last commit
- Pointer to the current branch reference, which is the last commit you made or the last commit that was checked out
- Index
- Snapshot of your next commit
- Includes all of the files from your previous commit plus the changes you've staged
- Working directory
- These are the actual files on your filesystem
- HEAD
- Anatomy of git
-
Configuration
--version
- lists the version of the git install- Update from within git using:
git clone http://github.com/git/git.git
(run as sudo for to update for all users) - Config file comes in 3 flavors:
--global
- stored in ~/.gitconfig; specific to user
--system
- stored in /etc/gitconfig and applies to every user and all their repositories--local
- stored in .git/config within each repository directory and applies only to the repository
- Check the config file:
config --list
- Can also use
config {key}
, likeconfig user.name
to check a specific key - Set user name and email for recording commits
config --global user.name "Name"
config --global user.email "[email protected]"
- To use a different text editor (uses the default system editor, usually Vi or Vim:
config --global core.editor emacs
(will use the emacs editor) ornano
- To use a different diff tool:
config --global merge.tool vimdiff
(will use vimdiff) - Set up aliases
config --global alias.[alias-name] [git-command]
will setup git to trigger the specified command using the alias- Ex:
git config --global alias.co checkout
would trigger a checkout command whenget co
is typed - Ex:
git config --global alias.unstage 'reset HEAD --'
makes it easier to upstage a file - Ex:
git config --global alias.last 'log -1 HEAD'
allows you to just see the last commit - Note that if you want to trigger an external command with an alias, prefix it with
!
; Ex:git config --global alias.visual "!gitk"
- Enable autocompletion
git-completion.bash
file located in thecontrib/completion
directory in source code- To enable for current user
- Copy file to home directory
- Add "source ~/.git-completion.bash to your .bashrc file
- To enable for all users
- Copy file to /opt/local/etc/bash_completion.d directory
- Alternatively, create the following two files in your home directory
- .bash_profile - "source ~/.bashrc"
- .bashrc - "source /usr/local/git/contrib/completion/git-completion.bash"
- Press the tab key when writing a git command to see suggestions
- Fix permission issues
- Set get to ignore file permission changes:
git config core.filemode false
- Set get to ignore file permission changes:
- diff/difftool
git config --global color.ui true
to enable colors in diff output- http://git-scm.com/book/en/Customizing-Git-Git-Configuration
- http://www.andymcintosh.com/?p=33
- Prevent mergetool from keeping .orig backups (doesn't work with all mergetools):
git config --global mergetool.keepBackup false
- Help for any git command help
<verb>
,<verb> --help
, orgit-<verb>
-
Creating Repositories and Managing Files
init
- Use init by itself within an existing directory to convert that directory into a git repository
- Create a new repository in a new directory with
init directoryName
add *
oradd [filename]
- For untracked files: adds to the repository to be tracked by git
- For tracked files: stages file to be included in the next commit
- Note that adding a file stages it in the state its in when it's added. If you modify the file further, those changes won't be staged until you add it again
add .
oradd folder/
adds the current directory and everything under it (but doesn't handle deleted files)- Wildcard searches can be done with
\\*
(need to escape the * character) add -u
adds all file changes for files that are already tracked, including deleted filesadd -A
does bothadd .
andadd -u
, when you want to add new files and remove deleted files
status
- tells which branch you are on and status of files (tracked, changed, staged, etc)diff
- Tells what you've changed but not yet staged
diff --staged
(or --cached) - tells what you've staged buy haven't yet committed
a.
rm [filename]- Stages the file for removal; at the next commit, the file will no longer be tracked
- If you simply remove the file without the git command, it will show up as changed but not updated (aka untagged)
-f
is used to remove a modified file already added to the index (this overrides the safety feature that prevents you removing a file that hasn't been recorded in the repository yet)--cached
will remove the file from being tracked, but keep it in the directory (useful if you forgot to add something to .gitignore- file-glob patterns like
git rm log/\\*.log
- removes all files with the .log extension in the log/ directorygit rm /*~
- removes all files that end with ~
mv [oldfilename][newfilename]
- Used to rename a file, but not really needed, because git will figure out that you renamed files
- Equivalent to running these 3 commands
mv oldfilename newfilename
git rm newfilename
git add newfilename
reset [filename]
unstages a filecheckout -- [filename]
will revert the file to its state at the previous commit (and all changes are GONE FOREVER)stash
- stores changes you don't yet want to commit- Stores stashes in a stack, with the most recent on top
--keep-index
will only stash changes that have not been added to the index withgit add
stash list
will list all stashes madestash apply
applies the most recent stashstash apply stash${n}
applies the specified stash- Stashes are not deleted when applied
- Stashes can be applied on a different branch than where the they were made
- Stashes can be applied on a branch with uncommitted changes
- Use
--index
withstash apply
if your stash contains changes to a file that is already staged and you want the stashed changes to be staged as well
stash drop [stash-name]
to delete a stashstash pop [stash-name]
to apply and delete a stash at the same time- Un-apply a stash:
git stash show-p [stash-name] | git apply -R
(assumes the most recent stash if you don't supply a stash name) - Create a new branch from a stash:
git stash branch [branch-name]
- Creates a new branch, checks out the commit you were on when you created the stash, reapplies the stash there, and drops the stash if it applies successfully
- Then you can merge the branch back into your main working branch when you're ready
- Create a stash-unapply command:
git config --global alias.stash-unapply '!git stash show -p | git apply -R'
- Stores stashes in a stack, with the most recent on top
- .
gitignore
file contains rules for excluding files from the repository- Blank lines or lines starting with # are ignored within the file
- Standard glob patterns work
*
matches zero or more characters[abc]
matches any character inside the brackets?
matches a single character[0-9]
matches any character within this range- Use a
\
to escape special characters (like#
and!
)
- Paths
- End patterns with
/
to specify a directory *
within a path will not match any directory, only the final filename- A leading
/
matches the beginning of the path name/*.html
matchesindex.html
but not/home/index.html
- A leading
**/
means match an in any directory - Trailing
/*
means match anything within a directory foo/**/bar
means match 0 or more directories insidefoo
that containbar
- End patterns with
- Negate patterns by starting it with
!
update-index --assume-unchanged
- Ignore uncommitted changes in a file that is already tracked
git ls-files
lists all of the files currently being tracked in a repo
-
Remotes
clone
- create a working copy of a repository- Automatically adds the remote repository as
origin
- From local to local:
clone /path
- From remote
clone git://url/file.git
(uses the git protocol; can also use http or https)clone user@server:/path/file.git
will use ssh- Folder name: by default the folder name is the same as the cloned repository; you can also specify a folder name after the path to the file
- Automatically adds the remote repository as
remote
lists the shortnames of each remote handle you've specifiedorigin
is the default name of a server you've cloned- ssh remotes are the only kind you can push to
-v
shows the URL associated with each shortname
remote add [remote-name] [url]
- Will assign a shortname to the repository at the specified url
- Then use the shortname to reference the url with any git command
[shortname]/master
will access the master branchremote add origin <server>
- connect existing repository with remote server and set that server as origin
remote show [remote-name]
- Lists the URL for the remote repository and tracking branch information and all remote references that have been pulled
- Shows which branch is automatically pushed when a push is executed, "new remote branches" (branches on the remote you don't have locally yet), "stale tracking branches" (remote branches you have that have been removed from the remote server), and tracked remote branches (that are automatically merged when pull is executed)
remote rename [filename] [newfilename]
renames a remote referenceremote rm
removes a remote referenceremote update
fetches from all remote repositories- Remote tracking branches
branch -r
lists all remote tracking branches-a
lists all branches
fetch [shortname or url]
- Gets all of the of the information from a remote repository and puts it in its "remote tracking" branches called
[remote-name]/[branch-name]
- Does not merge it or modify any of your work--this has to be done manually
--all
gets updates from all remotes- Ex:
fetch origin
fetches any new work that has been pushed to that server since you cloned or last fetched from it
- Gets all of the of the information from a remote repository and puts it in its "remote tracking" branches called
- Merge into current branch using
merge
[remote-name]/[branch-name]
- Peek at remote branches (without working on them)
- Check them out directly:
git checkout origin/dev
- Check them out directly:
- Create new local branches from remote tracking branches
checkout --track [new-local-branch-name] [remote-name]/[branch-name]
(you can leave out local branch name if you want to use the same branch name as the remote)
pull
- Generally fetches data from the server you originally cloned from and tries to merge it into the code you're currently working on
- In general, you don't want to do this (use
fetch
andmerge
instead) --all
will download all remote branches- Pull and overwrite local, untracked files
git fetch --all
to fetch the remote changesgit reset --hard origin/master
reset local files to status of remote
push [remote-name] [branch-name]
- send changes to remote repositorypush origin master
pushes your local master branch to the original server you cloned it from- Only works if you have write access to the server, and it hasn't been modified since you cloned/pulled it last
- You have to pull the changes to incorporate them before you push, if you don't have the most recent version of the repository
push [remote-name] [branch-name]:[remote-branch-name]
will specified a different name for the remote branchgit push [remotename] :[branch]
will delete a remote branch from the server-u
will set as the default remote tracking branch (upstream) repository-f
will force updates (like when removing commits that have already been pushed)
- PUSH directly to a website
- Create a "bare" repository on the server, in a different directory than the web root:
git init --bare
- Create a file called
post-update
in the hooks folder of this new repository with the following line:GIT_WORK_TREE=/path/public_html git checkout -f
(this should be the entire path to the web root, like public_html) - Give yourself execute permissions on this file:
chmod +x /path/website.git/hooks/post-update
- On the local machine, add a new remote server:
git remote add [shortname] [[email protected]:/path/server/website.git]
- Use this as the initial push command:
git push website +master:refs/heads/master
- Then you can use the normal push command to update the site:
push [server-name] [branch-name]
- Create a "bare" repository on the server, in a different directory than the web root:
-
Github
- When forking a repository, create a new remote called upstream that links to the original repository so you can update it.
-
Managing Commits
commit
- creates a current snapshot of the repository as it is- Opens a text file with a summary of the commit
-m
is for including a message (in quotes) after the command that identifies the commit-v
will push the output of diff to the text file-a
will skip the staging area and automatically stage every file that is already tracked before executing the commit--amend
will add changes to previous commit- If you don't specify a new commit message with -m, you'll be given the previous commit message
- Will create a conflict with commits that have already been pushed to a remote host
log
- Default command lists the commits made in that repository in reverse chronological order
- Note the difference between "author" (the person who wrote the patch) and "committer" (the person who executed the commit command)
-p
shows the diff introduced in each commit--stat
shows abbreviated statistics for each commit--shortstat
displays only the changed/insertions/deletions line from--stat
--pretty=[format]
changes the format of the log outputoneline
prints each commit on 1 lineshort
,full
, andfuller
provide increasing levels of informationformat:
lets you specify your own formatting
--graph
shows an ASCII graph with your branch and merge history--name-only
shows the list of files modified fate the commit information--name-status
shows the list of files affected with added/modified/deleted information as well- -
-abbrev-commit
shows only the first few characters of the SHA-1 checksum --relative date
- display the date in a relative format- Limiting log output
-2
limits the output to the last two entries- -
-since=
and --after= get the commit history within a specified timeline, like--since=2.weeks
or--until="2012-08-15"
--until=
and--before=
do the same but opposite of the above--author=
specifies a specific author--committer=
specifies the person who executed the commit--grep=
lets you search for keywords- use
--allmatch
if you use both--author
and--grep
commands for your query; otherwise all items that match each of your queries will be retrieved - Specify a directory of filename by adding it last, following
--
gitk
is a gui tool to visualize log searches (type gitk to activate it
- Undoing changes - be careful, you can't always undo (revert) your undos!
commit --amend
will add whatever is in your staging area to your most recent commit (uses the same commit message as the original, unless you change it)
reset [options] [hash/tag] [path/files]
reverts to a previous commit- Points the head to the previous commit
- Options
--soft
keeps the state of the files the same, so you can see the differences (like undoing commits without changing the files)--mixed
also updates the index from the specified commit, as if your files were not yet staged for the next commit (this is used if nothing is specified)--hard
resets all files to the status of the preivous commit
- Specify a path to effect only specific files or directories
- Squash commits
- Reset to the first commit you want to keep:
reset --soft HEAD~2
(moves back 2 commits) or specify a hash - Run
git commit
- Reset to the first commit you want to keep:
reset [hash/tag]
file will reset the index (staging area) for that file, but not change the version in the working directory (until you commit, of course!)
revert [hash]
- reverts to a previous commit- Will bring up an editor to revise your commit message (quit out of it if you don't want to change this message)
blame [path/filename]
- Tells you who is responsible for a certain section of code
-L[start],[end]
will only output specific lines
-
Tagging Files
- Info
- Often used for marking release points
- Two kinds
- Lightweight - a simple pointer to a specific commit
- Annotated - full objects that have their own checksum, tagger name, tagger email, date, message, and can be signed and verified
- Are not pushed by default
tag
lists the current tags in alphabetical order-l '[search]'
lists tags that have a particular format; Extag -l 'v1.4.*'
will list all of the tags that begin with "1.4"
tag [name]-lw
creates a lightweight tagtag -a [name]
creates an annotated tag- -
m '[message]'
creates a message; otherwise git opens the editor once a tag is created
- -
show [tagname]
will show information about a tag- Signed tags
tag -s [name]
creates a new annotated tag signed with GPG (assuming you have a private key)tag -v [tag-name]
uses GPG to verify the tag signature (you need the signers public key in your keyring
- Create a tag for a previous commit
- Add part (or all) of the checksum at the end
- Ex:
tag -a v1.2 8fceb02
- To push tags (just like sharing remote branches)
push [remote-name] [tag-name]
would push the specified tag to the named remote server; Ex:git push origin v1.4
- Use
--tags
option on the push command to push all of your tags; Ex:git push origin --tags
- Info
-
Branches
- HEAD is what keeps track of which branch you're on; the default branch is "master"
branch
- Lists the current branches, with a
*
to indicate the current branch you're on -v
lists the most recent commit with each branch--merged
shows which branches are already merged into the branch you're on (these you can usually delete because you've already merged the code)--no-merged
shows what branches that contain work you haven't merged
- Lists the current branches, with a
branch [branch-name]
- Creates a new branch
-d
deletes a branch-D
deletes a branch that hasn't yet been merged-u [remote-branch] [local-branch]
will set the specified local branch to track the specified remote branch (if no local branch is specified, the current branch is used)
checkout [branch-name]
- Moves the head to another branch
- If your working directory or staging area has uncommitted changes that conflict with the branch you're checking out, you can't switch branches
-b
creates a new branch and switches to it-a
will list all branches, including those from remote repositories- Tracking branch
- Use
-b
to create a local working copy of a remote branch you have fetched:git checkout -b [new-local-branch-name] [remote-name]/[branch-name]
--track
is shorthand for this; Ex:git checkout --track [remote-name]/[branch-name]
will create a new local branch with the same name as the remote branch- Tracking branches can then be pushed and pulled (when checked out) without specifying server information, because git remembers where they originally came from
- Delete a remote branch from the server:
git push [remotename] :[branch]
- Use
merge [branch-name]
- Merges a branch into the branch you specify
- If the working branch is just an addition commit of the branch merging-to branch (that hasn't been changed since the working branch was created), it just moves the pointer of the merged-to branch
- Use
merge --no-ff [branch-name]
to prevent a fast-forward merge, if you want to maintain the history of the branch --ff-only
will abort the merge if not able to do a clean fast foward merge
- Use
- If the merged-to branch has been changed, merges by recursive, creating a special "merge" commit that has more than one parent
- You can merge the master into your working branch, if you need to (from the working branch):
git merge master
- Conflicts
- Merge is paused if there are conflicts
- See unmerged files in the
status
- To manually resolve a conflict
- Remove everything between and including
<<<<<<<
and>>>>>>>
and replace it with new code add
to stage the file (which marks it resolved)- Check that all conflicts are resolved and added with
status
commit
and specify what you did in the commit message
- Remove everything between and including
mergetool
starts a visual merge tool to walk through conflictsclean
will recursively remove files that are not under version control-f
to force clean (required ifclean.requireForce
is not set to false)-x
will also remove ignored files (specified under.gitignore
; normally these files are not cleaned)-d
remove untracked directories in addition to files-n
or --dry-run-X
remove only files ignored by git
- Remote Branches
- References to the state of branches on your remote repositories
- Take the form [remote]/[branch]
- Never updated automatically--you must push the branches you want to share (this way, you can keep your code private until it's ready to share
- Rebasing
- Take all changes that were committed on one branch and replay them on another branch
- Essentially takes the branch and makes it the next commit in the branch you're rebasing to
- You can then do a fast-forward merge, because both commits are in the same stream
- Has the advantage that it leaves a clean history; good for contributing to a project that you don't maintain, because it will just add another commit to the main branch
rebase [branch-to-rebase]
(from within the target branch to which you are rebasing)rebase [branch-to-rebase] [target-branch]
will take the branch to rebase and rebase it into the target branch without checking out the target branch (you are then switched to the target branch)--onto
rebases a branch over top of another one, if the working branch and rebase-to branch aren't directly connected- DO NOT rebase commits that you have pushed to a public repository--instead use it as a way to clean up work with commits before you push them
- Interactive rebase - squashing commits
-i HEAD~n
where n is the number of commits to squash- You can then edit the rebase process to combine or skip commits as needed
- Use mergetool to fix any merge conflicts while rebasing
--continue
will continue the rebase once merge conflicts are fixed--skip
will skip the current commit that has an error--abort
will abort the rebase and return to the most recent commit
- Take all changes that were committed on one branch and replay them on another branch
cherry-pick [commit]
- add a specific commit- Ex:
cherry-pick master [commit-hash]
would add that commit from master to the current branch
- Ex:
-
Subtree
subtree split
breaks off a subfolder, extracts the relevent commits, and allows it to become it's own repo- Ex:
git subtree split --prefix=web --onto deploy -b deploy
takes the web folder and puts it in its own branch called deploy--prefix=
selects the folder--onto
selects an existing subtree to apply the split (you don't need this the first time you run this command)-b
declares which branch to use, and creates it if it doesn't exist
- Ex:
-
Submodules
- Usage
- Used for putting additional repositories within an existing repository
- Submodules should be self-sufficient repositories of code
- Good way to include dependencies for your project
- IMPORTANT: submodules are tracked separately; if you change files in a submodule, they are not tracked by the parent project! You must commit changes them with each submodule independently
- The "superproject" only keeps track of the status of a submodule by its current commit, when a commit is made to the superproject
- git push does NOT push a submodules code; push the changes from the submodule first
- Best practices for editing submodules
- Don't edit submodules themselves; instead edit them as separate projects and pull them into the superproject
- If you do edit submodules directly
- They will be overwritten with submodule update
- Create a new branch within them first,
git checkout -b [branch-name]
so you can find your work in that branch later, if you need to
- Adding submodules
submodule add [source] [path/name]
- The submodule will be cloned in the specified directory (sub submodules of this submodule will be empty directories)
submodule init
adds .gitmodule info to .git/config file
- Managing submodules
submodule status
submodule summary
compares submodules current state vs commited state- Merging branches
- Whenever you merge in a branch, submodule updates are not included
- Submodule updates must be done separately (see below)
- Updating submodules
submodule update
- Updated submodules are "headless", meaning they don't have a current branch
- Switch to submodule directory
git checkout master
(or create a new branch)
pull
(from submodule directory)- Make a commit in the superproject to save the state of all submodules. If submodules updates are not committed, they are lost
- Removing submodules
- Remove submodule reference from
.gitmodules
- Remove submodule reference from
.git/config
- Remove the created path for the submodule:
git rm --cached [path]
(no trailer slash)
- Remove submodule reference from
- Cloning with submodules
clone
(submodule directories will initially be empty)submodule init
to initialize the submodulessubmodule update
to pull in the contents from the submodules- Shortcut
clone [source] --recursive
to include submodules (and their submodules) in a clonesubmodule update --init --recursive
if you forgot to do this on the initial clone
- Branches and submodules
- When switching to a new branch without a submodule, the submodule directory remains, untracked
- If you move or remove it, you'll have to clone it back when you switch back to the original branch
- When switching to a new branch without a submodule, the submodule directory remains, untracked
- Convert subdirectory to submodule
- Move the subdirectory out of the superproject into its own project space
- Use
git filter-branch
to extract repository history just for this folder, if needed
- Use
- Rename the submodule-to-be directory if needed, to be more descriptive
- Create a new upstream hosting for the submodule (like GitHub)
- Initial new project
git init
and push to remote, if needed - Add new git module to superproject
- Commit and push the super project
- If you switch back to a branch that doesn't have the submodule but still has the directory, you need to move the directory out of the way first, so it doesn't get overwritten by the subdirectory from the branch
- Move the subdirectory out of the superproject into its own project space
- Usage
-
Debugging
bisect
to find the exact commit where a bug appearedbisect start
to get goingbisect bad
to mark the current commit that has the bugbisect good [commit]
to name a past commit where you know things worked- The bisect session will start
- Test the bug at each stopping point and enter
bisect good
orbisect bad
accordingly - Git will tell you the first commit where the bug appeared
bisect reset
to return to where you started
-
Configuring a git server
- Server setup
- Install git on the server
- Create a git user
- Add a ssh key for each person who will be able to access the git user
- Change the shell for the git user in
/etc/passwd
togit-shell
(runwhich git-shell
to determine where it's located) - Create a directory in the git user's home directory called
git-shell-commands
- Gitolite
-
Install
-
Create a git user (as above, but maintain regular shell access and don't add keys) and login
-
~/.ssh/authorized_keys
needs to be empty or non-existent -
Upload your ssh public key to
$HOME/YourName.pub
-
Run the following commands
git clone git://github.com/sitaramc/gitolite mkdir -p $HOME/bin gitolite/install -to $HOME/bin source .profile gitolite setup -pk YourName.pub
-
-
Add users
-
Do this using the special repository,
gitolite-admin
-
git clone git@host:gitolite-admin
on your local machine -
Keys - add users' public keyfiles (
[user].pub
) in thekeydir
-
Repos
-
Edit the file
conf/gitolite.conf
repo foo RW+ = alice - master = bob - refs/tags/v[0-9] = bob RW = bob RW refs/tags/v[0-9] = carol R = dave
-
Commit the changes and push to your git server
-
-
Permissions
R
readW
write+
allows deleting refs-
to deny access
-
Groups
@staff = alice bob carol @interns = ashok repo secret RW = @staff repo foss RW+ = @staff RW = @interns
-
@all
-
Can be used as an alias for all users or as an alias for all repos
repo @all R = @all
-
-
-
- Server setup
Created
April 17, 2014 23:25
-
-
Save creativecoder/11016995 to your computer and use it in GitHub Desktop.
git version control notes
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment