A -- (master)
\--> B (remove-blanks)
Master repo awaits user contributions. Many ways to achieve that, let's review some of them.
$ git checkout remove-blanks
$ git format-patch master
0001-remove-blank_lines.patch
Adding a commit to master makes all patches built against old revision useless.
A -- -> C (master)
\--> B (remove-blanks)
$ git checkout master
$ git apply-patch 0001-remove-blank_lines.patch
# ...
error: file: patch does not apply
Reasonable way, designed for this particular issue. Fails to work concurrently for many users contributing against one master repo. Rebasing needed to keep patches up to date, forcing merges is generally a bad idea.
$ git merge remove-blanks
**Auto**-merging file
Merge made by the 'recursive' strategy.
file | 3 ---
1 file changed, 3 deletions(-)
What we want to achieve is a fast-forward kind of merge, which ensures smooth patching without conflicts.
$ git reset --hard HEAD^
HEAD is now at ...
$ git merge --ff-only remove-blanks
fatal: Not possible to fast-forward, aborting.
Simple principle, requires no effort on the maintainer. Each time master repository changes, users are pulling them down. When they finish their work, they rebase it against master repository, which always makes this a perfect candidate for fast-forward merge.
$ git checkout remove-blanks
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: remove blank_lines
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging a
$ git checkout master
$ git merge --ff-only remove-blanks
Updating b138447..a6cc7f8
Fast-forward
file | 3 ---
1 file changed, 3 deletions(-)
Smoothly done, the master repository maintainer's work consists only of fast-forward merges from his contributors and his commits (both applicable with no extra effort at all times).
Made popular by GitHub, they are saving space on disk with little overhead for each fork repo (c. 92K).
$ git clone root_repo --shared fork
Cloning into 'fork'...
done.
$ du -s fork
148K fork
$ cd fork
History:
A -- -> C (master)
\--> B (remove-blanks)
Same as in parent/root repository, but objects are referenced, not copied.
fork$ vim file && git commit -a
New structure A -- -> C -> D (master) --> B (remove-blanks)
Only commit D exists in this repo, rest is referenced.
fork$ du -s .
176K .
Resolving dependencies and unbounding fork from the parent can be simply done by duplicating referenced objects from the master repository.
fork$ git repack -a
Counting objects: 6, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), done.
Total 6 (delta 0), reused 3 (delta 0)
At this point, fork is standalone and does not require parent, which can be now safely deleted.
fork$ du -s .
192K .
Master repository has to fetch contributions and then merge that changes.
Size before:
$ du -s root
148K root
Pulling changes from a fork. Assuming fork is a predefined remote available for master:
root$ git fetch fork && git merge fork/master
Updating b88acd1..777eb21
Fast-forward
file | 2 ++
1 file changed, 2 insertions(+)
Size after:
root$ du -s .
208K .
Git offers interesting ways for this issue, let's review them:
Patches
+Very lightweight, without.gitdisk overhead.+Easily stored in the database, without the need for managing git repositories.-Fails to apply after any kind of modification to the original file.-Cannot be brought up-to-date without user's input.
Merges and branches
+Rebasing can be used to keep commits up to date.+Moderate git disk overhead.-Impossible to store contributions of multiple users, as is working on multiple branches simultaneously.
Forks
+Very clean and easy way to let users make contributions.+Without push permissions to master repository, witch clean border between them, thus preventing attacks.-High git disk overhead, even though objects are shared (referenced) and not copied.-Very sensitive with disk-failures, if something happens to the master repository, all forks are compromised.