What is the main difference between following git commands?
git fetch --all
git pull origin repo1
git merge repo1
What is the main difference between following git commands?
git fetch --all
git pull origin repo1
git merge repo1
First, git pull
is not meant to, nor supposed to, "overwrite local file contents". It's just shorthand for run two Git commands, with the second command being git merge
by default:
git fetch
(with certain arguments), and that never overwrites any files.git merge
does not mean overwrite my files: it means merge my commits.1 The other main option, git rebase
, does not mean overwrite my files: it means rebase my commits.Note that the common denominator here is the word commits. Understanding that Git is about commits is one of the keys to using Git. Git is not about branches, though Git uses branch names to find commits. Git is not about files either, although commits contain files. Git is really all about the commits.
What this means is that you need to learn what commits are and do for you. There are a lot of articles and web tutorials about this, including many of my existing StackOverflow answers; find and read some of them. Don't just blindly try various Git commands: learn what commits are, what a repository is, and how Git uses commits. Learn how branch names help Git locate commits, and how branch names are specific to each repository copy.
Meanwhile, let's look at these three commands:
git fetch --all
If you're a Git beginner, I recommend that you never use --all
here. It's not that it is harmful, it's that beginners think it means all branches, and it doesn't. The git fetch
command means:
This updates no branches at all, because our branches are local to our own Git repository. It does, however, obtain all commits from all of their branches.2 This works well because we don't need branches to find commits. While Git (and we) use branch names to find commits, we can have Git use other names—remote-tracking names, in this case—to find commits, too. We just don't want to make our own commits this way.
git pull origin repo1
This means:
Using the name origin
, call up a Git. Get commits from them, as with git fetch origin repo1
. This first step explicitly limits the update from origin
to the commits that are find-able, in that other Git, using the branch name repo1
. This does not go to some repository repo1
; it goes to the repository named origin
.
If all of that has succeeded, run the second Git command to incorporate any new commits obtained from their Git, using their name repo1
(our origin/repo1
), into the current branch, whatever that branch may be.
As always, this second command is under your control: you must choose, before seeing what commits you get, whether you will use git merge
or git rebase
to incorporate those commits. Then you have your Git run git fetch
to get those commits, and immediately—with no chance to check whether this is a good idea now—run that second command.3
git merge repo1
This means: use the name repo1
to locate a specific commit, and merge that into my current branch. This usually makes sense if you have your own branch named repo1
and it is time to incorporate the commits that your Git will find on your repo1
into whatever branch you are on now.
Note that it does not matter where you got those commits, only that they are now findable by your name repo1
. If you don't have the name repo1
as a branch name, this command won't do anything useful.4 If you have origin/repo1
as a remote-tracking name, you could run git merge origin/repo1
: that will use your remote-tracking name to locate the final commit in the sequence, just as git merge repo1
tries to use your (local) branch name to locate the final commit in the sequence.
It's important to realize that all of these things are really just Git's way to locate some commit. When you use git fetch
, you're getting commits from some other Git repository, and adding them to your Git repository. This updates your remote-tracking names so that you can locate these commits. Merging combines the work on some commit(s) with work on other commit(s): it does not overwrite files, but rather combine work.
1This is an overstatement, because git merge
is quite complicated. Sometimes there are no commits to merge, and sometimes Git can "cheat" and do a fast-forward operation instead of a merge; and, rarely, there's no shared history between your commits and their commits, which makes merging unwise, and modern Git will refuse to do that by default. But in many common cases, it does mean merge commits, and in the cases where Git can use --ff-only
instead of --no-ff
, you can use --no-ff
to force it to make a real merge. Whether that's what you want is another question entirely.
2The exception here is when your local Git is configured as a single-branch clone. In this case, git fetch
obtains and updates only one of the other Git's branch information, so that you only update one remote-tracking name in this Git. Adding --all
does not help here: if you have a single-branch clone and want to convert it to an all-branch clone, see How do I "undo" a --single-branch clone?
3I tend not to like to do this since sometimes it's not a good idea, depending on what came in. There are ways to fix things up after the fact, but I like to run git fetch
myself, then look at what that did, and only then run git merge
or git rebase
, or maybe not run either of those. But many people like the convenience of fetch-then-immediate-second-command, and there's nothing wrong with that.
4Again, this is a bit of an overstatement. For instance, if you add repo1
as a remote, the bare name repo1
is shorthand for repo1/HEAD
, if that name exists. After git remote add repo1 <url>
and git fetch repo1
, you'll have commits obtained from whatever Git repository responds to Internet phone-calls at the specified URL. They presumably have a HEAD
and if you have had Git set up your repo1/HEAD
as a symbolic reference to whatever branch name they have as their HEAD
, using git remote set-head repo1 --auto
for instance, you'll now be able to merge that. I recommend that beginners avoid this kind of deeper magic: if you want to merge their foo
branch, use git fetch repo1
followed by git merge repo1/foo
, or—if you really like git pull
—use git pull repo1 foo
.
If you always want your server version to reflect a commit from your repo, it's probably better to use git reset instead of git pull - that way you never invoke merge functionality, but instead set all of the files to exactly what they are in the commit you reset to. For example:
git fetch origin master
git reset --hard FETCH_HEAD