-
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 whether add and commit operations work recursively or not in subtrees. For that we create and add a file in the child subtree:
$ cd child
$ touch newfile2Going to the parent and checking git status will show that the parent knows exactly what files changed in the child.
$ cd ..
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
child/newfile2
nothing added to commit but untracked files present (use "git add" to track)git add works from the parent
$ git add child/newfile2Checking the status again to see that git will still see correctly changes in child.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: child/newfile2Finally, we can push the change from the parent.
$ git push
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 373 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:guillep/Subtree_experiment_parent.git
05a9248..d12a264 master -> masterThis push will effectively change the child project inside parent but not the original child project, and we will be able to see it in e.g., github's UI.
- https://github.com/guillep/Subtree_experiment_parent/tree/d12a26477983f28ff1cee148c5b437bf933f7ddb/child
- https://github.com/guillep/Subtree_experiment_child
This shows that child behaves as a normal subfolder. Operations add, commit and push are recursive in subtrees.
Subtrees work as normal directories for operations such as add, commit, pull and push. These operations will work recursively on subdirectories as in any normal git repository. A parent evolves together with its subtrees.
This scenario evaluates what steps are to be done when cloning a repository with subtrees. For this, we clone in a separate directory the repository and then we check the status of the subtree.
$ git clone git@github.com:guillep/Subtree_experiment_parent.git parent2
$ cd parent2/child
$ ls
newfile README.md
$ git remote -v origin git@github.com:guillep/Subtree_experiment_parent.git (fetch)
origin git@github.com:guillep/Subtree_experiment_parent.git (push)This shows that:
- the required commit was loaded as demanded, as we have the expected files
- however, the git remotes show that while using normal git commands, we are going through the parent. Thus, we are not just pulling and pushing to the original child's repository (as it happened with submodules).
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 05a9248541e347454e873fcc7ae4d64b86987b04
$ ls
README.mdAgain, here the loaded commit is the correct one.
Getting/Fetching/Cloning a repository with subtrees works as using any normal subdirectory. There is no extra bookeeping as it was required with submodules. This also avoids inconsistencies (e.g., pushed the parent but not the child) which removes corner case scenarios.
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#in parent in second cloned repository
$ git pull
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory cleanThis shows that pulling will update children directly as any other sub directory.
This scenario checks the co-evolution of projects. A subtree can be cloned independently of the parent from its original repository, be changed and have new commits. What happens in the parent if a subtree 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 pull from the parent will not change the child subtree.
#in parent in second cloned repository
$ git status #=> does not realize changes in child. This is normal
$ git pull #=> does nothingIf we want to update the child, we need to do a subtree pull from the correct repository/branch where the updates are.
$ git subtree pull --prefix=child git@github.com:guillep/Subtree_experiment_child.git masterThis will import the commit from the remote repository and do the according merge (if possible). Take into account that a subtree pull cannot be done if the working directory is dirty.
Updating subtrees is simple when we are managing a single repository. However, keeping the synchronized with external repositories requires a git subtree pull operation instead of a normal git pull.
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