git note
I like starting a git repository separately, once locally and once remotely. This has given me some headaches especially when git doesn’t kindly lay out the rest of the process for me. I decided to write this note once and for all so I don’t repeat the same mistake!
When I create a git repository locally by git init and do something like git add ., I am creating a history with this repository.
Then if I go create a remote repository and do something extra like adding a .gitignore file, I am creating a separate history with the corresponding remote repository.
These two histories have **no common ancestor`, so Git refuses to merge them automatically.
Remote: [initial commit: .gitignore]
↑
(no connection)
↓
Local: [add all: your files]
The safest approach to merge them is with --allow-unrelated-histories as in
git pull origin master --allow-unrelated-histories
This tells Git “I know these histories are unrelated. Merge them anyway.” Git will create a merge commit combinding both. If there is a conflict on .gitignore because you also have one locally, then just resolve it and commit. Then push by
git push -u origin master
If I do just git pull then git doesn’t know my strategy for reconciling divergent branches.
- merge (
--no-rebase) creates a merge commit joining the two histories - rebase replays my local commits on top of the remote commit
- fast-forward only fails if histories have diverged
Let’s say this is the shared history before anyone diverged:
A --- B (common starting point, both local and remote agree here)
scenario 1: Only the remote has new work (I have nothing new locally)**
Remote: A --- B --- C --- D
Local: A --- B
All three strategies suceed here, and they all produce the same result. My local just moves forward to D. This is called a fast-forward: no merging needed, just sliding my pointer ahead.
Scenario 2: Both sides have new work (diverged)
Remote: A --- B --- C
Local: A --- B --- X --- Y
-
The merge (
--no-rebase) strategy creates a new merge commitMthat ties the two histories together:A --- B --- C ----------- M \ / X --- Y ---History is preserved exactly as it happened. The downside is it adds a merge commit that can clutter the log.
-
The rebase strategy takes my local commits (X, Y) and replays them on top of the remote’s tip (C), rewriting them as new commits (X’, Y’):
A --- B --- C --- X' --- Y'History looks linear and clean, as if I had started my work after C. The downside: it rewrites commit hashes, which causes problems if others are already working from X and Y.
-
The fast-foward only strategy refuses to do anything because the histories have diverged. It only works in Scenario 1 where no rewriting or merging is needed. I get the
fatal: Not possible to fast-fowarderror.
Which strategy to use?
| Situation | Recommendation |
|---|---|
| Solo project | Either works; rebase keeps a cleaner log |
| Team project, unshared branch | Rebase |
| Team project, shared branch | Merge (never rewrite shared history) |
| Unrelated histories (my case) | Merge with --allow-unrelated-histories |