Tips on using Git.
Here's a quick guide—for my own use as much as anyone else's—to the Git tasks I most often need to accomplish.
Git Tasks
This section shows you how to link individual Git commands together in order to accomplish a particular task or set of tasks.
Checking Branch Status
This will be the command you use most often. What it shows depends on the state of your current local branch. We'll see lots of examples below.
$ git status
Check Your Git Version
Naturally, Git's behavior has changed over time. For our purposes, this mostly comes down to some slight changes in output and a couple of new commands.
$ git --version git version 2.23.0
Download a Repository
Downloading an existing git repository is called "cloning" the repository. It's called "cloning" because of the way git works--you actually keep a local copy of that repository, and commit your local changes there. (This is unlike other version control systems, which are centralized rather than distributed.)
The first step in this task is getting a URL for the existing git repository. Once you have that, you can follow the example below.
$ git clone https://github.com/someProject/SomeProject.git Cloning into 'SomeProject'... remote: Counting objects: 181, done. remote: Compressing objects: 100% (134/134), done. remote: Total 181 (delta 23), reused 179 (delta 21), pack-reused 0 Receiving objects: 100% (181/181), 136.50 KiB | 0 bytes/s, done. Resolving deltas: 100% (23/23), done. Checking connectivity... done. $ cd SomeProject
Use git status
: to verify the clone.
$ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working directory clean
Commit Some Work
In Git, committing is a two-step process:
- Selecting the files you want to commit, called "staging."
- Committing the files which have been staged.
Use git status
to check what has changed on your current branch.
Use "git status" to see where things stand—what you've changed, what you've staged, what's ready to be committed.
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: requirements.txt Untracked files: (use "git add <file>..." to include in what will be committed) djangosvc/newFile no changes added to commit (use "git add" and/or "git commit -a")
The call to "git status" has returned a lot of useful information, telling you both where things stand and helping you figure out what your next command should be.
- "On branch master" indicates which local branch you're currently working with. The next two lines tell you that you have already committed some changes to your local repository, but they have not been pushed to the repository on the network (the one you share with other developers).
- The lines beneath the heading "Changes not staged for commit" indicate that you have modified some files, and these changes have not yet been staged for a commit.
- "Untracked" files are files not in the repository. Usually they will be files that you've recently created, and which you'll want to add to the repository eventually. (But you can also chose to ignore them if you don't want to commit them.)
Use git add
to stage files.
$ git add requirements.txt $ git add djangosvc/newFile $ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git reset HEAD ..." to unstage) modified: requirements.txt new file: djangosvc/newFile
Compare the output of the last git status
command to that of the first. Now everything which has been changed is staged. The next time I commit, my staged changes —and only my staged changes—will be committed to my local copy (my clone) of the repository.
Use git commit -m
to commit staged items.
Committing with the -m
flag (standing for "message") is usually the easiest, though other options are available. Put your commit statement immediately after the flag, in quotes.
$ git commit -m "Insert explanatory message here." (...) $ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean
Push Changes to the Shared Repository
As we've mentioned, Git always has you working on a local copy of a branch in the central repository. If you're the only one working on a branch, then you don't need to worry about conflicts with other developers' changes. You can push your changes to the central repository with no problem.
But, depending on your team's workflow, you may be sharing a branch with other developers. In this case, you'll need to merge the other developers' changes into your copy of the branch before you push to the central repository. See Merge a Branch into Your Branch before going to the next step in this section.
Use git status
: to verify what branch you're on.
$ git status On branch newAlgorithm Your branch is ahead of 'origin/newAlgorithm' by 3 commits. (use "git push" to publish your local commits) nothing to commit, working directory clean
In this example, all changes have already been added and committed locally. But you could also have some changes not added, or even some changes not committed. The behavior would be the same as in this case; namely, only committed work would be pushed.
Use git push
to "publish" committed changes on the current branch to the shared remote repository. As of Git 2.0, running this command with no arguments pushes only the current branch's commits to the branch in the remote repository that it is tracking. Previous versions would push commits made on all local branches.
$ git push (...) Counting objects: 106, done. Delta compression using up to 8 threads. Compressing objects: 100% (28/28), done. Writing objects: 100% (49/49), 4.40 KiB | 0 bytes/s, done. Total 49 (delta 11), reused 0 (delta 0) (...)
With older version of Git, you'll most likely want to specify the branch. Here we assume that origin
is where the tracked branch resides.
$ git push origin <branchName>
Use git status
to verify your push.
$ git status On branch newAlgorithm Your branch is up-to-date with 'origin/newAlgorithm'.
Undo Changes
How to undo a change depends on the change you want to undo. In this section, we cover a couple of different situations:
- Undoing an uncommitted local change to a file.
- Undoing the uncommitted creation of a new file.
- Undoing a
git add
. - Temporarily going back to a previous commit.
- Permanently going back to a previous commit.
Undoing Unstaged Changes
Sometimes you want to undo changes to an unstaged working copy. In this example, we've made two changes to our working copy: we've modified a file, and we've created a new file.
$ git status On branch master Your branch is up to date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: requirements.txt Untracked files: (use "git add <file>..." to include in what will be committed) djangosvc/newFile no changes added to commit (use "git add" and/or "git commit -a")
Undoing an Uncommitted File Modification
In all versions of Git, you can undo a change with git checkout -- <file>
. In more recent versions, you can also use git restore <file>
.
$ git checkout -- requirements.txt
$ git restore requirements.txt
In both cases, you'll find that the modified file no longer appears as part of the output of the git status
command.
$ git status On branch master Your branch is up to date with 'origin/master'. Untracked files: (use "git add <file>..." to include in what will be committed) djangosvc/newFile nothing added to commit but untracked files present (use "git add" to track)
Removing an Uncommitted New File
To remove a new file, simply delete it from your file system as your normally would outside of Git.
$ rm djangosvc/newFile
Now git status
shows no changes on the current branch.
$ git status On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean
Undoing a 'git add'
Undo an Add or a Commit to a Single File
Suppose you've staged a file with git add
and now you've changed your mind and you want to unstage it.
Use git reset filename.txt
(or git reset
to "un-add" all the added files).
$ git reset filename.txt
Undo Everything that Hasn't Been Pushed
You can undo all your local changes with the following. Be aware, though, that this cannot be undone.
$ git reset --hard
Reverting Back to a Previous Commit
Sometimes you want to go back to a previous version of the code. The first step is to do a "git log" to find the version you are looking for.
$ git log --since yesterday (...)
Pick the version you want to revert to.
Temporarily Reverting
Sometimes you wonder how on earth some piece of code used to work. You want to get a temporary copy so that, for example, you can step through the code with a debugger.
For temporary reverting, it's best to use a new branch. Make sure you don't have any uncommitted code.
$ git checkout -b old_1061e 1061e5b0a4cbac0fb54fb1acd9db53931ffb1c68 Switched to a new branch 'old_1061e' ~/Documents/workspace/eliza$ git branch master * old_1061e
Permantantly Reverting
Sometimes things are so messed up that you want to roll all changes back to a previous version. Start by following the steps above (using "git log" to identify the version number you want). Then, instead of checking out to a new branch, use "reset". Make sure you don't have any uncommitted code—'cause you're about to lose it.
$ git reset --hard 1061e5b0a4cbac0fb54fb1acd9db53931ffb1c68
Tell Git to Ignore Some Files
Some files created in software development should not be committed to the repository. For example, files related to a particular IDE, for instance PyCharm or Visual Studio Code, probably should not be committed if some developers on the project do not use that IDE.
In this example, our status check shows that Git detects untracked __pycache__
directories. It also doesn't know that the venv
directory is for my virtual environment, and therefore should not be committed.
$ git status On branch master Your branch is up to date with 'origin/master'. Untracked files: (use "git add <file>..." to include in what will be committed) djangosvc/djangosvc/__pycache__/ djangosvc/polls/__pycache__/ venv/ no changes added to commit (use "git add" and/or "git commit -a")
Edit .gitignore
to add files to the ignore list.
In your project's root, create a file named .gitignore
if it doesn't exist, then open it for editing. (For those of you on Windows systems, be sure that the OS doesn't automatically add the .txt
extension when you create the file.) Add these lines:
/venv/ __pycache__/
The backslash before venv
means "ignore only the venv
directory that is in root"; no backslash means "ignore this directory in all cases." Other patterns exist for other cases.
Use git commit -m
to commit your changes.
If you've just created the .gitignore
file, you'll have to add it before you can commit. Here's the whole routine, with status checks before and after.
$ git status On branch master Your branch is up to date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: .gitignore no changes added to commit (use "git add" and/or "git commit -a") $ git add .gitignore $ git commit -m "Updated .gitignore." [master c82603b] Updated .gitignore. 1 file changed, 1 insertion(+), 1 deletion(-) $ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working tree clean
Stop Tracking a File
What often happens is that a file gets committed that you later realize should have been ignored. Then you try to ignore it by adding the file to your .gitignore file—but .gitignore only stops files from appearing as changed. It doesn't remove files from the repository. What you need to do is tell Git to stop tracking the file.
$ git rm --cached <file>
Recursively Stop Tracking Files Having the Same Name
(Here are some old notes I've found on how to stop tracking files which often appear multiple times in a project. Use these notes with caution—I haven't had a chance to verify them.)
I work on a Mac, where each directory has a "hidden" .DS_Store
file. The problem is, sometimes I end up creating a repository before I've remembered to have these files ignored.
First let's see which files are being tracked by Git.
$ git ls-tree --full-tree --name-only -r HEAD .DS_Store src/main/java/.DS_Store src/main/java/com/.DS_Store src/main/java/com/interdataworking/.DS_Store src/main/java/com/interdataworking/UntypedGateway.class src/main/java/com/interdataworking/UntypedGateway.java (...)
As you can see, there are three file types listed here, and it turns out I want my repository to store only the .java files.
$ git rm --cached .DS_Store rm '.DS_Store' $ git ls-tree --full-tree --name-only -r HEAD src/main/java/.DS_Store src/main/java/com/.DS_Store src/main/java/com/interdataworking/.DS_Store src/main/java/com/interdataworking/UntypedGateway.class src/main/java/com/interdataworking/UntypedGateway.java (...)
The git rm --cached
command worked, but it looks as though it is useful only for a single file.
The next line, a mix of shell commands and git commands, will perform a git rm
recursively, that is, on all the files whose names are .DS_Store
.
The I use the same approach to remove from the repository all files whose names end in ".class".
$ find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch rm 'src/main/java/.DS_Store' rm 'src/main/java/com/.DS_Store' rm 'src/main/java/com/interdataworking/.DS_Store' $ git ls-tree --full-tree --name-only -r HEAD src/main/java/com/interdataworking/UntypedGateway.class src/main/java/com/interdataworking/UntypedGateway.java (...) $ find . -name *.class -print0 | xargs -0 git rm -f --ignore-unmatch rm 'src/main/java/com/interdataworking/UntypedGateway.class' (...) $ git ls-tree --full-tree --name-only -r HEAD src/main/java/com/interdataworking/UntypedGateway.java (...)
Whew! So finally the unwanted .class
and .DS_Store
files are untracked. Now let's make sure git permanently ignores them.
$ touch .gitignore $ vim .gitignore
Edit this file so that it has two lines, with ".class" on the first and ".DS_Store" on the second.
$ cat .gitignore .class .DS_Store $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: .DS_Store deleted: src/main/java/.DS_Store deleted: src/main/java/com/.DS_Store deleted: src/main/java/com/interdataworking/.DS_Store deleted: src/main/java/com/interdataworking/UntypedGateway.class (...) Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore
The status check shows that we still have to add the .gitignore
file, and then commit all of our changes.
$ git add .gitignore $ git commit -m "Deleting unwanted files from repo. Creating .gitignore." [master b2d6f98] Deleting unwanted files from repo. Creating .gitignore. 126 files changed, 2 insertions(+) delete mode 100644 .DS_Store create mode 100644 .gitignore delete mode 100644 src/main/java/.DS_Store delete mode 100644 src/main/java/com/.DS_Store delete mode 100644 src/main/java/com/interdataworking/.DS_Store (...) $ git status On branch master nothing to commit, working directory clean $ git ls-tree --full-tree --name-only -r HEAD .gitignore src/main/java/com/interdataworking/UntypedGateway.java (...)
We committed our changes, used git status
to verify that everything has been committed, then finally used git ls-tree --full-tree --name-only -r HEAD
to make sure that we are tracking only .java files. Fini.
List All Files Being Tracked
The following two commands will list the files being tracked in a Git branch. Output can be quite verbose, depending on the size of the project.
$ git ls-tree -r master --name-only (...) $ git ls-tree --full-tree --name-only -r HEAD (...)
Create a New Branch
We'll cover three scenarios here:
- You want to create a new branch from an existing branch.
- You have made some changes on some local branch, and you want to create a new local branch with them.
- You want to put some code into a new local Git repository.
Create a New Local Branch from an Existing Branch
This is the most common case—you want to start a new task in a new branch.
Very often you'll be starting with your local copy of master
.
$ git checkout -b <newBranch> master
For other starting branches, the more general pattern to follow will be:
$ git checkout -b <newBranch> <startingBranch>
Note that startingBranch
could be either local or remote. If remote, be sure to use the full remote name, and not the name of your local copy of that remote branch.
Create a New Local Branch with Current Changes
In this situation, you've started some changes in an existing branch and, suddenly, you realize the situation requires a completely new branch.
Note that these steps work only if you haven't yet made any commits.
$ git branch <newBranch> $ git checkout <newBranch>
Create a New Local Branch with Local Changes
Suppose there's some open source code you want to have a look at, and suppose that, while having a look at it you see some things you'd like to change—and you go ahead and make them. I've done that, and then I've been very annoyed with myself for having no easy way, at some later time, to see the changes I've made.
The smart thing to do is to put the original version of the code into a repository before you make any changes. It's a simple three-step process, though I've added a few other commands so that we can see what's happening. Once you've put the original version into a repository, you can go ahead and start modifying it, knowing that you'll always be able to track the changes you've made.
We start in a directory with some open source code. In this case, it's all in a directory called src
, but that doesn't change the steps you take—what matters is that the files are not currently in a Git repository.
$ ls src $ ls src somefile $ git status fatal: not a git repository (or any of the parent directories): .git
Initialize a new repository.
$ git init Initialized empty Git repository in /Users/jkurlandski/workspace/testProject/.git/ $ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) src/ nothing added to commit but untracked files present (use "git add" to track)
The branch in the new repository will always be named master
. Don't confuse this master
branch with some other branch on your computer also called master
that is tracking an origin/master
branch in a remote repository. (And don't try specifying a different name—for example, with $ git init testProject
—because that will create a new repo in a sub-directory called testProject
, and the branch in that repo will still be called master
.)
Use git add
to indicate you want the repository to track the files in this directory, then commit.
$ git add . $ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: src/somefile $ git commit -m "Original state of project." [master (root-commit) 02417d8] Original state of project. 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/somefile $ git status On branch master nothing to commit, working tree clean
At this point we can ask the new repository to list the files it is tracking. Here are two ways of doing that. (But note that in this case they return the same thing.)
$ git ls-tree -r master --name-only src/somefile $ git ls-tree --full-tree --name-only -r HEAD src/somefile
The three key commands are git init
, git add .
, and git commit -m
. Now you can make all the changes you want and, not only do you have a record of each and every one, but you also have the ability to roll things back to a previous version.
Work with Local and Remote Branches
Remote Repositories
Use git remote
to see the remote repository/repositories.
$ git remote origin $ git remote -v origin https://me@repo.com/django1.git (fetch) origin https://me@repo.com/django1.git (push)
Use git ls-remote <remote>
to view all the branches on a remote repository.
$ git ls-remote origin (...) e3aad0f828ba6717d80cb2d835328185c86082a6 refs/heads/usertype (...)
Branch Basics
Use git branch
to view all local branches.
$ git branch * usertype master
The asterisk indicates the branch you're currently working on.
Use git branch -a
to view both local and remote-tracking branches. Remote-tracking branches are references (sometimes called "snapshots") to the state of remote branches.
$ git branch -a * usertype master remotes/origin/master remotes/origin/usertype remotes/origin/newBranch
Use git branch -r
to view only the remote-tracking branches (i.e. those that you have a local snapshot of.
$ git branch -r origin/master origin/usertype origin/newBranch
Use git branch -vv
: to verify tracking.
Generally you want your local clone to "track" a branch in the shared network repository. One advantage of this is that your Git commands won't have to specify which network repository they should operate on—it can be assumed from the branch being tracked.
$ git branch -vv master d0b9014 [origin/master] Update documentation. * usertype 7c6586e [origin/usertype] Modify user type.
The -vv
in the last command means "doubly verbose." The verbiage at the far right indicates the message pushed with that branch's last commit.
Downloading ("pulling") New Remote-Tracking Branches
If you know a branch exists on a remote repository but it doesn't appear in your local view, first see if it appears in a full listing of the remote.
$ git ls-remote origin (...)
If it appears in this output, then use git fetch
to bring it locally:
$ git fetch (...) * [new branch] job-tracker -> origin/job-tracker * [new branch] design-doc -> origin/design-doc
Save ("stash") Your Work For Later
Sometimes you're in the middle of something when, suddenly, you have to do something else. Maybe that "something else" is change to a different branch; maybe it's simply to do a pull from some branch in the remote repository. If you're not ready to commit what you're currently working on, the next best way to save it is with git stash
.
// First check status. $ git status On branch sae Your branch is up-to-date with 'origin/sae'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: shortanswer/Wiki.java no changes added to commit (use "git add" and/or "git commit -a")
If git status
shows untracked files, you have a few options:
- Figure out which version of Git you have, and follow the advice on this Stack Overflow page. Note especially that, in older versions of Git, the preferred advice could wipe out entire directories of ignored files.
- Alternatively, play it safe and use
git add
to temporarily add the untracked files to your working copy. At a later point—when you return to working on the files you are now stashing—you can undo thegit add
withgit reset filename.txt
(orgit reset
to "un-add" all the added files).
Once you have only modified or new files, you're ready to stash. You can either use git stash
, or git stash save "message"
.
$ git stash save "New short answer." Saved working directory and index state On sae: New short answer. // Verify that your status is now "clean". $ git status On branch sae Your branch is up-to-date with 'origin/sae'. nothing to commit, working directory clean
Now you can do whatever work you need to do. When finished, pop what you've stashed.
$ git stash pop On branch sae Your branch is up-to-date with 'origin/sae'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: shortanswer/Wiki.java no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (c4480e1375987e0881fe3708fdb1576ff56eefdf)
The git stash
command has a number of options that are worth exploring.
View and Clear the Stash
After you've stashed a lot of stuff, the stash can get filled with a lot of garbage, which can be annoying if you mistakenly pop something. These commands let you delete the stash after you have verified that it doesn't contain anything you need.
Other stash options:
- Use
git stash list
to view your local stash. - Use
git stash drop <id>
to drop one of the elements. - Use
git stash clear
to delete everying in the stash.
$ git stash list stash@{0}: On sae: 88dfb0b SAE: New short answer. stash@{1}: On sae: c1c59c4 New SAE eval( ) method. stash@{2}: On sae: b2f2248 fix cache bug; $ git stash drop 0 Dropped refs/stash@{0} (44f8cdcf38c7c8964d1d85f9deaf92d1f0c23050) $ git stash list stash@{0}: On sae: c1c59c4 New SAE eval( ) method. stash@{1}: On sae: b2f2248 fix cache bug; $ git stash clear $ git stash list // Returns nothing.
Compare ("diff") Branches and Files
Suppose you and your team are working on a branch. Suppose other team members have committed and pushed their work to a remote version of the branch. Do you want their changes? Maybe you do, if it's not going to mess up your work. How do you find out?
Subversion has a convenient svn status -u
command that lets you compare your local working copy to the repository by listing the status of all the files that differ between the two copies. In Git, accomplishing the same task requires two steps:
- Use
git fetch
to retrieve a local snapshot of the remote branch. - Use
git diff <localBranch> <remoteBranch> --name-status
to compare the retrieved snapshot to your local branch at the directory level.
(Before proceeding, it might be helpful to make sure you understand the difference between 'git fetch' and 'git pull'. In paticular, you shoud keep in mind that git fetch
updates only the local snapshot of the remote branch—not the code itself.)
Start by verifying you're on the right branch, and it is tracking the branch you think it is.
$ git status On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean
Since you haven't retrieved the remote branch's snapshot, you know that doing a diff
without fetch
will show no changes.
$ git diff master origin/master --name-status
Nothing. Now do the fetch, then check status.
$ git fetch (...) 69d536b..e15e2f0 master -> origin/master $ git status On branch master Your branch is behind 'origin/master' by 4 commits, and can be fast-forwarded. (use "git pull" to update your local branch) nothing to commit, working tree clean
Notice that, with the snapshot updated, the status check is now able to detect differences between your local branch and the remote. Now you're also ready to do your diff.
$ git diff master origin/master --name-status A djangosvc/API/migrations/0008_merge_20200330_3010.py M djangosvc/API/models.py
Diff Two Branches at the File Level
git diff
without the --name-status
argument will show you the diffs of two branches at a fine-grained level, giving you the line-by-line differences between two files.
$ git diff master origin/master (...)
In most cases, what happens now is that Git opens its less-like environment.
To See the Diff for One File Only
To see the diff for one file only on remote branches, separate the branch name from the file path with a colon. In this example, the two branches are origin/fix-name-issue
and origin/master
.
$ git diff origin/fix-name-issue:djangosvc/API/models.py origin/master:djangosvc/API/models.py (...)
Do the following if you want to do a diff on a local file.
Here we compare a single file in the current branch to the same file in the local version of the master
branch.
$ git diff master -- API/models.py (...)
And here we compare that same file in the current branch to the same file in the remote version of the master
branch.
$ git diff origin/master -- API/models.py (...)
See the Contents of an Incoming File
Use git show
to view the contents of the incoming file.
$ git show origin/master:djangosvc/API/models.py
Merge a Branch into Your Branch
In this Git task we cover two scenarios:
- You're working on a local copy of a branch that is tracking a branch shared in a remote repository, and you want to bring changes there local.
- You're working on a local branch, and you want to bring into your branch changes made recently to a shared branch in the remote repository that your local branch is not tracking.
Merging Changes in a Tracked Branch into Your Branch
Our example for this scenario is the master
branch: you're working on a local copy of master
, your teammates have made changes to the shared version of that branch, and you want to merge their changes into your local copy.
If you want to see first see what kinds of changes your teammates have made, see Compare ("diff") Branches and Files. But let's suppose that, for this example, you don't need to see the diffs before merging.
Start by verifying you're on the right branch, and it is tracking the branch you think it is.
$ git status On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean
Before proceeding, remove any untracked files. Commit or stash any changes.
If it's a branch I'm actively working on, I am leery of the automatic merge that a git pull
results in. So I prefer to fetch.
$ git fetch (...) 69d536b..e15e2f0 master -> origin/master $ git status On branch master Your branch is behind 'origin/master' by 4 commits, and can be fast-forwarded. (use "git pull" to update your local branch) nothing to commit, working tree clean
Merge the remote branch's changes into your current branch.
$ git merge --no-commit origin/master (...) Automatic merge went well; stopped before committing as requested
If the message is something other than "Automatic merge went well," then see the Resolve Merge Conflicts section.
$ git status On branch 43-read-and-validate-xml-data Your branch is up to date with 'origin/43-read-and-validate-xml-data'. All conflicts fixed but you are still merging. (use "git commit" to conclude merge) Changes to be committed: (...) Untracked files: (use "git add <file>..." to include in what will be committed) .pytest_cache/ .vscode.code-workspace .vscode/ Storm_Orchestration/DustStorm/migrations/0002_auto_20191118_1828.py Storm_Orchestration/IonStorm/migrations/0002_auto_20191118_1828.py backup/
- If there is nothing to merge, the merge step would look like this:
$ git merge --no-commit master Already up to date.
Merging a Different Branch into Your Branch
Recall that, in this scenario, you are working on a local branch while members of your team are pushing to a shared remote branch that your branch is not tracking. For this example, we'll suppose that your branch is called sae
and the branch shared in the remote repository is master
.
Either commit your work or, if you can't commit, save ("stash") your work.
Use git branch -vv
to check the state of local and remote branches.
$ git branch -vv * sae cd01005 [origin/enable-deletion: ahead 3] Enable deletion. master baf79d3 [origin/master: behind 21] test
This tells you that you're are on the sae
branch, and that there have been 21 commits to master
since you branched off it.
Use git checkout
to switch to the master
branch.
$ git checkout master Switched to branch 'master' Your branch is behind 'origin/master' by 21 commits, and can be fast-forwarded. (use "git pull" to update your local branch)
Don't be concerned if a git status
at this point shows untracked files.
$ git status On branch master Your branch is behind 'origin/master' by 21 commits, and can be fast-forwarded. (use "git pull" to update your local branch) Untracked files: (use "git add <file>..." to include in what will be committed) djangosvc/archive.sqlite3 nothing added to commit but untracked files present (use "git add" to track)
Use git pull
to copy the latest from the remote master
branch locally. The output of this command can be quite verbose.
$ git pull (...)
Now switch back to the branch you were working on.
$ git checkout sae Switched to branch 'sae' Your branch is ahead of 'origin/sae' by 3 commits. (use "git push" to publish your local commits)
Use git merge --no-commit master
to merge the local master
branch into your local branch.
There are three possibilities at this point:
- There will be nothing to merge.
- Git will decide to handle the merge itself.
- There will be conflicts.
1. If there is nothing to merge, the merge step would look like this:
$ git merge --no-commit master Already up to date.
2. If Git decides to handle the merge itself, this is what you will see, and these are the steps you should take.
$ git merge --no-commit master (...) Automatic merge went well; stopped before committing as requested $ git status On branch enable-deletion Your branch is up to date with 'origin/enable-deletion'. All conflicts fixed but you are still merging. (use "git commit" to conclude merge) Changes to be committed: (...) Untracked files: (use "git add <file>..." to include in what will be committed) someNewFile.py
Now is probably the best time to run your unit tests.
Assuming the tests all pass, use git add
to add any new untracked files. Then commit your changes. Finally, in most cases you'll want to push your changes to the remote repo's version of your branch.
$ git add someNewFile.py $ git commit -m "Merging with master." $ git push (...)
3. If there are conflicts, good luck to you. Let's hope that your teammates have been responsibly verifying their code with unit tests.
Here's how Git shows you there are merge conflicts.
$ git merge --no-commit master Auto-merging djangosvc/test/test_api_models.py CONFLICT (content): Merge conflict in djangosvc/test/test_api_models.py Auto-merging djangosvc/API/views.py CONFLICT (content): Merge conflict in djangosvc/API/views.py Automatic merge failed; fix conflicts and then commit the result.
See the Resolve Merge Conflicts section for some advice.
After resolving the merge conflicts, don't forget to run your unit tests. Then commit all your merge conflict fixes. Finally, in most cases you'll want to push your changes to the remote repo's version of your branch.
$ git commit -m "Merging with master." $ git push (...)
Resolve Merge Conflicts
An unpleasant but almost unavoidable consequence of working on a team is that, sooner or later, two developers will separately change the same piece of code. When Git can't safely merge their changes, it declares the file to be a "Conflict."
Assessing a Merge Conflict
The following is likely to be your first notice of a conflict.
$ git merge --no-commit master Auto-merging djangosvc/test/test_api_models.py CONFLICT (content): Merge conflict in djangosvc/test/test_api_models.py Auto-merging djangosvc/API/views.py CONFLICT (content): Merge conflict in djangosvc/API/views.py Automatic merge failed; fix conflicts and then commit the result.
The git status
command might show some files as having been successfully merged.
$ git status On branch endable-deletion Your branch is ahead of 'origin/endable-deletion' by 3 commits. (use "git push" to publish your local commits) You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Changes to be committed: modified: .gitignore new file: djangosvc/services/delete.py Unmerged paths: (use "git add <file>..." to mark resolution) both modified: djangosvc/API/views.py both modified: djangosvc/test/test_api_models.py
The output above tells you a couple of things worth noting:
.gitignore
anddelete.py
have been successfully merged.views.py
andtest_api_models.py
are in a conflict state.
Fixing Merge Conflicts
A good resource for resolving merge conflicts is the GitHub help page for this task. Even though it was written specifically for GitHub users, it can serve as a guide for the problem in general.
As the help page indicates, when it discovers a merge conflict, Git inserts lines into the conflicted file. I can't improve on the page's explanation, so I'll quote from it directly:
To see the beginning of the merge conflict in your file, search the file for the conflict marker <<<<<<<. When you open the file in your text editor, you'll see the changes from the HEAD or base branch after the line <<<<<<< HEAD. Next, you'll see =======, which divides your changes from the changes in the other branch, followed by >>>>>>> BRANCH-NAME. In this example, one person wrote "open an issue" in the base or HEAD branch and another person wrote "ask your question in IRC" in the compare branch or branch-a.
If you have questions, please <<<<<<< HEAD open an issue ======= ask your question in IRC. >>>>>>> branch-a
In Java and similar languages, the lines will cause a compilation error; in Python, a runtime error.
For each conflict inserted into the file, you have three options:
- Keep your changes.
- Keep the other branch's (the "incoming") changes.
- Make a new change, often done by incorporating parts of the two separate changes.
Delete the conflict markers <<<<<<<, =======, >>>>>>> and make the changes you want in the final merge. In this example, both changes are incorporated into the final merge:
If you have questions, please open an issue or ask in our IRC channel if it's more urgent.
You will probably find that an IDE can help you resolve conflicts. For example, Visual Studio Code displays tiny buttons you can click on with the labels Accept Current Change, Accept Incoming Change, Accept Both Changes and Compare Changes.
Wrapping up Your Merge Conflict Fixes
For each file having a merge conflict, you have to add it to the staging area.
$ git add djangosvc/API/views.py $ git add djangosvc/test/test_api_models.py
Don't forget to run your unit tests. Then commit all your merge conflict fixes.
$ git commit -m "Merging with master."
See Recent Commits to a Branch
Use git log
: to view a branch's commit messages.
Using git log
in its bare form gives you output which is very unpleasant to look at. Here's a command that makes it pretty—or, at less unsightly.
$ git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative <branch_name> * c82603b - (HEAD -> master) Updated .gitignore. (9 days ago) * b538899 - (origin/master, user-profile) Initial setup of project. (10 weeks ago)
If the branch has a long history, Git will open its less-like environment. Here we're showing output that is short enough that Git automatically takes you back to the command prompt.
As you can see, output is in reverse-chronological order. This means that for older projects you'll probably want to pipe the output to head
. (Note that in the example below, piping with head
does not change the output because there is so little of it.)
$ git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative <branch_name> | head * c82603b - (HEAD -> master) Updated .gitignore. (9 days ago) * b538899 - (origin/master, user-profile) Initial setup of project. (10 weeks ago)
Other Useful Commands
See your changes for a particular file before adding or committing it.
$ git diff <filename>
Search entire codebase for this string. Prints out lines containing the string.
$ git grep "<string>"
View commit messages in pretty format for a branch.
$ git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative <branch name>
For a file:
$ git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative -- requirements.txt
You can't do a git pull because of a file ignored:
$ git pull Username for 'https://10.312.265.105': someUser Password for 'https://someUser@10.312.265.105': warning: redirecting to https://10.312.265.105/qae/GUI/qae_center.git/ Updating 7c2586e..01c4d35 error: Your local changes to the following files would be overwritten by merge: QAnswering/archivalSQL.sqlite3 Please commit your changes or stash them before you merge. Aborting $ git update-index --no-skip-worktree QAnswering/archivalSQL.sqlite3 $ git checkout -- QAnswering/archivalSQL.sqlite3 $ git pull
Git's "less-like" Environment
If the output of a command like git diff
or git log
is very long, it will open what I call a "less
-like" environment. I call it "less
-like" because you use the same commands to navigate through this environment as you would when you call the Linux less
command on a file. Here are the basics:
- Ctrl-F to go foward.
- Ctrl-B to go backward.
- ? to exit.
'git fetch' vs. 'git pull'
When you bring a branch in the remote repo local, you're bringing in just a snapshot—a copy of what's in the remote repository. The local copy is updated only when you explicitly update it. You explicitly update it in one of two ways:
- git fetch
downloads the latest data from the remote repository, but it doesn't integrate this data into your working copy.
- git pull
downloads the data, and merges it into your work.
From this description of git fetch
, it follows that git fetch
is harmless—it never changes your local copy. Run the fetch
command as often as you'd like, without worry. If after fetching you want to merge the fetched changes into your code, follow the git fetch
with a git merge
. In other words:
git fetch
+git merge
=git pull
Git Resources
http://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
http://git-scm.com/book/en/v2/Getting-Started-Git-Basics
https://www.git-tower.com/learn/git/faq/track-remote-upstream-branch