09. Remotes - ivomac/GitBasics GitHub Wiki

🐻 Bare Repository

  • A bare git repository is one without a checked-out workdir.
  • In a typical project folder, you will have:
project_name/       # 📂 Working Directory
├── .git/           # 🪣 Git Repository
│   ├── HEAD
│   ├── index
│   └── ...
│   ...
└── README.md
  • 🐻 A bare git repository holds only the contents of .git:
project_name.git/   # 🪣 Bare Git Repository
├── HEAD
├── index
└── ...
  • 🌐 Bare git repositories are typically used in servers which host the repository online.
    • 🔄 Collaborators/users can use such repositories to synchronize changes.

🌐 Remotes

  • 🌍 A remote is a reference in the local repository to a remote repository.
  • 🏷️ Each remote has a unique name and a location.
    • 🐻 The remote is usually a bare repository in a server, and the location is a URL.
    • 📝 The most common remote name (when there is only one) is origin.
Local Repository                     |  Repository in a remote location (origin)
                                     |
                                     |            G ◄─ H ◄ test
                                     |            ▼
A ◄─ B ◄─ C ◄ main                   |  A ◄─ B ◄─ C ◄ main
     ▲                               |       ▲
     D ◄─ E ◄─ F ◄ feature ◄ HEAD    |       D ◄─ E ◄ feature

🔗 Tracking Branches

  • 🏷️ Branches are local to each repository, but may be linked between repositories.
  • 👉 A tracking branch is a local copy/reference of a branch in a remote.
  • 📍 They show where the remote branch was the last time it was synchronized.
    • 🧭 Tracking branches are named as remote_name/branch_name.
      • 🏠 feature refers to the local branch pointing to F.
      • 🌐 origin/feature refers to the branch in the origin repo pointing to E.

📥 Fetching

  • 📡 We update the local tracking branches by fetching from a remote.
  • 📥 Fetching downloads new commits from the remote repository if needed.
  • 🛡️ It does not modify your local branches or your working directory.

📥 Fetch Scenario

  • 🔄 In the example above, if we fetch all branches from origin:
Local Repository                     |  Repository in a remote location (origin)
                                     |
          G ◄─ H ◄ origin/test       |            G ◄─ H ◄ test
          ▼                          |            ▼
A ◄─ B ◄─ C ◄ main, origin/main      |  A ◄─ B ◄─ C ◄ main
     ▲                               |       ▲
     D ◄─ E ◄─ F ◄ feature ◄ HEAD    |       D ◄─ E ◄ feature
          ▲                          |
    origin/feature                   |
  • ⚖️ Local main is in sync with origin/main.
  • ⬆️ Local feature is one commit ahead of origin/feature.
  • 🆕 New commits G, H, from branch test in origin are now available locally in origin/test.
  • 🏠 Local branches (main, feature) remain unchanged.
  • 🤝 With tracking branches, we see the local and (last seen) remote state in the local graph.

⬆️ Upstream Branch

  • 🔗 The upstream branch is the default tracking branch that a local branch should sync with.
  • ⛳ Commonly, you want to sync a local branch with the remote branch of the same name:
    • main with origin/main, feature with origin/feature.
  • 🟰 The upstream branch is almost always set to the branch of the same name in the remote.
  • 🤷‍♂️ You could always sync a local branch with any remote branch... but why?

📤 Pushing

  • Pushing uploads your local commits to the remote repository.
  • ⬆️ It updates a remote branch to match your local branch.
  • ⚠️ Pushing can fail if the remote has changes you don't have locally.

📤 Push Scenario

Local Repository                     |  Repository in a remote location (origin)
                                     |
A ◄─ B ◄─ C ◄─ D ◄ main ◄ HEAD       |  A ◄─ B ◄─ C ◄ main
          ▲                          |
      origin/main                    |
  • After a successful push:
Local Repository                     |  Repository in a remote location (origin)
                                     |
A ◄─ B ◄─ C ◄─ D ◄ main ◄ HEAD       |  A ◄─ B ◄─ C ◄─ D ◄ main
               ▲                     |
           origin/main               |
  • ✅ Remote main now includes commit D
  • 🔄 Local tracking branch updated automatically

⚔️ Push Conflicts

  • 🚫 Pushing fails if the remote has commits you don't have:
Local Repository                     |  Repository in a remote location (origin)
                                     |
A ◄─ B ◄─ C ◄─ D ◄ main ◄ HEAD       |  A ◄─ B ◄─ C ◄─ E ◄ main
          ▲                          |
      origin/main                    |
  • 🔄 To properly synchronize local and remote main, we could first fetch:
Local Repository                     |  Repository in a remote location (origin)
                                     |
A ◄─ B ◄─ C ◄─ D ◄ main ◄ HEAD       |  A ◄─ B ◄─ C ◄─ E ◄ main
          ▲                          |
          E ◄ origin/main            |
  • ↕️ We see that main is 1 commit behind, 1 commit ahead of origin/main (⬇️1⬆️1).
  • ✏️ We then rebase main on top of origin/main:
Local Repository                     |  Repository in a remote location (origin)
                                     |
A ◄─ B ◄─ C ◄─ E ◄─ F ◄ main ◄ HEAD  |  A ◄─ B ◄─ C ◄─ E ◄ main
               ▲                     |
           origin/main               |

  • 📝 F is the rebased D commit.
  • 🟢 We can now push without conflicts:
Local Repository                     |  Repository in a remote location (origin)
                                     |
A ◄─ B ◄─ C ◄─ E ◄─ F ◄ main ◄ HEAD  |  A ◄─ B ◄─ C ◄─ E ◄─ F ◄ main
                    ▲                |
                origin/main          |

🔄 Pull: Fetch + Merge

  • Pulling updates the current local branch with the changes in its upstream branch.
  • It combines fetching and rebasing/merging in one operation:
    • 📥 First, it fetches the upstream tracking branch of the current branch (origin/main for main).
    • ⬆️ If the upstream branch is ahead of the local branch, we move the local branch forward.
      • This is known as fast-forward.
    • 🔀 If the branches have diverged, the default is to merge local and tracking branches.
    • 📝 The default can be changed to rebase local branch on top of tracking branch instead.

🔀 Merge/Pull Requests

  • 🌐 Merge/Pull Requests are a feature provided by Git hosts like GitHub and GitLab.
  • 🤝 Merge/Pull Requests are requests to push/merge the local changes to the remote repository.
  • 👀 The changes in the request can then be reviewed before the merge is approved.
  • 📥 The merging happens in the remote. Fetch/Pull after to update locally.
  • 📋 They provide a structured way to discuss, review, and integrate code changes.
  • 🏷️ Pull Request (GitHub) 🟰 Merge Request (GitLab/BitBucket)