The short question: I find that git writes blob objects to remote before the remote resolves deltas during git push --force
, even if the same blob objects were written to the same remote repository a short time ago.
I want to ask:
- Why does git writes the static blob objects to remote even the latter has them
- Is it possible to stop git from doing this (client side or server side)
The longer story:
I have a repository that contains both static files and code, and I manage them differently.
All the code files are in branch "history", and all the static files are in branch "static", branch "history" and "static" share a common initial commit, and they merge to form branch "master", illustrated below:
* commit (HEAD -> master, origin/master)
|\ Merge:
| |
| |
| |
| | Merge branch 'static'
| |
| * commit (static)
| |
| |
| |
| |
| |
* | commit (origin/history, history)
| |
| |
| |
| |
| |
| |
* | commit
| |
| |
| |
| |
| |
* | commit
|/
|
|
|
|
* Initial commit
Each time there is a code update, I commit the change in branch "master", then rebase the commit onto branch "history", then checkout branch "history" and emerge branch "static" again, during this process, branch "history" (fast-forward) and "master" (force update) are pushed to remote:
git rebase --onto history origin/master master
commit=`git rev-parse HEAD`
git checkout $history_branch
git reset --hard $commit
git push
git checkout master
git reset --hard history
git merge -m "Merge branch 'static'" static
git push --force
This command executes faster, because it does not transfer the static files to remote, which contain large files.
When there is a change in static files, I checkout the branch "static", commit the change using --amend
flag, then checkout branch "history" and merge branch "static", force update branch "master" on remote at the end of the process:
git checkout static
git add .
git commit --amend -m 'Add static files'
# As torek pointed out, I made a mistake in this post
# The following "git push" command is not performed
# git push
git checkout master
git reset --hard history
git merge -m "Merge branch 'static'" static
# git push --force
git push --force origin static master
# torek's suggestion "What you can do about this, part 1"
# does not work out for me:
#
# $ git push --force origin static master
# Counting objects: 422, done.
# Compressing objects: 100% (407/407), done.
# Writing objects: 100% (422/422), 480.08 MiB | 1.05 MiB/s, done.
# Total 422 (delta 41), reused 0 (delta 0)
# remote: Resolving deltas: 100% (41/41), completed with 1 local object.
# To ...
# + 3539524...6618427 master -> master (forced update)
# + 6a1f0c0...ba60bb9 static -> static (forced update)
The last command, however, takes a long time to complete, and I find git writes all the static blob objects to remote before the remote resolves the deltas:
Counting objects: 422, done.
Compressing objects: 100% (407/407), done.
Writing objects: 100% (422/422), 480.08 MiB | 1.44 MiB/s, done.
Total 422 (delta 41), reused 0 (delta 0)
remote: Resolving deltas: 100% (41/41), completed with 1 local object.
This happens even if the commands are executed the second time, and no modification to the work tree is performed between the 1st and the 2nd execution.
I used the script in how-does-gits-transfer-protocol-work to list all the objects in the local repository before and after the 2nd execution, and the results shows only 2 new objects after the 2nd execution, the commit objects produced by git commit --amend -m 'Add static files'
and git merge -m "Merge branch 'static'" static
, which means no new blob objects are created.
Extra Information:
Here is the script that looks after the workflow:
#!/bin/bash
master_branch=master
master_origin=origin/master
history_branch=history
static_branch=static
make_master() {
git checkout $master_branch
git reset --hard $history_branch
git merge -m "Merge branch 'static'" $static_branch
}
extend_history() {
git rebase --onto $history_branch $master_origin $master_branch
local commit=`git rev-parse HEAD`
git checkout $history_branch
git reset --hard $commit
}
add_static() {
git checkout $static_branch
git add .
git commit --amend -m 'Add static files'
}
case "$1" in
code)
extend_history
git push
make_master
git push --force
;;
asset)
add_static
make_master
# git push --force
git push --force origin $static_branch $master_branch
;;
*)
echo "Unknown action \"$1\"" >&2
exit 127
esac
client git version: 2.17.1 client os: 18.04.2 LTS (Bionic Beaver), x86_64, VM inside virtualbox 5.2.26 server git version: 2.11.0 server os: Debian GNU/Linux 9 (stretch), x86_64
The local repository was a directory on the client os's local disk, latter moved to a directory inside a virtualbox shared folder, same result.
Edit: After all the trouble I have been through, I decided to take torek's second advice, not rewrite history. If the outdated static files takes too much space, I still need to squash the commits, so I moved all the code files into a subtree, and manage them from there:
git checkout static
git subtree add -P code history
git checkout master
git reset --hard static
# Remove branch static and history, their tracking branches,
# and their counterparts in remote repository
To squash static commits:
code_commit=`git subtree split -P code`
git rm --quiet -r code
git checkout --orphan new
git commit --quiet -m 'Add static files'
git branch -M new master
git subtree add -P code $code_commit