问题
Wordpress plugin with free and PRO version. The PRO version contains additional files, scattered across the codebase.
What is the optimal strategy to track both versions in git, satisfying the following constraints:
- free version is open-source on GitHub, accepting contributions;
- PRO version synced with a private repository;
- local development takes place on the PRO version (e.g. for refactoring to work);
- both histories are related (history of PRO ⊇ free)
- low-maintenance:
- we are git noobs,
- no manual accounting of what file fits where.
There are numerous Wordpress plugins following this exact free vs. PRO dichotomy. How are they versioned?
回答1:
A few simple approaches come to mind ...
Separate Namespace
Move all scattered files into a separate namespace directory, such as ./pro, into which you clone the separate PRO repository, containing only the PRO files. Drawback: The checkouts of the two repositories would have to be kept in sync (i.e. if you switch to an old commit in one repo, you'd always also need to switch to a compatible commit in the other repo).
Two Repositories
This, I suppose, is how it's normally done. Maintain free and PRO versions in two distinct repositories. Let the PRO repo define the public remote source to fetch and merge the changes from.
cd ~/project-libre
git remote add origin GITHUB_PUBLIC_REPO
cd ~/project-pro
git remote add origin PRIVATE_REPO
git remote add libre GITHUB_PUBLIC_REPO # to fetch and merge changes from
Whenever there are changes submitted to the public repository, you can merge them into your PRO version with:
cd ~/project-pro
git checkout master
git pull libre master --allow-unrelated-histories
git push origin master
Whenever there are changes in the PRO version you want to sync into the published free version, you can use git-format-patch to export the changes as patch files, then on the other end import that patchset, excluding any files that are not to published in the free version. Like so:
cd ~/project-pro
git checkout master
git format-patch HEAD~3..HEAD # Export e.g. last three commits as patches
Now switch to free version and apply the patches (with git-am), excluding from each commit all the PRO files (paths) that are on the ignore list for the free version. I put them in a .gitignore file here in the free project root, and the command line assumes POSIX shell is available (repeats --exclude
parameter for every file/path in .gitignore).
cd ~/project-libre
git checkout master
git am $(printf -- '--exclude=%s ' $(cat .gitignore)) ~/project-pro/*.patch
Two Branches
Have two branches, each synced with a different remote.
git remote add origin GITHUB_PUBLIC_REPO
git remote add private PRIVATE_REPO
Create two files, one free and one PRO:
touch free1 pro1
On master branch, create a .gitignore containing all the PRO files
git checkout master
echo 'pro*' > .gitignore
git add .gitignore
git commit -m 'Add .gitignore ignoring PRO files'
Sync the public branch with the public repository:
git push -u origin master
Now branch master into a private PRO branch and clear the .gitignore as the PRO files are not ignored there.
git checkout --branch master-private
echo > .gitignore
git commit .gitignore -m 'Clear .gitignore -- track all files here'
Sync the private branch with the private repository:
git push -u private master-private
Now commit free1 and pro1 files to their respective branches:
git checkout master
git add free1
git commit -m 'Add free1'
git checkout master-private
git add pro1
git commit -m 'Add pro1'
And merge master into master-private so that it contains the full set.
git checkout master-private
git merge master
You need to resolve the one .gitignore conflict (or you can specify -X ours
merge switch).
Later, you do some development on master-private branch (§ 3) retouching and creating various files that would fit in either of two branches:
git checkout master-private
touch free2 pro2
echo xxx > free1
echo xxx > pro1
You don't want to enumerate all the non-PRO files to commit (§ 5.2); that's what you have the .gitignore for. You use it by switching to master and there committing everything that fits, then switching back to master-private, committing what remains. You need to stash the changes first as they would be overwritten by switching branches.
git stash
git checkout master
git stash apply
You get a conflict here about pro1 changed in stash but deleted on master. You resolve it by telling git to just continue to ignore it by unstaging it.
git reset HEAD .
Now commit all the free files:
git add free2 # Manually add the new files (easy and not violating § 5.2)
git commit -a -m 'Update to free1 and free2'
Switch back to the private branch, merge in the updated master branch, and commit the remaining files, again from stash.
git checkout master-private
git merge master
git stash pop # The stash now applies cleanly and is removed
git add pro2 # Manual but necessary adding of new files (not violating § 5.2)
git commit -a -m 'Update pro1 and pro2'
And that's it.
When you get a pull request on GitHub and you merge it. Afterwards just sync your local master with it and then merge it into the private branch:
git checkout master
git pull
git checkout master-private
git merge master
git push
git is easy–peasy.
来源:https://stackoverflow.com/questions/51202660/git-workflow-for-a-project-with-open-source-and-proprietary-private-part