Here you can find the questions I commonly ask myself while managing my source code, over and over again. This serves as a reference to those tasks which are common, but not quite common enough to remember easily. If any of the terms used here are unclear, you can refer to the Git Glossary.
Contents
- 1 Creating Repos
- 2 Undoing Mistakes
- 2.1 How do I undo something?
- 2.2 Just reset everything. Get me back in sync with everyone else.
- 2.3 Oopsie, I didn’t mean to ‘git add’ those changes.
- 2.4 How do I undo an accidental commit?
- 2.5 I moved some directories and I want to mark them as renames.
- 2.6 I accidentally committed and pushed a file I don't want to track in the repo.
- 3 Inspecting Repo Content
- 4 Adding/Removing Files
- 5 Managing Branches
- 5.1 How do I start my own branch?
- 5.2 How do I share my branch with others?
- 5.3 Start a new branch based on my current changes.
- 5.4 Same as above, but I already committed the changes to master. Oops. How do I pull them out of master and into a new branch?
- 5.5 How do I rebase one branch on top of another?
- 5.6 How do I update a branch with the latest remote version after a force-push?
- 6 Diffs
- 7 Miscellaneous
Creating Repos
How do I set up a repo for some already-existing files?
1 2 3 4 |
$ cd projectDir $ git init $ git add . $ git commit |
How do I set up a remote repo for some already-existing files?
Same as above, plus:
1 2 3 |
$ git remote add origin <repo-url> $ git pull origin master $ git push -u origin master |
How do I "fork" a repo without using GitHub?
Sometimes I want to create a private copy of a repo, which GitHub does not allow. For example, how would I "fork" something from GitHub to alternative service Bitbucket? This is almost equivalent to just cloning the repository, with some small additions. This is different from purely duplicating the repository.
Let's say you want to create some new-invention on Bitbucket based on some prior-art from GitHub. In essence, what you want is (1) a "mirror clone" of the prior art, and (2) a way to pull changes from the upstream prior art and merge them with your own new invention.
To set up the new repository (mirror clone):
1 2 3 4 5 6 7 8 9 10 11 12 |
# Create a copy of the old repo. $ git clone --mirror git@github.com:username/prior-art.git $ cd prior-art.git $ git push --mirror git@bitbucket.org:username/new-invention.git # Set up your new repo to refer the old repo. $ git clone git@bitbucket.org:username/new-invention.git $ cd ../new-invention $ git remote add upstream git@github.com:username/prior-art.git # Don't need the temporary copy anymore. $ rm -rf prior-art.git |
Whenever you want to grab the latest changes from the prior art:
1 2 3 4 5 |
# In folder new-invention... $ git fetch upstream $ git merge upstream/master # OR $ git pull upstream master |
You must specify the branch you want to pull. This merges upstream/master into whatever local branch you're currently on (the output of git branch). Don't accidentally merge two branches that aren't supposed to be the same!
Undoing Mistakes
How do I undo something?
You have (at least) four choices.
-
1git checkout
- To erase changes in your working copy.
- “I made some changes but I don’t want them anymore.”
-
1git commit --amend
- To fix just the most recent commit.
- “I forgot to mention something in the commit message.”
- “I forgot to add this one file to the commit.”
-
1git reset
- To fix local errors and errors in your working copy.
- “Oops, I didn’t mean to ‘git add’ those changes.”
- “I messed up a merge, I want to start over and try again.”
- “I decided the last few commits were all wrong. I want to destroy them forever.”
- “I decided the last few commits were all wrong. I want to modify them and re-commit.”
- “I accidentally merged from the wrong branch, or merged the wrong commits. Where’s the reset button on this thing?!”
-
1git revert
- To fix already-pushed errors.
- “Someone discovered something seriously wrong with my bugfix. We need to revert the change right away.”
- “Someone ported a patch from a previous version of the software, but it doesn’t belong there. We just need to undo it.”
Just reset everything. Get me back in sync with everyone else.
Please, God, why is this so hard?! I just want what’s in the repository. I don’t care what’s in here now, I don’t care that it would be “overwritten by merge”—kill them with fire—nuke them from orbit—I JUST WANT WHATEVER EVERYONE ELSE HAS!
1 |
$ git reset --hard |
The only annoying thing about this is it still won't delete all your untracked files; you'll have to do that yourself.
Oopsie, I didn’t mean to ‘git add’ those changes.
1 |
$ git reset |
See also the FAQ about different ways to undo things.
How do I undo an accidental commit?
1 |
$ git reset HEAD~1 |
See also the FAQ about different ways to undo things.
I moved some directories and I want to mark them as renames.
No need! Git will detect this automatically!
I accidentally committed and pushed a file I don't want to track in the repo.
See How to stop tracking (remove) a file without deleting it? If you haven't already pushed, you should definitely instead see git reset or git commit --amend above!
Otherwise, unfortunately, there's no easy way to undo this. You will need to remove the file from your repo, so it will still be stuck in the repo history taking up space, unless you go much deeper. Furthermore your colleagues and your other machines will have the file deleted when they pull, so all other machines must backup the local file before they pull the removing change, then pull, then restore the file. The ideal method is to remove the file and add it to .gitignore in a single commit.
If it is necessary for you to try harder to fix this, there may be solutions for you; check here.
Inspecting Repo Content
How do I list all tracked files?
1 |
$ git ls-files |
How do I pull down all remote branches?
Update 2021-10-12: I wrote this some time ago, and I'm no longer sure why you would really want to do this, unless you were backing up or migrating the code somewhere. Maybe if you wanted to browse all the branches without a web interface? In any case, you can still do it if you like...
Method 1:
For each remote branch in git branch -r:
1 |
$ git branch --track local-name origin/remote-name |
Then execute once:
1 |
$ git pull --all |
This method will leave your working copy where it is.
Method 2:
For each remote branch in git branch -r:
1 |
$ git checkout remote-name |
OR
1 |
$ git checkout -b local-name origin/remote-name |
This method will change your working copy to the checked-out branch.
Method 3:
You can also see how this appears in your .git/config, and even set it up manually.
1 2 3 |
[branch “local-name”] remote = origin remote = refs/heads/remote-name |
Adding/Removing Files
How to stop tracking (remove) a file without deleting it?
Warning: When you commit this, if others pull it, their local copy of this file will get deleted.
1 |
$ git rm --cached <file> |
Following this, you may want to add the file to your .gitignore
Managing Branches
How do I start my own branch?
Typically, you want your branch to track the branch which it stems from, so you can easily merge/rebase upstream changes into your branch. This also makes it easier to maintain a chain of branches with parent/child relationships. So I will provide instructions for that. You normally also want to switch to the new branch immediately to start working on it. So you really want to do three things:
- Create a new branch from a particular starting point.
- Set the starting point as the "upstream" branch (a.k.a. "tracking" branch).
- Switch to the new branch.
You can do all three things with a single command:
1 |
$ git checkout -b <local-branch-name> [<start-point>] -t |
<start-point> is optional, and will default to HEAD if omitted. It can be branch name, commit-id, or tag.
However, if it is a remote branch (of the form origin/<branch-name>), then in fact "switching" to a branch with a matching name will automatically create a local tracking copy of that branch, so all you need to do is:
1 |
$ git checkout <branch-name> # Note that we dropped the `origin/` prefix here. |
It used to be that you were forced to do this in two separate steps, which you can still do if you prefer to switch to the branch later:
1 2 |
$ git branch -t <local-branch-name> [<start-point>] # Create branch and track. $ git checkout <local-branch-name> # Switch to branch. |
Once you've started your branch and you're ready to share it with the world, you need a special command to push it for the first time.
If you set up a local upstream tracking branch (the -t option when creating the branch, as described in the previous question), things are much simpler:
1 |
$ git push origin |
WARNING: If you have push.default set to upstream in your .gitconfig, then this will push directly onto the main branch instead of a new remote branch! You must instead specify the destination name explicitly (see below).
Notes:
- You could instead choose to push to any other remote you have access to.
- The resulting remote branch becomes origin/<local-branch-name>.
- [Optional] The -u flag: Set up this branch to track the remote branch.
- If you are maintaining a chain of local branches that depend on each other, then you may want to maintain those relationships, in which case you will omit the -u.
- If you used both -u and origin, then for subsequent pushes you can simply call git push. Otherwise, you will need to say git push origin.
If you are not tracking branches locally, then you will need to manually specify the destination:
1 |
$ git push -u origin <destination-branch-name> |
Notes:
- You could instead choose to push to any other remote you have access to.
- To avoid confusion, the destination name should almost always be the same name as what it's named locally, so that the resulting remote branch becomes origin/<local-branch-name>.
- If you used both -u and origin, then for subsequent pushes you can simply call git push. Otherwise, you will need to say git push origin.
Start a new branch based on my current changes.
I made a whole ton of changes, and all of a sudden I now realize that this is too big to go into a few commits, it really needs its own branch. How do I start a new branch after the fact?
Answer: the same way you start a branch normally!
1 |
$ git checkout -b foo -t |
Commit as normal, you are now on the branch.
Same as above, but I already committed the changes to master. Oops. How do I pull them out of master and into a new branch?
As long as you haven’t pushed the changes, you can do this: Commit everything first. If you have, let's say, three commits:
1 2 3 |
$ git branch newbranch $ git reset --hard HEAD~3 # Send master back 3 commits. $ git checkout newbranch |
You’re now on the new branch.
How do I rebase one branch on top of another?
Sometimes you're working on two things in parallel, and then decide instead that one of them needs to depend on the other. Or you need to depend on someone else's branch. Or you have two branches in series, but you want to change the order in which they get merged, because a child is ready to merge but the parent isn't ready yet. In all these cases and more, you may want to rebase your branch onto another.
First, I'll assume your branch has no merge commits in it. See more notes about this below.
As long as that is true, you can rebase your branch onto another and update the tracking information. How to do this varies based on how the tracking branch is set up...
On a branch with a local upstream
This is the easiest situation—your branch is tracking another local branch. Your branch's upstream must already be set properly (which can be done with git branch -u <current-upstream>). You can view this information with git branch -vv. <current-upstream> is often just main, if your branch has no other parents.
Let <new-upstream> be the new parent you want to be branched off of.
1 2 |
$ git rebase --onto <new-upstream> $ git branch -u <new-upstream> |
That's it!
On a branch with a remote upstream
Often, people have each of their local branches track the remote counterpart, even if it is dependent on some local branch. This will be the default case if you've checked out a branch that was created on another machine. In this case, we sadly are lacking the information about which commits should be moved, so we need a more manual approach. If you use the above approach for this type of branch, you will lose your commits! (But they can always be recovered using the reflog.)
So, in our case we need to manually look at our git log and figure out which commits belong exclusively to this branch and should be carried over to the newly-rebased branch.
- View your git log (I highly recommend using the git lg alias included in my .gitconfig), and figure out which commit starts the current branch (this may not be obvious from the branch tags and you may need to pay close attention to the commit messages, so I hope you wrote descriptive commit messages).
- Then, copy the git-sha hash for the immediate parent to this commit. We will refer to this as the <previous-upstream>.
- Now, we can rebase:
1$ git rebase --onto <new-upstream> <previous-upstream>
- If you want to, you can use the same command as above to set the new tracking branch, but I assume most of the time you won't want this if you are tracking a remote branch.
1$ git branch --u <new-upstream>
On a branch with merge commits
The above techniques both assume your branch has no merge commits in it. This assumes you are using a "rebase" workflow, or you haven't merged in any changes from the upstream since starting the branch.
If you have been merging in commits from main/upstream, that's going to be more painful, because now your branch is a mix of commits. I'm not currently sure if the above works for that situation, although it might. I'm not sure of the right way to do it. But when I find out I'll try to update this page.
How do I update a branch with the latest remote version after a force-push?
If only new commits have been added (to the local, or the remote, or both), then we can use rebase very easily:
1 |
$ git pull --rebase origin <branch-name> |
However, if some of the pre-existing commits were removed or changed, I'm fairly sure we need a more drastic approach:
1 2 3 |
$ git reset --hard origin <branch-name> # OR this (and it's unclear to me when we need one or the other) $ git reset --hard origin/<branch-name> |
The above will not preserve any local commits you had, or any unstaged changes in your working tree. You can preserve the unstaged changes with a soft reset:
1 |
$ git reset --soft origin <branch-name> |
But this still won't recover your local commits. This is fine if you are just tracking a colleague's branch and hadn't made any un-pushed changes. Otherwise, I think you'll need to create a new branch and cherry-pick the missing commits from the old one:
1 2 3 |
$ git branch -m old-<branch-name> $ git checkout <branch-name> $ git cherry-pick <commits-from-old-branch> |
Diffs
How do I diff my current branch with master?
1 |
$ git diff master |
My diff shows nothing but I have made changes! Where are they?
If you git add your changes then they will no longer show up in git diff. This is because diff shows the difference between the working copy and the index. If you want to see the difference between the index and the current branch, you need to:
1 |
$ git diff --cached |
How do I see the affects of a single commit?
(In other words, how do I diff any commit with its parent?)
1 |
$ git show <commit> |
How do I diff any two things?
1 |
$ git diff <old-thing> <new-thing> |
This works between any two commits, or any two refs, in the whole repository. If you specify only one commit/ref, then newthing defaults to HEAD. To diff any two files outside the repository, you can use:
1 |
$ git diff --no-index <old-file> <new-file> |
How do I find the source of a buggy commit?
This command is super cool!
1 |
$ git bisect |
Or, if you already know the faulty line(s) of code, but you want to know why they were committed:
1 |
$ git blame |
Miscellaneous
What are the ways to refer to a commit?
How the hell do I refer to one of these “commitishes”? Do I really have to copy-paste this whole damn SHA-1 hash??
Luckily, no. There are many ways of specifying a commit:
- By its SHA1 name, which you can get from git log.
- By the first few characters of its SHA1 name.
- By a head. For example, HEAD refers to the commit object referenced by HEAD. You can also use the name, such as master.
- Relative to a commit. Putting a caret (^) after a commit name retrieves the parent of that commit. For example, HEAD^ is the parent of the current head commit. origin/master^ would be the parent of wherever the remote repository currently is at.
- Relative to a commit. You can also use the notation HEAD~1 to walk an arbitrary number of steps back in history. HEAD~1 == HEAD^ and HEAD~3 == HEAD^^^
- You can also say: HEAD@{-1}. But this is a very special case! It refers to wherever HEAD was last, which could be backward or forward or on a whole other branch! Read about the reflog before you use it.
How do I commit a file once and ignore it thereafter?
This often comes up when you have a template file that programmers will customize to their liking. You don't want their customizations to make it into the team's central repo. However, you do want some kind of default version, for those who don't customize it.
If you place this file in your .gitignore, then you'd have to create it from scratch every time you clone a fresh copy of the repo. That's no good. Instead, commit the default version of the file, then do this:
1 |
$ git update-index --assume-unchanged path/to/file.txt |
After that any changes to this file will no longer show up in git status. But you'll still be able to commit new changes to the default by explicitly adding them to the index:
1 |
$ git add -f path/to/file.txt |
Recommended graphical clients?
- GitX on Mac, TortoiseGit on Windows, ?? on Linux
- Versions on Mac, TortoiseSVN on Windows, ?? on Linux
Recommended graphical differs?
Beats me.
Well then what do you use?
I use an IDE, but lately I’ve been using them only for hairy conflicting merges. IntelliJ IDEA and Eclipse both work well.
But I don’t wanna use an IDE!
Well then use vimdiff, you hippie.
1 2 3 |
$ git mergetool -t vimdiff $ git mergetool -t vimdiff2 $ git mergetool -t emerge |
I already have an SVN repository but I want to start using Git.
If migrating from SVN, don't use git-svn, git-svn slow and terrible; use SubGit instead, SubGit is fast and awesome.