Which items from the local .git directory get copied to/from the remote upon pushing/fetching?

git fetch
git fetch vs pull
git fetch remote branch
git push
git fetch --all
git merge
git pull
git delete local branch
  1. When you push to a remote repository, which items from the local .git directory get copied to the remote .git directory?

  2. The same question in the opposite direction, when you perform fetch.

Basically two things get shared during fetch or push operations: objects and refs. Note that the receiving repo might store a given piece of information in a different file than where the sending server stored that piece of information.

Objects include commits, "tree" objects (which represent content directories) and "blob" objects (which represent individual files of content), among other things. Together these make up your project's history. They are stored under .git/objects in either "loose" form (a file per object, in directories/files whose names are derived from the object's ID - which is the SHA hash of the object data) or "packed' form (in a file under .git/objects/packs). Transfers between repos use packed form, and it's up to the receiving repo to reorganize its packs if it sees fit.

Refs are branches, tags, and other things; they provide "entry points' into history. These are stored in "loose" form under .git/refs or in packed form in a packed-refs file. Not only might a locally-loose ref end up packed on the receiving end, but depending on the refspec used to share the ref, the remote might update an entirely different ref (e.g. when fetching you typically update a tracking ref to match the remote's branch)

Git Fetch, git/objects directory, Git stores all commits, local and remote. branches you might see after fetching a remote repo named conveniently named remote-repo: git  Typically, you'd merge a remote-tracking branch (i.e., a branch fetched from a remote repository) with your local branch: $ git merge remotename/branchname # Merges updates made online with your local work Pulling changes from a remote repository. git pull is a convenient shortcut for completing both git fetch and git merge in the same command:

A lot of this is implementation detail—how Git stores information, rather than what information Git stores. As such it's irrelevant: all you know about some other Git repository is what it will show you it has, not where it put it.

Nonetheless, in a typical setup, what's at some remote is another Git repository, just like yours. In fact, from their point of view, you're the remote. They are the real thing, you're the clone. From your point of view, they're the clone and you're the real thing. From an outside observer point of view, your clone and their clone are just two independent Git repositories.

When you run git push, your Git calls up their Git and your Git and their Git have a conversation. This conversation has an end goal, and if we start there and work backwards, it makes more sense. The end goal is that you would like their Git to set one of their names (a branch or tag name in their repository) to remember one specific commit. So you're going to send them a polite request, or a forceful command, of the form: set your refs/heads/master to commit hash ID a123456....

(This name can be anything, including a name that they already have, or a name that they don't have yet. The accept/reject rules depend on the spelling of the name and whether they have it yet, but are up to the receiving Git. Typically receivers don't like names that aren't well=known, e.g., refs/heads/*, refs/tags/*, refs/notes/*. GitHub will reject any attempt to set a refs/pull/* name, for instance.)

They may refuse—even if you send a command rather than a polite request—but if they don't, they must have commit a123456... in their repository. So, as part of this conversation, your Git will offer that commit to them. They will either say I already have that or yes, I'd like that. If commit a123456... depends on any earlier commit(s), by having them as its parents, your Git must now offer their Git those commit(s) as well. If those commits depend on earlier commits, your Git must keep offering them more commits, up until you reach a point where the commits you offer are ones they already have, or you've offered every commit you have (whichever comes first).

To give the other Git a commit, you must also give it every file that's in that commit, unless—again—they already have that version of that file. So some of this also goes into the conversation, although a lot of this is implied: if they don't have commit X but do have commit W then they definitely have all the files that are in W, many of which are probably exactly the same in X so there's no need to send them even though we're sending commit X.

So that's the earliest part of the conversation, after getting your two Gits hooked up: you offer them some commit(s) and file(s), by hash ID. They choose which of these they want and which they already have, and then your Git packages up those objects.

Those objects—the internal Git storage form of commits and trees and files and annotated tags—may live within in your .git/objects/ directory, or may be packed into a pack file in your .git/objects/pack/ sub-directory. But you don't offer them the files, normally. (The exact details depend on the transport protocol.) Instead, typically your Git makes a new pack, of a special "thinned out" type called a thin pack. Your Git sends their Git the thin pack. Their Git fattens it up into a normal pack. If they accept all your commits and files in the end, they probably just put this pack into their .git/objects/pack/ sub-directory, though you'll never know for sure, because in a future conversation with them, your Git and their Git will only talk about objects, by their hash IDs.

(They might also completely tear down the fattened pack and build themselves new packs that are more efficient than just shoving in the fattened version of the thin pack. Again, you'll never really know, unless you can log in to the other machine and poke around in its .git directory.)

Last, once they've accepted whatever they are going to accept, and obeyed your request or command that they set their refs/heads/whatever, they will update their copy of that reference, however they store it: as a file in .git/refs/heads/ perhaps, or as a database entry in some reference database, or perhaps some other thing entirely.

The short answer, then, is that typically, no .git files get copied directly. Instead, the information is re-packaged into a suitable "wire format", delivered that way, and then re-repackaged into a suitable "storage format".

The same holds with git fetch, except that at start of the conversation, your Git has their Git list their names and hash IDs; their Git offers yours objects by hash ID, and yours says want/have to those IDs; and at the end, they never send you any requests or commands. Instead, having obtained their objects, your Git updates your remote-tracking names, which is how your Git remembers what their Git said their refs/heads/master was: that becomes your refs/remotes/origin/master, your memory of their refs/heads/master.

Git Remote, Learn how syncing works in Git with this comprehensive tutorial on git remote and every developer their own copy of the repository, complete with its own local history Records registered through the git remote command are used in conjunction with the git fetch , git push , and git pull commands. GET-URL <​NAME>. How git fetch works with remote branches. To better understand how git fetch works let us discuss how Git organizes and stores commits. Behind the scenes, in the repository's ./.git/objects directory, Git stores all commits, local and remote. Git keeps remote and local branch commits distinctly separate through the use of branch refs.

When we create (using git init) or clone a git repo from upstream, git create .git directory which contains so many information about our local repo with our remote repo which help us to version control our code. If we delete .git directory then we will get fatal: Not a git repository (or any of the parent directories): .git

I don't think we have .git directory in remote repo, i think .git directory is found only in our local repo which is used to track branch, commits, user config, adding remotes repos ... We only have .gitignore file in the remote repo, if we add it for ignoring files...

If you look inside the .git directory you can see following files and folders...

├── HEAD
├── branches
├── config
├── description
├── hooks
│ ├── pre-commit.sample
│ ├── pre-push.sample
│ └── ...
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
 ├── heads
 └── tags

git-fetch Documentation, git-fetch - Download objects and refs from another repository By default, Git will report, to the server, commits reachable from all local refs to find common commits in an Before fetching, remove any local tags that no longer exist on the remote if git clone, git fetch and git pull, but not git push, will also accept a suitable  Git adds your commits to an existing branch on the remote or creates a new branch with the same commits as your local branch. Git makes sure that pushed changes are consistent with the remote branch. Others can pull your commits and merge them into their own local copy of the branch.

Embedded Software for the IoT, Command Meaning git init Creates “.git” repo dir in the root of the workdir it is Use -m for message git checkout Get files from local repo to workdir git push Local repo content pushed to remote repo git fetch Remote repo content pulled to local Essentially a very clean way to merge git stash Copy workdir to a stack called  git remote add origin /path/to/origin.git git add git commit -m "initial commit" git push origin master i) the -f option no longer exists ii) as I noob I was sweating, it looked for a while like I could only push files (like individual scripts) rather than a folder

Remote Branches, Remote branches act as bookmarks to remind you where the branches on You can use this format to push a local branch into a remote branch that is named differently. The next time one of your collaborators fetches from the server, they will get a git fetch origin remote: Counting objects: 7, done. remote: Compressing  Git is a free and open-source distributed version control system designed to handle everything from small to very large projects with speed and efficiency. Git relies on the basis of distributed development of software where more than one developer may have access to the source code of a specific application and can modify changes to it which may be seen by other developers.

git-pull Documentation, git-pull - Fetch from and integrate with another repository or a local branch A---​B---C master on origin / D---E---F---G master ^ origin/master in your repository It is generally best to get any local changes in working order before pulling or stash point at objects that are downloaded from the remote repository are fetched  It’s the counterpart to git fetch but whereas fetching imports commits to local branches, pushing exports commits to remote branches. Remote branches are configured using the git remote command.

Comments
  • Thanks! What happens if I have created a local branch named X, and there is a different branch named X on the remote, and this branch gets fetched?
  • @EvanAad - Well, what happens depends on your refspec. By default when you fetch the local branch is left unchanged, and a remote tracking ref is created (e.g. you have branch x, you fetch from remote named origin, it might give you refs/remotes/origin/x). Then you decide what makes sense. Is this really a separate branch? Should I merge them together? Something else...?
  • And when I push, do my branch names get passed on to the remote?
  • @EvanAad - Again it depends on your configuration, and possibly also on the exact command you use. Normally if you push branch x you will be trying to update branch x on the remote, but this will not work (without taking additional measures to make it work) if the update woudl cause any commits to be removed from the history of x
  • Thanks! Why do I need to mention a ref in my push command (as in your example: set your refs/heads/master to commit hash ID a123456)? Why can't I just say: add commit hash ID a123456 to your repository?
  • Git finds commits by refname. The name finds a commit, which makes the commit reachable. That commit has its predecessor(s), which makes those commits reachable too, and those commits have their parents which are now reachable, etc., but the process kicks off from the set of refs that any Git has. So if they added a123456 but had no other name or commit that finds a123456, their cleanup process would find that nobody calls to keep a123456 around, and would remove it. (This cleanup is git gc. See also think-like-a-git.net)
  • I don't think I made myself clear. Allow me to rephrase. Suppose the remote has a branch named b and suppose the last commit on this branch has hash ID 1. Now I have cloned this repository to my local computer, and have added a commit with hash ID 2 whose parent is the commit whose hash ID is 1. Now I'd like to push the new commit to the remote. Why do I have to specify in my command set your refs/heads/b to commit hash ID 2? Why can't I simply say: add commit hash ID 2 to your repository? The new commit can be reachable on the remote via ref b, no?
  • Ah: the answer is no. A branch name like refs/heads/b contains just one hash ID. That hash ID is defined as the last commit in the branch. So for the branch to contain the new commit, the reference must be overwritten with the new hash ID. That makes the new commit reachable, keeps the old one reachable, and adds the new commit to their notion of "branch B", all in one go.
  • And can't git figure out on its own that if I add commit 2 whose parent is commit 1, the latter of which exists on the remote and is pointed at by branch b, to update branch b to point to 2?
  • So where do remotes keep branch information, for instance?
  • @EvanAad, found git uses DGit on their side to manage the repos.. this link might help some.. githubengineering.com/introducing-dgit .