-
Notifications
You must be signed in to change notification settings - Fork 0
Git subtree evaluation for Pharo
The main setup of the evaluation will be a ''parent'' repository that will contain a ''child'' repository as subtree. For most of the questions (e.g., commit, push, pull) it is enough having both of them as a git repository in your github user. Pull requests will require some extra work: for example, with @tesonep we created a fork of the repositories in each his own github account to emulate the real use case where people will want to change things in Pharo.
- Create a parent repository (e.g., pharo)
# in https://github.com/guillep/Subtree_experiment_parent
git clone git@github.com:guillep/Subtree_experiment_parent.git- Create a child repository (e.g., glamour)
# in https://github.com/guillep/Subtree_experiment_child
git clone git@github.com:guillep/Subtree_experiment_child.git- Install child as subtree of parent
$ git subtree add --prefix=child git@github.com:guillep/Subtree_experiment_child.git master git fetch git@github.com:guillep/Subtree_experiment_child.git master
warning: no common commits
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:guillep/Subtree_experiment_child
* branch master -> FETCH_HEAD
Added dir 'child'This will load the child into parent, and create a couple of commits: first it is the initial commit imported from the child repo into ours; second is the commit that merges or includes the child into our parent repository.
Check for resulting repository:
https://github.com/guillep/Subtree_experiment_parent/tree/0ceaea21e3e21354df5f6d63a07333f6576af4e6
https://github.com/guillep/Subtree_experiment_parent/commits/0ceaea21e3e21354df5f6d63a07333f6576af4e6
This simple (the simplest) scenario just changes the parent without modifying the child project.
#in parent directory
touch newfile
git add newfile
git commit -m "test"
git pushCommitting and pushing from parent works as usual. No extra work is required. The relationship between parent and child still works ok.
This scenario checks wether add and commit operations work recursively or not in submodules. For that we create and add a file in the child submodule:
cd Submodule_experiment_child
touch newfile2However, going to the parent and checking git status will show that the parent knows that the child has untracked changes but he cannot tell what they are or what to do with them.
cd ..
git status #=> says submodule has untracked changes onlyMoreover, git add from the parent does not propose files to add:
git add ... #=> does not propose anythingThen we proceed to add the file from the submodule:
cd Submodule_experiment_child
git add newfile2Then we go back to the parent and check the status again to see that git will still not see changes in child, not propose us to commit anything.
cd ..
git statusFinally, we can go back to the child, commit the file. Trying to push the change from the parent will push nothing, so we need to push the child separately.
cd Submodule_experiment_child
git commit -m "test"
cd ..
git push # => Nothing!
cd Submodule_experiment_child
git pushThis push will effectively change the child project, and we will be able to see it in e.g., github's UI.
This shows that child behaves as a normal git repository, and that it evolves separate from parent. Moreover, add, commit and push operations are not recursive in submodules.
This scenario evaluates what happens to the parent project if we add, commit and push a submodule.
We start from where scenario 1.2 stopped: a file was committed and pushed in child repository. We saw that the child is seen by git as a normal repository. It does not really show any relationship about the parent. This will create a normal commit in the child repository.
We saw that the child's github's UI sees the pushed change. However, checking the github's UI of the parent will show that the parent still refers to the older version (commit) of the child. To fix this, we have to create a commit in parent to "update it to the newer version of the child".
# in parent
git add Submodule_experiment_child
git commit -m "updated submodule"
git pushThis happens because git keeps in parent a configuration of the exact version of the submodules it needs. Without this, git could not ensure that reloading a project could be reproducible.
This scenario evaluates what happens in the exact inverse of scenario 3. That is, we commit the child but not push it to the remote scenario. Then we commit the new configuration of the parent, pointing to the new child's commit, and we push it. This should create an inconsistent configuration.
cd Submodule_experiment_child
git add newfile3
git commit -m "commit child push parent"
cd ..
git add Submodule_experiment_child
git commit -m "updated submodule that is not yet pushed"
git pushThis will push only parent! And parent will will depend on a commit that is not yet published (pushed). If we go to github's UI and click on the parent submodule, we will get a 404 error because the commit we are pointing to does not exist in github (but on our local machine).
Since submodules evolve separately, git operations are not submodule recursive (by default), and that the parent git repository keeps a configuration of the exact used commits, managing a project with submodules implies that:
- changes in children should be commited and pushed before parent.
- then all submodules should then be "added" in parent and committed and pushed.
If it is not done in such way, repositories may be inconsistent and point to non-existent commits.
This scenario evaluates what steps are to be done when cloning a repository with submodules. For this, we clone in a separate directory the repository, initialize the submodules, and then we check the status of the submodule.
git clone git@github.com:guillep/Submodule_experiment_parent.git parent2
git submodule init
git submodule update
cd Submodule_experiment_child
git statusThis shows that:
- the required commit was loaded as demanded
- however, the submodule is in a "detached head mode". This means that it is just the commit but there is no branch used. This means that if any work is meant to do in such submodule, a branch should be created or checked out, otherwise, there is a risk of losing all commits.
This scenario checks if the link between parent and child really is versioned. For this we take and old version of the parent repository that should point to an old version of the child repository, and we check that what is fetched in the child is the correct commit.
git clone ... or git init, fetch, pull...
git reset --hard 100c67d15ca037680044e30f70b0fc75cd6f5143
git submodule init
git submodule updateAgain, here the loaded commit is the correct one, and we have the same dettached head problem as in scenario 2.
This scenario checks what happens when we clone a repository that has inconsistent child submodules. That is, submodules whose commits were not pushed.
#commit but not push child
touch newfiletopull
git add newfiletopull
git commit -m "newfiletopull"
cd ..
git add Submodule_experiment_child
git commit -m "updated submodule that is not yet pushed"
git pushCloning the project in a separate directory will work, but the submodule initialization will fail.
#clone in new folder
git clone git@github.com:guillep/Submodule_experiment_parent.git parentBadChild
cd parentBadChild
git submodule init
git submodule update #=> cannot checkout!
Getting/Fetching/Cloning a repository with submodules requires some extra actions:
- submodule init and update
- also this fetches a specific commit and is not aware of branches so as they are they are not editable. Branches should be checked out for each submodule.
This scenario checks whether pulling a parent with changes creates some conflict.
#in parent in a second cloned repository
touch newfiletopull
git add newfiletopull
git commit -m "newfiletopull"
git push#in parent in first repository
git pullPulling a parent project just works as a normal project and shows no problems.
This scenario checks what happens when we change a child submodule and we pull that change from parent in a separate directory.
#in child in second cloned repository
touch newfiletopull
git add newfiletopull
git commit -m "newfiletopull"
git push
cd .. #go to parent
git add Submodule_experiment_child
git commit -m "updated submodule"
git push#in parent in second cloned repository
git pull
git status #=> detects that parent depends on child commit that has to be pulled
git submodule updateThis shows that pulling will not update childs directly. For that, we require to do a submodule update. However, this may make us lose local changes in the submodule.
This scenario checks the co-evolution of projects. A submodule can be cloned independently of the parent, be changed and have new commits. What happens in the parent if a submodule has changes.
#in child in second cloned repository
touch newfiletopull
git add newfiletopull
git commit -m "newfiletopull"
git pushWe will see that doing a git submodule update will not change the child submodule, because it will load a specific commmit: the specific commit that we used when we committed the parent.
#in parent in second cloned repository
git status #=> does not realize changes in child. This is normal
git pull #=> does nothing
git submodule updateIf we want to update the child, we need to go into it and do a pull from the correct repository/branch where the updates are. And then, we need to update the parent to use the new child's commit.
# go to child and pull
cd Submodule_experiment_child
git pull
#need to update parent
cd ..
git add Submodule_experiment_child
git commit -m "update submodule"
git pushUpdating submodules should be done separately. Also, we should be careful if we have local changes: we need to merge them instead of simply doing a git submodule update.
This scenario checks what happens in the child if we create a branch in the parent.
#in parent
git checkout -b test
cd Submodule_experiment_child
git branch #=> still in master. NormalAs submodules evolve separately, branches in them also are manage separately. Branches created in the parent are not seen in the childs and vice-versa. Moreover, as we saw before parents only know childs specific commits but not what branch was used during development.
This scenario checks what happens in the parent when a branch is created in the child.
cd Submodule_experiment_child
git checkout -b test
# go to parent
cd ..
git status #=> no changes!We see that the parent does not know if the child created branches or not. Links between submodules are commit based, no branch based.
We evaluated in this case a single scenario were in a second forked repository:
- a change was made in child
- parent was updated to point to new commit of child
- then we issued two pull requests: one from child and one from parent.
Merging these pull requests (or trying to) independently showed the following:
-
Making a pull request on the parent will not "drag over" changes in childs. Actually it will just drag "pointers to the childs" but when doing
git submodules updateit will fail, as if the submodules commit were not pushed. This happens because they were pushed but in the repository of the one that made the pull request, but the repository that is pulling does not have any knowledge of where it is. It is the same problem as in scenario 1.4. -
On the contrary, accepting first the pull requests in the childs will make the parent pull request succeed.
Some options we where thinking that could solve these problems:
-
Option 1: issue pull request for child and parents. Then the parent pull request will fail until the childs are integrated. Actually it does not fail, but it cannot build, because submodules cannot be updated, because commits cannot be found.
-
Option 2: issue a pull request for parent only and build some infrastructure to support merging childs. Then at validation and integration time, two things should be done:
- first should take from the pull request the list of changed submodules
- then a merge should be done from the remote repository into each submodule. How to match each submodule should be done by convention as we do not have any extra information in the pull request.
- once all submodules are merged, validation can take place, or a new integration can take place by committing and pushing first all submodules, then parent