In simple terms, if you were about to hop onto a plane without any Internet connection...before departing you could just do git fetch origin <branch>
. It would fetch all the changes into your computer, but keep it separate from your local development/workspace.
On the plane, you could make changes to your local workspace and then merge it with what you've fetched and resolve potential merge conflicts all without a connection to the Internet. And unless someone had made new changes to the remote repository then once you arrive at the destination you would do git push origin <branch>
and go get your coffee.
From this awesome Atlassian tutorial:
The git fetch
command downloads commits, files, and refs from a
remote repository into your local repository.
Fetching is what you do when you want to see what everybody else has
been working on. It’s similar to SVN update in that it lets you see
how the central history has progressed, but it doesn’t force you to
actually merge the changes into your repository. Git isolates
fetched content as a from existing local content, it has absolutely
no effect on your local development work. Fetched content has to be explicitly checked out using the git checkout
command. This makes
fetching a safe way to review commits before integrating them with
your local repository.
When downloading content from a remote repository, git pull
and git fetch
commands are available to accomplish the task. You can consider
git fetch
the 'safe' version of the two commands. It will download
the remote content, but not update your local repository's working state,
leaving your current work intact. git pull
is the more aggressive
alternative, it will download the remote content for the active local
branch and immediately execute git merge
to create a merge commit
for the new remote content. If you have pending changes in progress
this will cause conflicts and kickoff the merge conflict resolution
flow.
With git pull
:
- You don't get any isolation.
- It doesn't need to be explicitly checked out. Because it implicitly does a
git merge
.
- The merging step will affect your local development and may cause conflicts
- It's basically NOT safe. It's aggressive.
- Unlike
git fetch
where it only affects your .git/refs/remotes
, git pull will affect both your .git/refs/remotes
and .git/refs/heads/
Hmmm...so if I'm not updating the working copy with git fetch
, then where am I making changes? Where does Git fetch store the new commits?
Great question. It puts it somewhere isolated from your working copy. But again where? Let's find out.
In your project directory (i.e., where you do your git
commands) do:
ls
. This will show the files & directories. Nothing cool, I know.
Now do ls -a
. This will show dot files, i.e., files beginning with .
You will then be able to see a directory named: .git
.
Do cd .git
. This will obviously change your directory.
Now comes the fun part; do ls
. You will see a list of directories. We're looking for refs
. Do cd refs
.
It's interesting to see what's inside all directories, but let's focus on two of them. heads
and remotes
. Use cd
to check inside them too.
Any git fetch
that you do will update items in the /.git/refs/remotes
directory. It won't update anything in the /.git/refs/heads
directory.
Any git pull
will first do the git fetch
, update items in the /.git/refs/remotes
directory, then merge with your local and then change the head inside the /.git/refs/heads
directory.
A very good related answer can also be found in Where does 'git fetch' place itself?.
Also, look for "Slash notation" from the Git branch naming conventions post. It helps you better understand how Git places things in different directories.
To see the actual difference
Just do:
git fetch origin master
git checkout master
If the remote master was updated you'll get a message like this:
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
If you didn't fetch
and just did git checkout master
then your local git wouldn't know that there are 2 commits added. And it would just say:
Already on 'master'
Your branch is up to date with 'origin/master'.
But that's outdated and incorrect. It's because git will give you feedback solely based on what it knows. It's oblivious to new commits that it hasn't pulled down yet...
Is there any way to see the new changes made in remote while working on the branch locally?
Some IDEs (e.g. Xcode) are super smart and use the result of a git fetch
and can annotate the lines of code that have been changed in remote branch of your current working branch. If that line has been changed by both local changes and remote branch, then that line gets annotated with red. This isn't a merge conflict. It's a potential merge conflict. It's a headsup that you can use to resolve the future merge conflict before doing git pull
from the remote branch.
Fun tip:
If you fetched a remote branch e.g. did:
git fetch origin feature/123
Then this would go into your remotes directory. It's still not available to your local directory. However, it simplifies your checkout to that remote branch by DWIM (Do what I mean):
git checkout feature/123
you no longer need to do:
git checkout -b feature/123 origin/feature/123
For more on that read here