Difference between revisions of "Working with git"
(→Find out what changes were made to a remote repository:) |
(add instructions on how to have git cache credentials) |
||
(66 intermediate revisions by 6 users not shown) | |||
Line 14: | Line 14: | ||
==Check out a repository:== | ==Check out a repository:== | ||
git clone <remote-repo-url> <local-repo-name> | git clone <remote-repo-url> <local-repo-name> | ||
− | git clone | + | For example |
+ | git clone git://carpetcode.org/McLachlan McLachlan | ||
− | ==Find out what changes were made to a remote repository:== | + | ==Find out what changes were made to a remote repository since I last pulled:== |
− | + | git fetch | |
+ | git log HEAD..origin | ||
==Update from a repository, if there are no local uncommitted changes:== | ==Update from a repository, if there are no local uncommitted changes:== | ||
+ | git pull --rebase | ||
+ | Hint: If you find you always want to rebase when you pull, then make it the default behavior by setting the autosetuprebase option | ||
+ | git config --global branch.autosetuprebase always | ||
+ | Now whenever you run | ||
git pull | git pull | ||
− | + | it will do a rebase too. | |
==Update from a repository, if there are local changes:== | ==Update from a repository, if there are local changes:== | ||
Save all local changes: | Save all local changes: | ||
git stash | git stash | ||
− | Then update as described above | + | Then update as described above: |
+ | git pull --rebase | ||
Re-apply all local changes: | Re-apply all local changes: | ||
git stash pop | git stash pop | ||
Line 32: | Line 39: | ||
If there are conflicts, handle them, then: | If there are conflicts, handle them, then: | ||
git stash drop | git stash drop | ||
− | since "stash pop" doesn't pop the stash if there are conflicts. "git stash list" shows all stashes. | + | since "stash pop" doesn't pop the stash if there are conflicts. "git stash list" shows all stashes. View the content of a stash with "git stash show <stash>". |
==Push to a repository:== | ==Push to a repository:== | ||
− | Ensure all remote changes have been pulled | + | Ensure all remote changes have been pulled (see above), then: |
git push | git push | ||
==Create a branch containing the current snapshop of the local repository, including all uncommitted changes, and switch to this branch== | ==Create a branch containing the current snapshop of the local repository, including all uncommitted changes, and switch to this branch== | ||
− | + | ||
+ | Create a new branch pointing to the current commit: | ||
+ | |||
+ | git branch <newbranchname> | ||
+ | |||
+ | Check out the new branch: | ||
+ | |||
+ | git checkout <newbranchname> | ||
+ | |||
+ | These can be combined into a single command: | ||
+ | |||
+ | git checkout -b <newbranchname> | ||
==List all branches== | ==List all branches== | ||
+ | git branch -a | ||
+ | |||
+ | If you also want to see the relationship to remote branches, use very verbose mode | ||
+ | |||
+ | git branch -vva | ||
+ | |||
+ | ==Find out which branch I am on:== | ||
git branch | git branch | ||
− | + | The branch marked with a star "*" is the current branch. | |
==Switch to a branch, if there are no uncommitted changes== | ==Switch to a branch, if there are no uncommitted changes== | ||
git checkout <branch-name> | git checkout <branch-name> | ||
+ | |||
+ | If the branch name happens to be the same as a file name, you can explicitly specify that you're referring to a branch: | ||
+ | |||
+ | git checkout <branch-name> -- | ||
=Committing= | =Committing= | ||
==Commit a change:== | ==Commit a change:== | ||
− | |||
− | |||
− | == | + | Commit certain files: |
− | + | ||
+ | git commit <list-of-files-to-commit> | ||
+ | |||
+ | Commit all modified files which are already under version control: | ||
+ | |||
+ | git commit -a | ||
+ | |||
+ | ==Modify a commit that hasn't propagated yet, keeping the working tree the same:== | ||
+ | |||
+ | git commit --amend <files> | ||
+ | |||
+ | This is the same as "git commit", except it rewrites the last commit instead of creating a new one. <files> lists any files with new changes that you want to include. You can also say "-a" to mean all registered files. | ||
+ | |||
+ | ==Undo a commit that hasn't propagated yet, keeping the working tree the same:== | ||
+ | |||
+ | git reset HEAD~1 | ||
+ | |||
+ | This deletes the last commit but leaves the changes from it in the working tree. HEAD~1 means one commit before the latest. | ||
− | ==Revert a commit | + | ==Revert a commit (i.e. create another commit), dropping all changes that it made:== |
− | |||
− | + | git revert <commit> | |
− | |||
+ | If your working tree is not clean, do a "git stash" before and a "git stash pop" after. | ||
+ | ==Revert a commit (i.e. create another commit) that may have propagated already, keeping all the changes as local modifications:== | ||
+ | git show <commit> | git apply -R | ||
=Making local changes= | =Making local changes= | ||
+ | |||
+ | ==Look at local modifications:== | ||
+ | List modified files: | ||
+ | git status | ||
+ | Get a diff between the local repo and the working tree ("which changes did I make since the last commit?"): | ||
+ | git diff HEAD [<filename>] | ||
+ | Get a diff between the local repo and the staging area ("which changes did I mark for committing?"): | ||
+ | git diff --staged [<filename>] | ||
+ | Get a diff between the staging area and the working tree ("which of my changes would not be committed?"): | ||
+ | git diff [<filename>] | ||
+ | Get a diff between the remote repository and working tree ("what would a 'git pull' do?"): | ||
+ | git diff origin [<filename>] | ||
==Revert an uncommitted change to a file:== | ==Revert an uncommitted change to a file:== | ||
+ | git checkout <file> | ||
+ | If there is also a branch with the same name as the file, then we can specify explicitly that we mean the file. | ||
git checkout -- <file> | git checkout -- <file> | ||
==Add a file to the repository:== | ==Add a file to the repository:== | ||
− | + | First create file locally, then: | |
git add <list-of-files-to-add> | git add <list-of-files-to-add> | ||
− | + | git commit | |
==Remove a file from the repository:== | ==Remove a file from the repository:== | ||
− | + | Remove the files from the working tree and mark them as removed in the staging area, then commit the staging area: | |
− | git rm < | + | git rm <files> |
− | + | git commit | |
− | |||
==Undo adding a file to the repository:== | ==Undo adding a file to the repository:== | ||
− | |||
git reset HEAD <file> | git reset HEAD <file> | ||
+ | This leaves the file in place, but removes it from version control. | ||
==Undo removing a file from the repository, recreating the file manually:== | ==Undo removing a file from the repository, recreating the file manually:== | ||
− | + | Recreate file locally, then: | |
git reset HEAD <file> | git reset HEAD <file> | ||
==Undo removing a file from the repository, resurrecting the file in the repository:== | ==Undo removing a file from the repository, resurrecting the file in the repository:== | ||
− | ?? | + | git reset HEAD <file> |
+ | git checkout <file> | ||
+ | |||
+ | =Useful hints= | ||
+ | |||
+ | ==Aliases== | ||
+ | Aliases are a handy way to simplify the commands needed for your own common tasks. For example, you could add a "what's upstream" command to easily find out which commits will be pulled | ||
+ | git config --global alias.wu '!sh -c "git fetch && git log origin..HEAD"' | ||
+ | From now on, just run | ||
+ | git wu | ||
+ | and it will list the commits which would be merged in by a pull. Similarly, it can be useful to have a "what's downstream" command to know which commits would be pushed: | ||
+ | git config --global alias.wd "log origin..HEAD" | ||
+ | Another useful example is if you often find yourself wanting to do the stash-pull-pop sequence, you could create an alias for that: | ||
+ | git config --global alias.sp '!sh -c "git stash && git pull --rebase && git stash pop"' | ||
+ | |||
+ | ==Easy rebasing with mergetool== | ||
+ | Rebasing is mostly painless with git. The only problem arises when there is a conflicting commit and you have to manually intervene. In this case, the mergetool command can be invaluable: | ||
+ | git mergetool | ||
+ | This will present a diff utility (opendiff on Mac OSX) which allows you to easily select which changes you want. Once you have finished, save and quit, then run | ||
+ | git rebase --continue | ||
+ | If you want more manual control, edit the conflicting files manually and get them how you want them. Make sure to add them: | ||
+ | git add <conflicting-files> | ||
+ | and then continue the rebase: | ||
+ | git rebase --continue | ||
+ | |||
+ | ==Remove files unknown to git== | ||
+ | Sometimes you just want to get rid of all files which aren't under version control. For this use the clean command. First, run | ||
+ | git clean -n | ||
+ | to do a dry-run and see what will be deleted. If you're happy with this change, run | ||
+ | git clean -f | ||
+ | to actually get rid of the files. | ||
+ | |||
+ | ==Oops, I didn't mean to do that== | ||
+ | Don't worry if you accidentally do an operation which you didn't intend to do. It can usually be recovered. For this, the reflog command can be very useful. It essentially lists all recent operations. For example, say you accidentally deleted the last commit using reset and now you realise that you actually do want it after all. Simply run | ||
+ | git reflog | ||
+ | and find the SHA1 of the commit you want in the list, then cherry-pick that commit | ||
+ | git cherry pick <commit-sha1> | ||
+ | |||
+ | ==Aargh, I made a total mess== | ||
+ | It can happen that you get your repository into a hopeless mess and just want to get it back to its original state, disregarding all local changes. This can be achieved using the -f option to checkout | ||
+ | git checkout -f HEAD | ||
+ | This will throw away all local changes and will bring your repository back to the state it would be in if you freshly checked it out (with the exception of files which aren't under version control). | ||
+ | |||
+ | ==Saving credentials in your keyring== | ||
+ | Git will ask for credentials when it needs to authenticate itself against a server to push/pull changes. By default it will ask for credentials each time it needs access which can be cumbersome. The usual solution is to use ssh public key based access and add the key to the keyring managed by ssh-agent (or equivalent). For https based access this does not work and one can instead use git's credendtial helpers introudced in version 1.7.9 ([https://www.kernel.org/pub/software/scm/git/docs/gitcredentials.html man page] or [https://confluence.atlassian.com/display/STASH/Permanently+authenticating+with+Git+repositories bitbucket tutorial]). A simple approach that mimics what svn does is to cache the credentials in memory for a while: | ||
+ | git config --global credential.helper cache | ||
+ | git config --global credential.helper 'cache --timeout=3600' | ||
+ | A more comprehensive approach would use [https://stackoverflow.com/questions/13385690/how-to-use-git-with-gnome-keyring-integration gnome-keyring] (source code only, included in git 1.8.0) or [http://www.sixfeetup.com/blog/git-credential-helpers osxkeychain] or an encrypted [https://stackoverflow.com/questions/6031214/git-how-to-use-netrc-file-on-windows-to-save-user-and-password .netrc] file. | ||
+ | |||
+ | =Workflows= | ||
+ | |||
+ | These workflows are designed to be self-contained. git is a complicated program, and the documentation can be confusing. The aim of these sections is that if you stick to the commands and techniques used in each workflow, you should not get into a mess or need to consult any other git documentation. | ||
+ | |||
+ | ==SVN style== | ||
+ | |||
+ | ===Checking out=== | ||
+ | |||
+ | Clone the repository (like "svn checkout") | ||
+ | |||
+ | git clone <url> | ||
+ | |||
+ | Work on your new feature or bug fix. | ||
+ | |||
+ | ===Updating=== | ||
+ | |||
+ | Every so often, you will want to make sure you are working against the latest version of the code. This is accomplished by | ||
+ | |||
+ | git pull | ||
+ | |||
+ | If you get an error about a file not being "up to date", this means that you have made changes to a file which has also been changed in the remote repository. You need to "stash" your changes away in a safe place, then do the update, then apply your changes to the new version: | ||
+ | |||
+ | git stash | ||
+ | git pull | ||
+ | git stash pop | ||
+ | |||
+ | This is equivalent to "svn update". If all is well, your changes will be applied to the working copy and dropped from the stash. If there are conflicts, these will be marked with conflict markers in the source files just like with SVN. Your changes will also be kept in the stash for safe keeping, rather than being dropped. You will need to fix these conflicts. Once you have fixed all the conflicts, use "git reset" to tell git that you have fixed the conflicts. To be tidy, you can now drop your changes from the stash with "git stash drop". | ||
+ | |||
+ | ===What have I done?=== | ||
+ | |||
+ | To see what changes you have made in your local copy, use | ||
+ | |||
+ | git diff HEAD | ||
+ | |||
+ | ===Reverting local changes=== | ||
+ | |||
+ | If you want to revert one or more files to its clean state before you started editing it, use | ||
+ | |||
+ | git checkout <file1> <file2> ... | ||
+ | |||
+ | ===Status=== | ||
+ | |||
+ | If you want to see what files have been modified, added or removed, use | ||
+ | |||
+ | git status | ||
+ | |||
+ | ===Adding and removing files=== | ||
+ | |||
+ | If you need to add a new file to the repository, use | ||
+ | |||
+ | git add -N <filename> | ||
+ | |||
+ | (like svn add) for the file you have created. (The -N stops the content of the file being added to the "staging area", which would make "git diff" give confusing results. Don't worry about this.) If you need to remove a file, use | ||
+ | |||
+ | git rm <filename> | ||
+ | |||
+ | (like svn rm). | ||
+ | |||
+ | ===Committing=== | ||
+ | |||
+ | Eventually you will have your working directory in the state that you would like to commit. Ensure that you are up-to-date as described above. The next stage depends on whether you want to push your commit directly to the repository (if you have commit rights) or if you want to send your work as a patch. | ||
+ | |||
+ | ====Direct commit==== | ||
+ | |||
+ | Make a local commit: | ||
+ | |||
+ | git commit -a | ||
+ | |||
+ | This will prompt you for a commit message. The "-a" means "all registered files". This will make a local commit in your repository. Push this commit to the central repository: | ||
+ | |||
+ | git push | ||
+ | |||
+ | These two commands together are equivalent to "svn commit". | ||
+ | |||
+ | Your work is now in the central repository, and you are done. | ||
+ | |||
+ | ====Submitting a patch file==== | ||
+ | |||
+ | Sometimes you don't have commit access to the repository, or you want to have your work reviewed by someone else before you commit it. You can package up your changes into a "patch file" with | ||
+ | |||
+ | git diff > ~/Uploads/MyWork.patch | ||
+ | |||
+ | This file can be emailed or added to an issue-tracker. | ||
+ | |||
+ | Suppose you need to modify your work. Edit the working-copy files to incorporate the modifications, and then generate a new patch file as above. Repeat until the patch is final. | ||
+ | |||
+ | If you are responsible for committing the accepted patch, you can use the procedure in "Direct commit" above. If someone else has committed it, you don't need your local changes any more, and you can erase them using | ||
+ | |||
+ | git checkout | ||
+ | |||
+ | You can then update from the remote repository with | ||
+ | |||
+ | git pull | ||
+ | |||
+ | and you are done. | ||
+ | |||
+ | ==Git style== | ||
+ | |||
+ | This style is useful if you want to keep multiple changes organised locally. | ||
+ | |||
+ | Clone the repository: | ||
+ | |||
+ | git clone <url> | ||
+ | |||
+ | Work on your new feature or bug fix. If you need to add a new file to the repository, use | ||
+ | |||
+ | git add -N <filename> | ||
+ | |||
+ | for the file you have created. (The -N tells git just to register an empty file as added to the repository. If you omit it, the entire file will be put in the staging area, which might not be what you want if you only want to commit part of the file). | ||
+ | |||
+ | If you need to remove a file, use | ||
+ | |||
+ | git rm <filename> | ||
+ | |||
+ | Suppose you now want to bundle up ''some'' of your work into a single unit. This is achieved by making a "local commit". If there are no other changes to the files that contain the work you want to commit, you can make a commit in one step using | ||
+ | |||
+ | git commit <file1><file2> ... | ||
+ | |||
+ | You will be prompted to enter a commit message to describe this change. You can type "git log" to see all the commits, and your new commit will be at the top of the list. If your work is in files which have other changes which you don't want to commit just yet, then you can interactively select which parts of the files to commit using a two-stage process. First you will "add" parts of the files to the "staging area", then you will commit the staging area: | ||
+ | |||
+ | For files which need to be added in their entirety: | ||
+ | |||
+ | git add <file1> <file2> ... | ||
+ | |||
+ | For files which need certain parts only: | ||
+ | |||
+ | git add -p <file1><file2> ... | ||
+ | |||
+ | This will ask you, for each individual change in each file, whether you want to stage that "hunk" or not. You can answer "yes", "no", "all" or "done" by entering the first letter of each. Type "?" for help on the other options. Quitting the interactive "git add" will leave all the hunks you have staged staged. If you want to unstage a hunk, you can use | ||
+ | |||
+ | git reset <file1> <file2> ... | ||
+ | |||
+ | to unstage the content of entire files, or | ||
+ | |||
+ | git reset -p <file1> <file2> ... | ||
+ | |||
+ | to select hunks to unstage. This gives you the same interface as "git add -p", but lets you unstage rather than stage each hunk. | ||
+ | |||
+ | You can see all the hunks which are currently staged using | ||
+ | |||
+ | git diff --staged | ||
+ | |||
+ | Once you are happy with what is staged, you can commit it using | ||
+ | |||
+ | git commit | ||
+ | |||
+ | You will be prompted for a commit message. Once your changes are committed, you can see them using "git log". | ||
+ | |||
+ | You can commit as many changes locally as you like. Nothing has yet been sent to a remote repository. | ||
+ | |||
+ | Every so often, you will want to make sure you are working against the latest version of the code. This is accomplished by | ||
+ | |||
+ | git stash | ||
+ | git pull --rebase | ||
+ | git stash pop | ||
+ | |||
+ | The "git pull --rebase" will unapply all your local commits, update your local repository with commits from the remote repository, and then attempt to re-apply your commits. This process might generate conflicts. These will be indicated in the output of "git pull --rebase". You can edit the files to fix the conflicts. When you have finished fixing the conflict, you need to mark the files as fixed using | ||
+ | |||
+ | git add <file1> <file2> ... | ||
+ | |||
+ | and then | ||
+ | |||
+ | git rebase --continue | ||
+ | |||
+ | to move on to the next commit. If at some point you decide to abandon the rebase, you can use | ||
+ | |||
+ | git rebase --abort | ||
+ | |||
+ | This will take you back to your last commit, i.e. to before the "git pull --rebase" command. | ||
+ | |||
+ | Once the "git pull --rebase" command has finished, and you have fixed any conflicts, you will use the "git stash pop" command as above. This will restore any local uncommitted changes. Again you might have conflicts which you need to fix in your working copy. | ||
+ | |||
+ | |||
+ | Eventually you will have a set of commits at the end of your history (at the beginning of the output of "git log") that you would like to push. Ensure that you are up-to-date as described above. The next stage depends on whether you want to push your commits directly to the repository (if you have commit rights) or if you want to send your work as a series of patches. | ||
+ | |||
+ | ===Direct commit=== | ||
+ | |||
+ | If you want to push the patches directly to the repository, you can use | ||
+ | |||
+ | git push | ||
+ | |||
+ | This will push all your local unpushed commits to the repository that you cloned from. If you need to push to a different repository (for example if you cloned from a read-only git:// repository and you need to push to an ssh git@server:/repo style repository), you can add the URL of the repository: | ||
+ | |||
+ | git push <url> | ||
+ | |||
+ | If you get an error about a "non-fastforward" push, this means that you were not up-to-date when you tried to push. Use the "git pull --rebase" command as described above to make yourself up-to-date and try again. | ||
+ | |||
+ | Your work is now in the central repository, and you are done. | ||
+ | |||
+ | ===Submitting a series of patches === | ||
+ | |||
+ | Sometimes you don't have commit access to the repository, or you want to have your work reviewed by someone else before you commit it. You can package up all your local commits which don't exist in the remote repository (refered to as "origin") with | ||
+ | |||
+ | git format-patch -o ~/patches origin | ||
+ | |||
+ | This will make a series of numbered patch files named after the subject of each commit (the first line in the commit message) in the ~/patches directory. These files can be emailed or added to an issue-tracker. | ||
+ | |||
+ | ===Editing your commits=== | ||
+ | |||
+ | Suppose you have several local commits, but you decide you need to make changes before pushing or making patches. One option is to make the changes you need and then make new commits with these changes. You can then either push the entire set of commits or make new patch files with "git format-patch" as above. | ||
+ | |||
+ | Git allows you to modify your existing local commits. If you only need to modify the last commit, you can just make your changes to your files and use | ||
+ | |||
+ | git commit --amend <file1> <file2> ... | ||
+ | |||
+ | This will modify the last commit to incorporate your new changes. | ||
+ | |||
+ | If you have to edit a commit which is not the last one, you can use | ||
+ | |||
+ | git rebase -i <commit> | ||
+ | |||
+ | WORK IN PROGRESS HERE | ||
+ | |||
+ | In the editor window which appears, change "pick" to "edit", save the file and exit the editor. Make your changes to the working tree, then do | ||
+ | |||
+ | git rebase --continue |
Latest revision as of 11:08, 2 November 2014
Contents
- 1 Interacting with a remote repository
- 1.1 Check out a repository:
- 1.2 Find out what changes were made to a remote repository since I last pulled:
- 1.3 Update from a repository, if there are no local uncommitted changes:
- 1.4 Update from a repository, if there are local changes:
- 1.5 Push to a repository:
- 1.6 Create a branch containing the current snapshop of the local repository, including all uncommitted changes, and switch to this branch
- 1.7 List all branches
- 1.8 Find out which branch I am on:
- 1.9 Switch to a branch, if there are no uncommitted changes
- 2 Committing
- 2.1 Commit a change:
- 2.2 Modify a commit that hasn't propagated yet, keeping the working tree the same:
- 2.3 Undo a commit that hasn't propagated yet, keeping the working tree the same:
- 2.4 Revert a commit (i.e. create another commit), dropping all changes that it made:
- 2.5 Revert a commit (i.e. create another commit) that may have propagated already, keeping all the changes as local modifications:
- 3 Making local changes
- 3.1 Look at local modifications:
- 3.2 Revert an uncommitted change to a file:
- 3.3 Add a file to the repository:
- 3.4 Remove a file from the repository:
- 3.5 Undo adding a file to the repository:
- 3.6 Undo removing a file from the repository, recreating the file manually:
- 3.7 Undo removing a file from the repository, resurrecting the file in the repository:
- 4 Useful hints
- 5 Workflows
Interacting with a remote repository
This page describes solution to typical needs with git. If you are new to git, please read any of the fine tutorials to learn about the concepts that git offers, and how one would typically work with git.
Later, if you get stuck, or if you maneuvered yourself into a corner, you can come back here to find solutions to particular problems. That is, this page is not organised by "how do I do thing that are natural for git", but rather "how do I map things I need to do onto actions that are natural for git".
This page is split into three parts:
- Interacting with a remote repository (pull, push, merge)
- Local commits
- Working (adding/removing files, undoing changes)
Check out a repository:
git clone <remote-repo-url> <local-repo-name>
For example
git clone git://carpetcode.org/McLachlan McLachlan
Find out what changes were made to a remote repository since I last pulled:
git fetch git log HEAD..origin
Update from a repository, if there are no local uncommitted changes:
git pull --rebase
Hint: If you find you always want to rebase when you pull, then make it the default behavior by setting the autosetuprebase option
git config --global branch.autosetuprebase always
Now whenever you run
git pull
it will do a rebase too.
Update from a repository, if there are local changes:
Save all local changes:
git stash
Then update as described above:
git pull --rebase
Re-apply all local changes:
git stash pop
If there are no conflicts, you are done. If there are conflicts, handle them, then:
git stash drop
since "stash pop" doesn't pop the stash if there are conflicts. "git stash list" shows all stashes. View the content of a stash with "git stash show <stash>".
Push to a repository:
Ensure all remote changes have been pulled (see above), then:
git push
Create a branch containing the current snapshop of the local repository, including all uncommitted changes, and switch to this branch
Create a new branch pointing to the current commit:
git branch <newbranchname>
Check out the new branch:
git checkout <newbranchname>
These can be combined into a single command:
git checkout -b <newbranchname>
List all branches
git branch -a
If you also want to see the relationship to remote branches, use very verbose mode
git branch -vva
Find out which branch I am on:
git branch
The branch marked with a star "*" is the current branch.
Switch to a branch, if there are no uncommitted changes
git checkout <branch-name>
If the branch name happens to be the same as a file name, you can explicitly specify that you're referring to a branch:
git checkout <branch-name> --
Committing
Commit a change:
Commit certain files:
git commit <list-of-files-to-commit>
Commit all modified files which are already under version control:
git commit -a
Modify a commit that hasn't propagated yet, keeping the working tree the same:
git commit --amend <files>
This is the same as "git commit", except it rewrites the last commit instead of creating a new one. <files> lists any files with new changes that you want to include. You can also say "-a" to mean all registered files.
Undo a commit that hasn't propagated yet, keeping the working tree the same:
git reset HEAD~1
This deletes the last commit but leaves the changes from it in the working tree. HEAD~1 means one commit before the latest.
Revert a commit (i.e. create another commit), dropping all changes that it made:
git revert <commit>
If your working tree is not clean, do a "git stash" before and a "git stash pop" after.
Revert a commit (i.e. create another commit) that may have propagated already, keeping all the changes as local modifications:
git show <commit> | git apply -R
Making local changes
Look at local modifications:
List modified files:
git status
Get a diff between the local repo and the working tree ("which changes did I make since the last commit?"):
git diff HEAD [<filename>]
Get a diff between the local repo and the staging area ("which changes did I mark for committing?"):
git diff --staged [<filename>]
Get a diff between the staging area and the working tree ("which of my changes would not be committed?"):
git diff [<filename>]
Get a diff between the remote repository and working tree ("what would a 'git pull' do?"):
git diff origin [<filename>]
Revert an uncommitted change to a file:
git checkout <file>
If there is also a branch with the same name as the file, then we can specify explicitly that we mean the file.
git checkout -- <file>
Add a file to the repository:
First create file locally, then:
git add <list-of-files-to-add> git commit
Remove a file from the repository:
Remove the files from the working tree and mark them as removed in the staging area, then commit the staging area:
git rm <files> git commit
Undo adding a file to the repository:
git reset HEAD <file>
This leaves the file in place, but removes it from version control.
Undo removing a file from the repository, recreating the file manually:
Recreate file locally, then:
git reset HEAD <file>
Undo removing a file from the repository, resurrecting the file in the repository:
git reset HEAD <file> git checkout <file>
Useful hints
Aliases
Aliases are a handy way to simplify the commands needed for your own common tasks. For example, you could add a "what's upstream" command to easily find out which commits will be pulled
git config --global alias.wu '!sh -c "git fetch && git log origin..HEAD"'
From now on, just run
git wu
and it will list the commits which would be merged in by a pull. Similarly, it can be useful to have a "what's downstream" command to know which commits would be pushed:
git config --global alias.wd "log origin..HEAD"
Another useful example is if you often find yourself wanting to do the stash-pull-pop sequence, you could create an alias for that:
git config --global alias.sp '!sh -c "git stash && git pull --rebase && git stash pop"'
Easy rebasing with mergetool
Rebasing is mostly painless with git. The only problem arises when there is a conflicting commit and you have to manually intervene. In this case, the mergetool command can be invaluable:
git mergetool
This will present a diff utility (opendiff on Mac OSX) which allows you to easily select which changes you want. Once you have finished, save and quit, then run
git rebase --continue
If you want more manual control, edit the conflicting files manually and get them how you want them. Make sure to add them:
git add <conflicting-files>
and then continue the rebase:
git rebase --continue
Remove files unknown to git
Sometimes you just want to get rid of all files which aren't under version control. For this use the clean command. First, run
git clean -n
to do a dry-run and see what will be deleted. If you're happy with this change, run
git clean -f
to actually get rid of the files.
Oops, I didn't mean to do that
Don't worry if you accidentally do an operation which you didn't intend to do. It can usually be recovered. For this, the reflog command can be very useful. It essentially lists all recent operations. For example, say you accidentally deleted the last commit using reset and now you realise that you actually do want it after all. Simply run
git reflog
and find the SHA1 of the commit you want in the list, then cherry-pick that commit
git cherry pick <commit-sha1>
Aargh, I made a total mess
It can happen that you get your repository into a hopeless mess and just want to get it back to its original state, disregarding all local changes. This can be achieved using the -f option to checkout
git checkout -f HEAD
This will throw away all local changes and will bring your repository back to the state it would be in if you freshly checked it out (with the exception of files which aren't under version control).
Saving credentials in your keyring
Git will ask for credentials when it needs to authenticate itself against a server to push/pull changes. By default it will ask for credentials each time it needs access which can be cumbersome. The usual solution is to use ssh public key based access and add the key to the keyring managed by ssh-agent (or equivalent). For https based access this does not work and one can instead use git's credendtial helpers introudced in version 1.7.9 (man page or bitbucket tutorial). A simple approach that mimics what svn does is to cache the credentials in memory for a while:
git config --global credential.helper cache git config --global credential.helper 'cache --timeout=3600'
A more comprehensive approach would use gnome-keyring (source code only, included in git 1.8.0) or osxkeychain or an encrypted .netrc file.
Workflows
These workflows are designed to be self-contained. git is a complicated program, and the documentation can be confusing. The aim of these sections is that if you stick to the commands and techniques used in each workflow, you should not get into a mess or need to consult any other git documentation.
SVN style
Checking out
Clone the repository (like "svn checkout")
git clone <url>
Work on your new feature or bug fix.
Updating
Every so often, you will want to make sure you are working against the latest version of the code. This is accomplished by
git pull
If you get an error about a file not being "up to date", this means that you have made changes to a file which has also been changed in the remote repository. You need to "stash" your changes away in a safe place, then do the update, then apply your changes to the new version:
git stash git pull git stash pop
This is equivalent to "svn update". If all is well, your changes will be applied to the working copy and dropped from the stash. If there are conflicts, these will be marked with conflict markers in the source files just like with SVN. Your changes will also be kept in the stash for safe keeping, rather than being dropped. You will need to fix these conflicts. Once you have fixed all the conflicts, use "git reset" to tell git that you have fixed the conflicts. To be tidy, you can now drop your changes from the stash with "git stash drop".
What have I done?
To see what changes you have made in your local copy, use
git diff HEAD
Reverting local changes
If you want to revert one or more files to its clean state before you started editing it, use
git checkout <file1> <file2> ...
Status
If you want to see what files have been modified, added or removed, use
git status
Adding and removing files
If you need to add a new file to the repository, use
git add -N <filename>
(like svn add) for the file you have created. (The -N stops the content of the file being added to the "staging area", which would make "git diff" give confusing results. Don't worry about this.) If you need to remove a file, use
git rm <filename>
(like svn rm).
Committing
Eventually you will have your working directory in the state that you would like to commit. Ensure that you are up-to-date as described above. The next stage depends on whether you want to push your commit directly to the repository (if you have commit rights) or if you want to send your work as a patch.
Direct commit
Make a local commit:
git commit -a
This will prompt you for a commit message. The "-a" means "all registered files". This will make a local commit in your repository. Push this commit to the central repository:
git push
These two commands together are equivalent to "svn commit".
Your work is now in the central repository, and you are done.
Submitting a patch file
Sometimes you don't have commit access to the repository, or you want to have your work reviewed by someone else before you commit it. You can package up your changes into a "patch file" with
git diff > ~/Uploads/MyWork.patch
This file can be emailed or added to an issue-tracker.
Suppose you need to modify your work. Edit the working-copy files to incorporate the modifications, and then generate a new patch file as above. Repeat until the patch is final.
If you are responsible for committing the accepted patch, you can use the procedure in "Direct commit" above. If someone else has committed it, you don't need your local changes any more, and you can erase them using
git checkout
You can then update from the remote repository with
git pull
and you are done.
Git style
This style is useful if you want to keep multiple changes organised locally.
Clone the repository:
git clone <url>
Work on your new feature or bug fix. If you need to add a new file to the repository, use
git add -N <filename>
for the file you have created. (The -N tells git just to register an empty file as added to the repository. If you omit it, the entire file will be put in the staging area, which might not be what you want if you only want to commit part of the file).
If you need to remove a file, use
git rm <filename>
Suppose you now want to bundle up some of your work into a single unit. This is achieved by making a "local commit". If there are no other changes to the files that contain the work you want to commit, you can make a commit in one step using
git commit <file1><file2> ...
You will be prompted to enter a commit message to describe this change. You can type "git log" to see all the commits, and your new commit will be at the top of the list. If your work is in files which have other changes which you don't want to commit just yet, then you can interactively select which parts of the files to commit using a two-stage process. First you will "add" parts of the files to the "staging area", then you will commit the staging area:
For files which need to be added in their entirety:
git add <file1> <file2> ...
For files which need certain parts only:
git add -p <file1><file2> ...
This will ask you, for each individual change in each file, whether you want to stage that "hunk" or not. You can answer "yes", "no", "all" or "done" by entering the first letter of each. Type "?" for help on the other options. Quitting the interactive "git add" will leave all the hunks you have staged staged. If you want to unstage a hunk, you can use
git reset <file1> <file2> ...
to unstage the content of entire files, or
git reset -p <file1> <file2> ...
to select hunks to unstage. This gives you the same interface as "git add -p", but lets you unstage rather than stage each hunk.
You can see all the hunks which are currently staged using
git diff --staged
Once you are happy with what is staged, you can commit it using
git commit
You will be prompted for a commit message. Once your changes are committed, you can see them using "git log".
You can commit as many changes locally as you like. Nothing has yet been sent to a remote repository.
Every so often, you will want to make sure you are working against the latest version of the code. This is accomplished by
git stash git pull --rebase git stash pop
The "git pull --rebase" will unapply all your local commits, update your local repository with commits from the remote repository, and then attempt to re-apply your commits. This process might generate conflicts. These will be indicated in the output of "git pull --rebase". You can edit the files to fix the conflicts. When you have finished fixing the conflict, you need to mark the files as fixed using
git add <file1> <file2> ...
and then
git rebase --continue
to move on to the next commit. If at some point you decide to abandon the rebase, you can use
git rebase --abort
This will take you back to your last commit, i.e. to before the "git pull --rebase" command.
Once the "git pull --rebase" command has finished, and you have fixed any conflicts, you will use the "git stash pop" command as above. This will restore any local uncommitted changes. Again you might have conflicts which you need to fix in your working copy.
Eventually you will have a set of commits at the end of your history (at the beginning of the output of "git log") that you would like to push. Ensure that you are up-to-date as described above. The next stage depends on whether you want to push your commits directly to the repository (if you have commit rights) or if you want to send your work as a series of patches.
Direct commit
If you want to push the patches directly to the repository, you can use
git push
This will push all your local unpushed commits to the repository that you cloned from. If you need to push to a different repository (for example if you cloned from a read-only git:// repository and you need to push to an ssh git@server:/repo style repository), you can add the URL of the repository:
git push <url>
If you get an error about a "non-fastforward" push, this means that you were not up-to-date when you tried to push. Use the "git pull --rebase" command as described above to make yourself up-to-date and try again.
Your work is now in the central repository, and you are done.
Submitting a series of patches
Sometimes you don't have commit access to the repository, or you want to have your work reviewed by someone else before you commit it. You can package up all your local commits which don't exist in the remote repository (refered to as "origin") with
git format-patch -o ~/patches origin
This will make a series of numbered patch files named after the subject of each commit (the first line in the commit message) in the ~/patches directory. These files can be emailed or added to an issue-tracker.
Editing your commits
Suppose you have several local commits, but you decide you need to make changes before pushing or making patches. One option is to make the changes you need and then make new commits with these changes. You can then either push the entire set of commits or make new patch files with "git format-patch" as above.
Git allows you to modify your existing local commits. If you only need to modify the last commit, you can just make your changes to your files and use
git commit --amend <file1> <file2> ...
This will modify the last commit to incorporate your new changes.
If you have to edit a commit which is not the last one, you can use
git rebase -i <commit>
WORK IN PROGRESS HERE
In the editor window which appears, change "pick" to "edit", save the file and exit the editor. Make your changes to the working tree, then do
git rebase --continue