可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm using git for a PHP project, I think it's really handy. There is one thing that would be great if I get it to work.
I have created a branch, meant for deployment. It has some differences, like different configuration files and documentation.
I can't just ignore them, because then they will stay in both branches, while I would like to keep them different in both branches.
The problem is that when I merge the branches, those files that are meant to be different are merged too.
Is there any convenient way to accomplish such a thing? How is this normally done?
回答1:
I am not sure Git is meant to be used this way.
First a quick Linus advice, always "colorful" and informative ;)
Git very fundamentally tracks project state, not file state. Which means that you very much can NOT try to "merge a file". It is a senseless operation in git, and in fact, any SCM that allows it pretty much is doomed to be a total piece of sht ().
(*) And I'm not saying that just because git doesn't do it. It's much more fundamental than that. Once you start doing per-file branching and merging, you've basically screwed yourself, and you'll never be able to work on the project as a "whole project" any more - you no longer have a well-defined history that actually is the history of the whole project.
There.
That said, you could:
- manage those config/doc files a separate git sub-projects (note: the use of submodules has been discussed here)
- or record partial merge (using "ours" strategy for files we don't want to merge), then --amend it.
Other solutions in this thread involve working on a "server-specific" branch on your deployment server
Development Deployment
#origin/master:
x--x $ git clone
# master
x--x
$ git checkout -b deployment origin/master
x--x
\
-- #deployment
$ .... #makes changes for config files
#or other specific deployment files
x--x
\
--d1--d2 # no need to push that branch ever.
#new developments
x--x--x--x
$ git pull --rebase #pull origin/master and
#replay current branch on top of it
x--x--x--x
\
--d1'--d2' #SHA1 rewritten in deployment branch
#not important since this branch
#is not pushed (published)
回答2:
This works for me, and I make only minor configuration changes to deploy (3 lines in config files).
Clone your repository from GitHub or wherever you keep it. To where you wish to deploy.
Run git checkout -b deployment origin/master
.
Make your changes (push them if you like).
Whenever your master (or whatever branch you made the deployment from) has changes you want to deploy, simply git pull --rebase
.
It's a simple solution and it certainly works for me, I can't speak to wether or not it this makes it "shi*t" as others suggest, but it is certainly very useful for our purposes.
回答3:
I do some silly tricks like:
- Have the app read file
config
- Add
config.development
and config.production
but not config
to the repository
- Have your deploy script not only clone the repository, but also then
cp config.production config
Does that make any sense?
It works okay for me.
回答4:
Quick answer is, don't ever merge the branches. In fact you don't need to merge them at all, just merge from development (aka "master") to deployment to merge fixes and generic changes.
回答5:
If you want to keep your history nice, you can keep the deployment files in some commits on top of your clean branch. Then, when it's time to deploy a new version, you check out the deployment branch and 'git rebase master', to put those commits on top of the original branch.
That way, you can also make easy changes to the configuration files, and change the top commit with 'git commit --amend'.
回答6:
I had a similar problem and created a tiny project called config-magic to help manage this.
Config-magic lets you create template conf files and then profiles of data for each of dev/staging/production. You then run "cfg dev" to produce the "dev" config files, "cfg staging" to produce the staging config, etc.
I then have this wired up with scripts, so that when I deploy to staging, I locally run "cfg staging" then scp over all of the config files after updating the codebase from git.
The "actual" config files are set to be ignored by git. This has worked really well for me so far.
https://github.com/apinstein/config-magic/tree
回答7:
I was thinking about all of these solutions for my situation but none of them seem to apply. I edit both on my live and development servers.
Rebase works well if you dont need to republish that branch, but I make changes on my deployment branch and publish those back to the main repository.
The submodule way only works if your files are in a seperate subdir, my config files are in several places.
The merge ours method wouldnt work so well also since i'd have to pull that branch first and then the big branch.
Maybe it would work if there was still a merge theirs and I could pull in the proper configuration branch when needed.
For now a .gitignore works and I just manually upload the files.
After doing more research I've found my solution to not have a git repo on the live site but use a staging site to pull the latest changes into my branch with the staging/live config files. and then deploy by git archive and extracting the files onto my live site.
回答8:
cherry-pick seems to work for this for this (at the expense of polluting the logs a bit).
git checkout -b testing
make changes and commit
git checkout master
git checkout -b deploy
make changes and commit
git checkout master
do everything under master and
git cherry-pick testing or git cherry-pick deploy to apply the diffs needed to switch from the current system into the testing or deployment version.
回答9:
I mentioned earlier patch files. I've gone off this now, and instead maintain deployment branches.
ie, I branch off master with a new branch named 'master-deployed', and make changes on that branch that I need to be only in the test version on the build server (i.e. adding a warning that this is not the live version, and different db server in web.config).
The build server when building the bleeding edge version then checks out master-deployed, and rebases it onto origin/master before performing the usual build. If no-one has made any conflicting changes then everything is fine. If they have then I can't see any system handling this without manual intervention.
Same goes for tip of qa, which has a branch 'qa-deployed'
I use the --onto flag to make sure if a whole branch is rewritten then the patch doesn't take all the old commits with it.
So on the build server the qa build looks something like
git reset --hard
git clean -xfd
git checkout -f qa-deployed
git rebase --onto qa HEAD^ qa-deployed
build-script
回答10:
currently i have a couple of patch files checked in, and the build server applies those patches for the relevant versions. though i'm having second thoughts about it at the moment
回答11:
That's easy and clean.
You'd create a deployment branch like:
> git checkout deployment-1234
Now you can make the deployment specific changes. Once that is done, commit it:
> git commit -as
Go back to your master branch.
> git checkout master
Immediately, merge the deployment branch. This will modify your master branch with those deployment-specific changes, but don't worry, we will revert it.
> git merge --no-ff deployment-1234
To revert the changes, just checkout the files before the merge and commit it with amend.
> git checkout HEAD^ .
> git commit --amend
That's it. Now git treats that the file changes in the first commit of deployment-1234 as already considered by the master and found to be unsuitable. So it will never add those changes to the master branch even if you try to merge the whole deployment-1234 branch to master. (Try it!)
I also use another method in a project that requires better control. The bad thing of the above is that you might create a conflict during a future merge from deployment-1234 to master. That is okay if those merges are manual. But if you need automatic merges, it is better if I can prevent this systematic conflict. So instead, I created a script that can apply and undo the deployment-specific changes. Then the changes themselves do not need to be in the repository, instead what appears in the repo would be that script.