Chapter 8: Pushing, Fetching, and Pulling with Git
Chapter Objectives
By the end of this chapter, you will be able to:
- Fetch changes from a remote repository using
git fetch
and understand how it updates remote-tracking branches. - Clearly distinguish between
git fetch
andgit pull
. - Pull changes from a remote repository using
git pull
, understanding it as a combination of fetch and merge. - Push your local commits to a remote repository using
git push
. - Push new local branches to a remote and set them up for tracking.
- Understand and set upstream branches for your local branches using
git push -u
orgit branch --set-upstream-to
. - Recognize the implications and dangers of force pushing (
--force
,--force-with-lease
) and when it might be considered. - Understand the concept of tracking branches and their relationship with remote branches.
Introduction
In Chapter 7, you learned how to set up connections to remote repositories by cloning existing projects or adding remotes to your local projects. These connections are like pathways, but no data has been exchanged yet beyond the initial clone. Now, it’s time to learn how to use these pathways to synchronize your work. How do you get the latest changes made by your collaborators? How do you share your own brilliant contributions with them?
This chapter delves into the core Git commands that manage the flow of data between your local repository and its remote counterparts: git fetch
, git pull
, and git push
. You’ll learn how to download changes from a remote without immediately merging them, how to integrate remote changes into your local work, and how to upload your local commits to share them. We’ll also cover the important concept of tracking branches and the cautious use of force pushing. Mastering these operations is fundamental to effective collaboration and maintaining an up-to-date project.
Theory
Understanding the Synchronization Process
Working with remote repositories involves a three-step conceptual process for staying synchronized:
- Fetch: Download new data and commit history from the remote repository. This updates your local copies of remote branches (remote-tracking branches) but doesn’t change your own local working branches.
- Merge (or Rebase): Integrate the fetched changes into your local working branches.
- Push: Upload your local commits (that are not yet on the remote) to the remote repository.
Git provides commands that handle these steps either separately or in combination.
Fetching Changes: git fetch
The git fetch <remote-name>
command (e.g., git fetch origin
) communicates with the specified remote repository and downloads all the data (commits, files, tags, etc.) that exist on the remote but are not currently in your local repository.
Crucially, git fetch
:
- Updates your remote-tracking branches: For example, if the remote
origin
has amain
branch,git fetch origin
will update your localorigin/main
pointer to match the current state ofmain
on the remote. - Does NOT modify your local working branches: Your
main
branch (or any other local branch you’re working on) remains untouched. Your working directory also remains unchanged.
Fetching is a safe way to see what new work has been done on the remote without immediately integrating it into your own local branches. After fetching, you can inspect the changes on the remote-tracking branches (e.g., git log origin/main
) and then decide how and when to merge them.
%%{ init: { "theme": "base", "themeVariables": { "primaryColor": "#EDE9FE", /* Default commit node color */ "primaryTextColor": "#5B21B6", /* Text on commits */ "primaryBorderColor": "#5B21B6", "lineColor": "#5B21B6", /* Default branch line color */ "textColor": "#1F2937", /* General text */ "fontSize": "13px", "fontFamily": "Open Sans" } } }%% gitGraph BT: commit id: "C1" tag: "Initial commit" commit id: "C2" tag: "main (HEAD) - Before & After Fetch" branch "origin/main" checkout "origin/main" commit id: "C2-orig" tag: "Before Fetch: origin/main @ C2" commit id: "C3" tag: "After Fetch: origin/main @ C3" type: HIGHLIGHT %% Diagram Explanation: %% Before Fetch: %% - Local 'main' (HEAD) is at C2. %% - Local 'origin/main' (remote-tracking branch) is also at C2. %% - Remote 'origin' has its 'main' branch at C3 (C2 -> C3). %% %% After 'git fetch origin': %% - Commit C3 is downloaded to the local repository. %% - Local 'origin/main' is updated to point to C3. %% - Local 'main' (HEAD) remains untouched at C2. %% - The working directory is unchanged.
In this graph:
- Initial State:
- We start with commit C1 as the initial commit
- The local
main
branch is at commit C2 - The local
origin/main
branch (remote-tracking branch) is also at C2 - (Not directly shown, but implied) The remote repository’s
main
branch has advanced to C3
- After Running
git fetch origin
:- The local
main
branch remains at C2 (unchanged) - The local
origin/main
is updated to point to C3 (highlighted) - Commit C3 is downloaded into your local repository
- The local
Remote-Tracking Branches Revisited
As introduced in Chapter 7, remote-tracking branches are local read-only pointers to the state of branches on a remote repository. They are in the form <remote-name>/<branch-name>
(e.g., origin/main
, upstream/develop
).
- When you
git fetch
, these pointers are updated. - You can check out a remote-tracking branch directly (e.g.,
git switch origin/main
), which puts you in a “detached HEAD” state, useful for inspection. - They serve as a reference point for seeing what’s changed on the remote (
git diff main origin/main
) before you decide to merge.
Pulling Changes: git pull
The git pull <remote-name> <branch-name>
command (e.g., git pull origin main
) is essentially a shortcut for two other commands:
git fetch <remote-name>
(orgit fetch <remote-name> <branch-name>
)git merge FETCH_HEAD
(orgit merge <remote-name>/<branch-name>
into your current local branch, assuming it’s set up to track the remote branch).
FETCH_HEAD
is a special short-lived reference that points to the tip of the branch just fetched from the remote.
So, git pull
downloads the changes from the remote and immediately tries to merge them into your currently checked-out local branch.
- If your local branch has not diverged from the remote branch, the merge will typically be a fast-forward.
- If your local branch has diverged (i.e., you have local commits that the remote doesn’t, and the remote has commits you don’t),
git pull
will create a merge commit (assuming no conflicts). - If there are merge conflicts, the pull process will pause, and you’ll need to resolve them just like with a local merge (see Chapter 6).
git pull vs. git fetch + git merge:
While git pull is convenient, some developers prefer to use git fetch followed by a manual git merge (or git rebase, covered later). This gives them a chance to inspect the fetched changes (git log origin/main) before integrating them. For beginners, git pull is often simpler to understand initially.
%%{ init: { "theme": "base", "themeVariables": { "primaryColor": "#EDE9FE", "primaryTextColor": "#5B21B6", "primaryBorderColor": "#5B21B6", "lineColor": "#5B21B6", "textColor": "#1F2937", "fontSize": "13px" } } }%% gitGraph BT: %% Scenario 1: Fast-Forward Pull commit commit id: "S1C1" tag: "Initial commit" commit id: "S1C2" tag: "Local & remote at same point" branch originmain commit id: "S1C3" tag: "Remote moved ahead" checkout main merge originmain tag: "After pull: Fast-forward" %% Scenario 2: Merge Pull commit id: "S2C0" tag: "New scenario start" commit id: "S2C1" tag: "Common base" branch feature commit id: "S2C3" tag: "Local changes" checkout main commit id: "S2C2" tag: "Remote changes" checkout feature merge main tag: "After pull: Merge commit"
Diagram Explanation:
Scenario 1: Fast-Forward Pull
- Local ‘main’ is at S1_C2. Remote ‘origin/main’ has a new commit S1_C3 (S1_C2 -> S1_C3_remote).
- ‘git pull origin main’ (while on local ‘main’):
- Fetches S1_C3_remote, updates local ‘origin/main’ to S1_C3_remote.
- Merges ‘origin/main’ into local ‘main’. Since local ‘main’ (S1_C2) is an ancestor, it’s a fast-forward.
- Local ‘main’ (HEAD) moves to S1_C3_remote.
Scenario 2: Merge Pull
- Common ancestor is S2_C1.
- Local ‘main’ has a new commit S2_C3_local (S2_C1 -> S2_C3_local). HEAD is on ‘main’.
- Remote ‘origin/main’ has a different new commit S2_C2_remote (S2_C1 -> S2_C2_remote).
- ‘git pull origin main’ (while on local ‘main’):
- Fetches S2_C2_remote, updates local ‘origin/main’ to S2_C2_remote.
- Merges ‘origin/main’ (at S2_C2_remote) into local ‘main’ (at S2_C3_local).
- Since histories diverged, a new merge commit ‘S2_M’ is created on local ‘main’. ‘S2_M’ has parents S2_C3_local and S2_C2_remote. HEAD moves to ‘S2_M’.
Pushing Changes: git push
Once you have made local commits and are ready to share them with others, you use the git push <remote-name> <branch-name>
command (e.g., git push origin main
). This command uploads your local branch’s commits (and associated Git objects) to the specified remote repository, updating the corresponding branch on the remote.
Key points about git push
:
- Non-Fast-Forward Rejection: By default, Git will reject a push if it’s not a “fast-forward” update on the remote branch. This means if someone else has pushed changes to the same remote branch since you last pulled, your push will be rejected. This prevents you from accidentally overwriting their commits. The solution is to
git pull
(orfetch
andmerge/rebase
) the remote changes first, integrate them with your local work, and then try pushing again. - Pushing a Local Branch to a Remote Branch:The full syntax is git push
: . - If
<remote-branch-name>
is omitted, Git tries to push<local-branch-name>
to a remote branch with the same name. - If
<remote-branch-name>
doesn’t exist on the remote, it will be created.
- If
- Pushing a New Local Branch:To push a local branch that doesn’t yet exist on the remote, you can use:git push origin my-new-feature (this will create my-new-feature on origin).
Tracking Branches and Upstream Configuration
A local branch can be configured to track a remote branch. This means Git knows that your local branch has a direct relationship with a specific branch on a specific remote. This relationship is often called an upstream branch.
When a local branch is tracking a remote branch:
git pull
(with no arguments, while on that local branch) will automatically know to fetch from the tracked remote branch and merge it.git push
(with no arguments, while on that local branch) will automatically know to push to the tracked remote branch.git status
will tell you if your local branch is ahead of, behind, or has diverged from its upstream branch.
Setting an Upstream Branch:
The most common way to set an upstream branch is when you first push a new local branch:
git push -u <remote-name> <local-branch-name>
The -u flag (short for –set-upstream) does two things:
- Pushes the local branch to the remote.
- Sets up the tracking relationship between your local branch and the newly created remote branch.
You can also set or change an upstream branch for an existing local branch using:
git branch –set-upstream-to=<remote-name>/<remote-branch-name> <local-branch-name>
Example: git branch –set-upstream-to=origin/main main
Force Pushing: git push --force
and git push --force-with-lease
Sometimes, you might want to overwrite the history on a remote branch. This is generally strongly discouraged for branches that other people are using or have pulled from, as it rewrites history and can cause major problems for collaborators.
- git push –force <remote-name> <branch-name> (or git push -f …)This command forces your local version of the branch to overwrite the remote version, even if it’s not a fast-forward. It discards any commits on the remote branch that you don’t have locally.
- git push –force-with-lease <remote-name> <branch-name>This is a safer alternative to –force. It will only force the push if the remote branch is in the state you expect it to be (i.e., if no one else has pushed to it since you last fetched). If someone else has pushed, –force-with-lease will fail, preventing you from accidentally overwriting their work. It’s generally preferred over plain –force.
Warning: Force pushing should be used with extreme caution. It’s acceptable on your own private feature branches that no one else uses, or in specific, coordinated situations within a team (e.g., after rebasing a feature branch before it’s merged into a shared branch). Never force push to shared branches like
main
ordevelop
in a collaborative project unless you are absolutely certain of the consequences and have coordinated with your team.
Command | Description | Key Actions & Notes |
---|---|---|
git fetch <remote> | Downloads commits, files, and refs from a remote repository into your local .git directory. | – Updates remote-tracking branches (e.g., origin/main). – Does NOT modify your local working branches or working directory. – Safe way to see remote changes before integrating. |
git pull <remote> <branch> | Fetches changes from the specified remote branch and immediately tries to merge them into your current local branch. | – Essentially git fetch followed by git merge FETCH_HEAD. – Can result in a fast-forward or a merge commit. – May lead to merge conflicts if histories have diverged. |
git push <remote> <branch> | Uploads your local branch’s commits to the specified branch on the remote repository. | – By default, rejects non-fast-forward pushes (remote has changes you don’t). – Requires you to git pull first if rejected. |
git push -u <remote> <branch> (or –set-upstream) |
Pushes the local branch to the remote and sets up tracking: your local branch will track the remote branch. | – Useful for pushing a new local branch for the first time. – Enables argument-less git pull and git push on that branch later. |
git branch –set-upstream-to=<remote>/<r_branch> <l_branch> | Sets an existing local branch (<l_branch>) to track a specific remote-tracking branch (<remote>/<r_branch>). | Alternative to git push -u for already existing branches or to change tracking. |
git push –force <remote> <branch> (or -f) |
Forces the push, overwriting the remote branch’s history with your local branch’s history. | – Highly dangerous on shared branches. – Can discard other collaborators’ work. – Use with extreme caution. |
git push –force-with-lease <remote> <branch> | Safer force push. Only overwrites the remote if its state matches what you last fetched. Fails if someone else pushed in the meantime. | – Preferred over –force if a forced update is absolutely necessary. |
Practical Examples
Setup:
For these examples, we’ll simulate a remote repository using a local bare repository and a separate “collaborator” clone.
1. Create a “central” bare repository (simulating a remote server like GitHub):
# Navigate to a directory where you keep projects (e.g., ~/Projects)
mkdir central-server-repo
cd central-server-repo
git init --bare project-alpha.git # This is our "remote"
cd .. # Go back up
A bare repository (--bare
) is just the .git
directory contents, with no working directory.
2. Developer 1: Clone the “remote” and make initial commits:
git clone central-server-repo/project-alpha.git dev1-project-alpha
cd dev1-project-alpha
git config user.name "Developer One" # Configure user for this repo
git config user.email "dev1@example.com"
echo "# Project Alpha" > README.md
git add README.md
git commit -m "Initial commit by Dev1"
echo "Version 1.0" >> README.md
git add README.md
git commit -m "Dev1 adds version to README"
# Push initial commits to the "remote"
git push origin main # 'origin' was set up by clone
cd ..
3. Developer 2: Clone the “remote”:
git clone central-server-repo/project-alpha.git dev2-project-alpha
cd dev2-project-alpha
git config user.name "Developer Two" # Configure user
git config user.email "dev2@example.com"
# Dev2 now has the two commits made by Dev1
cd ..
Now we have central-server-repo/project-alpha.git
as our shared remote, and dev1-project-alpha
and dev2-project-alpha
as two local clones.
1. Fetching Changes (git fetch
)
Let’s have Developer 1 make another change and push it. Then Developer 2 will fetch it.
1. Developer 1 makes and pushes a change:
cd dev1-project-alpha
echo "Developed by Awesome Inc." >> README.md
git add README.md
git commit -m "Dev1 adds company info"
git push origin main
cd ..
2. Developer 2 fetches the changes:
cd dev2-project-alpha
git fetch origin
Expected Output:
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 290 bytes | 290.00 KiB/s, done.
From ../central-server-repo/project-alpha
<hash_old_main>..<hash_new_main> main -> origin/main
(Hashes will vary.)
3. Developer 2 inspects:
At this point, Dev2’s local main branch is still at the “Dev1 adds version to README” commit. The new commit (“Dev1 adds company info”) is downloaded but not yet merged.
git log main --oneline # Shows local main history
git log origin/main --oneline # Shows remote-tracking branch history
git log main..origin/main --oneline # Shows commits on origin/main but not on local main
Expected Output (for main..origin/main
):
<hash_company_info_commit> (origin/main) Dev1 adds company info
This shows the commit that origin/main
has, which main
doesn’t. Dev2’s working directory is unchanged.
2. Pulling Changes (git pull
)
Now, Developer 2 will integrate the fetched changes using git pull
. (Alternatively, after fetching, Dev2 could git merge origin/main
).
1. Developer 2 pulls (assuming they are on their main
branch):#
# cd dev2-project-alpha (if not already there)
# git switch main (ensure on main branch)
git pull origin main # Or simply 'git pull' if main is tracking origin/main
Since Dev2’s main
hasn’t diverged from origin/main
‘s history (it’s a direct ancestor), this will be a fast-forward merge.
Expected Output:
From ../central-server-repo/project-alpha
* branch main -> FETCH_HEAD
Updating <hash_old_main>..<hash_new_main>
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
2. Developer 2 checks log and README.md:
git log –oneline will now show the “Dev1 adds company info” commit on the local main branch. README.md will contain the new line.
3. Pushing Local Changes (git push
)
Developer 2 makes a local change and pushes it.
1. Developer 2 makes a local commit:
# cd dev2-project-alpha
echo "Copyright 2025" >> README.md
git add README.md
git commit -m "Dev2 adds copyright"
2. Developer 2 pushes to origin
:
git push origin main
Expected Output:
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to X threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 300 bytes | 300.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To ../central-server-repo/project-alpha.git
<hash_prev_remote_main>..<hash_new_remote_main> main -> main
4. Handling a Non-Fast-Forward Push Rejection
Now, let’s simulate a scenario where Dev1 tries to push, but Dev2 has already pushed changes.
1. Developer 1 (unaware of Dev2’s last push) makes a local commit:
cd ../dev1-project-alpha # Switch to Dev1's repo
echo "Project Alpha is super cool." >> notes.txt
git add notes.txt
git commit -m "Dev1 adds some notes"
2. Developer 1 tries to push:
git push origin main
Expected Output (Error!):
To ../central-server-repo/project-alpha.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to '../central-server-repo/project-alpha.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
The push is rejected because origin/main
has Dev2’s “adds copyright” commit, which Dev1 doesn’t have locally. Dev1’s local main
and origin/main
have diverged.
3. Developer 1 pulls the changes (this will cause a merge):
git pull origin main
Git will fetch Dev2’s commit and then try to merge it with Dev1’s “adds some notes” commit. Since they changed different files (README.md vs notes.txt), it should merge cleanly, creating a merge commit. An editor will open for the merge commit message.
Expected Output (before editor for merge commit):
From ../central-server-repo/project-alpha
* branch main -> FETCH_HEAD
Merge made by the 'recursive' strategy.
README.md | 1 +
1 file changed, 1 insertion(+)
(Save and close the editor with the default merge message)
4. Developer 1 can now push their merged history:
git push origin main
This time it should succeed.
5. Pushing a New Local Branch and Setting Upstream (git push -u
)
1. Developer 1 creates a new local feature branch and makes a commit:
# cd ../dev1-project-alpha
git switch -c feature/new-styling
echo "body { font-family: sans-serif; }" > styles.css
git add styles.css
git commit -m "Add basic styles for feature"
2. Developer 1 pushes the new branch and sets it to track:
git push -u origin feature/new-styling
Expected Output:
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to X threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 320 bytes | 320.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To ../central-server-repo/project-alpha.git
* [new branch] feature/new-styling -> feature/new-styling
Branch 'feature/new-styling' set up to track remote branch 'feature/new-styling' from 'origin'.
Now, feature/new-styling
exists on the origin
remote, and Dev1’s local feature/new-styling
branch is tracking origin/feature/new-styling
. Dev1 can now use git pull
and git push
on this branch without specifying origin feature/new-styling
each time.
OS-Specific Notes
- Authentication for Push/Fetch/Pull:
- As covered in Chapter 7, how you authenticate with the remote server (SSH keys, PATs via HTTPS, credential managers) is the main OS-dependent aspect. Ensure this is set up correctly for smooth operations. If
git push
orgit pull
hangs or fails asking for credentials repeatedly, it’s likely an authentication setup issue.
- As covered in Chapter 7, how you authenticate with the remote server (SSH keys, PATs via HTTPS, credential managers) is the main OS-dependent aspect. Ensure this is set up correctly for smooth operations. If
- Network Connectivity:
git fetch
,pull
, andpush
all require network access to the remote server. Firewalls, proxies, or VPN configurations can affect connectivity. If you experience timeouts or “host not found” errors:- Verify your internet connection.
- Check if you can access the remote server’s domain (e.g., ping
github.com
). - Ensure Git is configured for your proxy if you use one (see Chapter 7 OS-Specific Notes).
- Case Sensitivity of Branch Names:
- While Git itself can handle case-sensitive branch names, file systems on different OSs behave differently (macOS/Windows often case-insensitive but preserving, Linux case-sensitive). It’s best practice to use all lowercase for branch names or be strictly consistent with casing to avoid confusion or issues if collaborators are on different OSs, especially when pushing and fetching branch names.
Common Mistakes & Troubleshooting Tips
Git Issue / Error | Symptom(s) | Troubleshooting / Solution |
---|---|---|
Push Rejected (Non-Fast-Forward) | Error: ! [rejected] … (fetch first) or (non-fast-forward). | Solution: Remote has new changes. Run git pull (or git fetch then git merge/rebase), resolve conflicts if any, then git push. |
Accidental Force Push | Collaborators’ history diverges, commits lost on remote. | Solution: Avoid git push –force on shared branches. Use –force-with-lease if necessary. If accidental, coordinate with team for recovery (may involve git reflog, advanced). |
Merge Conflicts during git pull | git pull stops with CONFLICT messages. git status shows unmerged paths. | Solution: Resolve conflicts (edit files, remove markers, git add), then git commit to finalize merge. Or, git merge –abort. |
Committing on origin/main (Detached HEAD) | Commits made in “detached HEAD” state after git switch origin/main. Commits may be lost. | Solution: After git fetch, switch to local branch (git switch main) then git merge origin/main. If commits made in detached HEAD, create a branch from it: git branch temp-work HEAD. |
Pushing to wrong remote/branch | Code ends up in an unintended location. | Solution: Be explicit: git push <remote> <branch>. Check with git remote -v and git branch -vv. If pushed to wrong remote branch, may need to delete/revert on remote. |
Authentication Failure | Repeated password/token prompts for HTTPS; Permission denied (publickey) for SSH. | Solution: Verify credentials/tokens for HTTPS. For SSH, ensure public key is on server and SSH agent is configured. (See Ch 7). |
“Everything up-to-date” on git pull/push | Command runs but reports no changes, even if you expect some. | Solution: For pull: local branch may already have all remote changes. For push: local branch may have no new commits compared to its upstream. Check git status and git log origin/<branch>..<local-branch>. |
Exercises
Use the simulated remote setup (central-server-repo/project-alpha.git
, dev1-project-alpha
, dev2-project-alpha
) from the Practical Examples section.
- Fetch, Inspect, and Merge:
- In
dev1-project-alpha
, create a new fileideas.txt
with some content and commit it tomain
. Push this change toorigin
. - In
dev2-project-alpha
, rungit fetch origin
. - Use
git log
to comparedev2-project-alpha
‘s localmain
withorigin/main
. What’s different? - Merge the changes from
origin/main
into your localmain
branch indev2-project-alpha
. Verifyideas.txt
is now present.
- In
- Pushing a New Branch and Tracking:
- In
dev1-project-alpha
, create a new local branch calledfeature/user-profiles
. - Add a file
profiles.txt
with some initial content and commit it to this new branch. - Push
feature/user-profiles
toorigin
and set it up to track the remote branch in a single command. - In
dev2-project-alpha
, fetch fromorigin
. - Use
git branch -r
to see the remote-tracking branches. Do you seeorigin/feature/user-profiles
? - (Optional) In
dev2-project-alpha
, create a localfeature/user-profiles
branch that tracksorigin/feature/user-profiles
(e.g.,git switch -c feature/user-profiles origin/feature/user-profiles
).
- In
- Simulate and Resolve Push Rejection:
- In
dev1-project-alpha
, on themain
branch, modifyREADME.md
and commit the change. Do not push yet. - In
dev2-project-alpha
, on themain
branch, modifyREADME.md
(a different change than Dev1) and commit. Push this change fromdev2-project-alpha
toorigin main
. - Now, back in
dev1-project-alpha
, try togit push origin main
. You should get a rejection. - In
dev1-project-alpha
, perform agit pull origin main
. This should result in a merge conflict inREADME.md
because both developers changed it. - Resolve the conflict in
README.md
indev1-project-alpha
. - Complete the merge by adding the resolved file and committing.
- Now, push from
dev1-project-alpha
again. It should succeed.
- In
Summary
Synchronizing your local work with remote repositories is key to collaboration and backup in Git:
git fetch <remote>
: Downloads new data and updates remote-tracking branches (e.g.,origin/main
) from the specified remote. It does not change your local working branches.- Remote-tracking branches (like
origin/main
) are local read-only references to the state of branches on remotes. git pull <remote> <branch>
: Combinesgit fetch
(to get changes) andgit merge
(to integrate them into your current local branch). Can lead to fast-forwards or merge commits (and potential conflicts).git push <remote> <branch>
: Uploads your local branch’s commits to the specified branch on the remote repository.- Pushes are rejected if they are not fast-forwards on the remote, requiring you to pull first.
- Upstream/Tracking Branches: Local branches can “track” remote branches.
- Set up with
git push -u <remote> <branch>
(pushes and sets upstream) orgit branch --set-upstream-to=<remote>/<branch> <local-branch>
. - Allows for argument-less
git pull
andgit push
on that branch. git status
shows how your local branch relates to its upstream.
- Set up with
- Force Pushing:
git push --force
: Overwrites remote history. Highly dangerous on shared branches.git push --force-with-lease
: Safer alternative, checks if remote was updated by someone else.- Use with extreme caution, primarily on private branches if history rewriting is necessary.
These commands form the backbone of collaborative Git workflows, allowing teams to share and integrate their work effectively.
Further Reading
- Pro Git Book:
- Chapter 3.5 Git Branching – Remote Branches: https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches
- Official Git Documentation:
git-fetch(1)
: https://git-scm.com/docs/git-fetchgit-pull(1)
: https://git-scm.com/docs/git-pullgit-push(1)
: https://git-scm.com/docs/git-push
- Atlassian Git Tutorials:
