0

What is the main difference between following git commands?

git fetch --all
git pull origin repo1
git merge repo1
  • Note that this is not a very good question. See [ask] for advice on writing better questions. Because the question is unfocused, the answers we can give are similarly not well focused; my answer is quite general. – torek Jun 06 '21 at 05:49

2 Answers2

0

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:

  • The first command is git fetch (with certain arguments), and that never overwrites any files.
  • The second command is under your control (to a large extent). The default 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:

  • call up some other Git repository;
  • have them list out all their branch names and commit hash IDs;
  • use this information to get, from the other Git repository, all their commits; and
  • use that to update all our own (local) remote-tracking names to remember their branch names.

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:

  1. 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.

  2. 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.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thanks for answering. I did study the git documentation, the problem is not with the unfamiliarity of the git commands, it is somewhere else that I can not find. The problem in simple words is that: (Pulling contents from a remote branch to my local branch is not updating the local branch contents, it shows that "Everything is up to date" but the contents are not the same when you look at them closely, they are completely different) this is the shorten scenario of the problem. – Sayed Hussainullah Sadat Jun 06 '21 at 06:03
  • @SAHASADAT: The "everything is up to date" message comes from `git merge` and means just what it says. What you need is a [mcve]. – torek Jun 06 '21 at 09:36
0

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