views:

170

answers:

9

This question occurred to me whilst playing around with git, but I'll ask the general case...

I just thought of a feature which might be nice for version control, but I don't know if it exists or what it is called. I want to call it persistent local changes.

Say I have a config file in svn, which has lots of useful non-recreatable stuff (and so must be in version control), but has one section which everyone needs to edit for themselves. Maybe a database config, or a username and password, or the local path to some 3rd party software. Your options in this situation are

  1. Edit wars in version control. Just keep changing the file and hope that everyone else gives up editing the file before you do.

  2. Edit it, but never commit those changes. They just sit there making your "What's new/changed" command look dirty, and you have to remember to not commit it.

  3. Template it. Remove the file from version control and check in a copy of it with .template on the end. Locally copy the file and rename it back, with your changes in.

  4. Use the new (fictional?) persistent local change feature. Make your change then issue the record-changes-as-local-persistent command, which figures out a patch, and after every update reapplies your patch.

Does this feature exist anywhere (it feels like git stash, but the purpose is slightly different)? If it doesn't exist, is there a good reason why not? (has someone thought about it and decided it was a bad idea?)

A: 

This is doable if you can combine multiple repositories into one working tree. The most simple solution would be symlinks.

The problem (which makes it hard) is that VCSs want to preserve the notion of change sets. So if you commit such a file together with regular versioned files - should the changes belong to the changeset or not? Having the same changeset mean different things on different machines is clearly confusing.

With multiple repositories, you can certainly have commits that go in one repository or the other. How much local setup this requires depends on the VCS system. For example, for svn:externals, you would need to use the same file: repository on each machine, but they could point to different sets of files. With symlinks, you could organize it in any form you please (assuming the symlink itself is not versioned).

Martin v. Löwis
A: 

I've always addressed this by working to avoid the situation altogether.

I try to make all environments as similar to one another as possible. The only things that differ are usually logins and sometimes the URLs of infrastructure services (DB, message broker, enterprise data services, etc.).

The development environment of all developers is set up exactly the same, and configuration for development environments is checked in so developers can just check out the code from version-control and build, without any further fiddling about.

Passwords and configuration for CI and test environments are checked in, so build and deployment servers can automatically bring up systems in test environments.

Passwords for production are never checked in (that's often a legal requirement where I work) and administrators maintain the password file in the production environment.

Nat
A: 

i do a similar thing on monotone, using the propagate command.

in short, i have a 'development' branch and a 'deployed' branch. in the deployed branch, the config has a few settings different (some directories and DEBUG=False). When i want to deploy, i do a mtn propagate from the development to the deployed branch. then at the server i do a pull and update, so the workspace gets the latest changes from its branch, which include all new developments, but respect the setting differences.

Javier
A: 

You can ignore any configuration file from the version control. It's the .gitignore file.

For example if you want to ignore all your log directory, you add a line to that file with :

log/*

To ignore the .DS_Store file, you add a line with :

.DS_Store

You can then update the file locally, you will never see it in the modified but uncommitted files. And if you do a git add ., it won't be added to the commit.

If you need to put your configuration file to git but only keep one password or var somewhere out of it, what I do is calling a distant file inside that configuration file. And this file contains my password.

On a rails project for example, my database configuration looks like that :

production:
 database: project_database
 username: database_user
 password: <%= File.read('path/to/my/password/file').chomp if File.readable? 'path/to/my/password/file' %>

The file "path/to/my/password/file" is not in version control.
So my configuration file is versionned. But the password depends of the machine on which we are.

It also has the advantage of not having the passwords in your version control to be read by potentially anyone.

Damien MATHIEU
+1  A: 

This might be a little more specific to Visual Studio but I assume most IDEs will have some similar feature.

In all of my app/web configs that have settings that change for different enviroments I split them into their own files so that my main config looks like this

  <dataConfiguration configSource="Config\Development\dataConfiguration.config" />
  <connectionStrings configSource="Config\Development\connectionStrings.config" />
  <appSettings configSource="Config\Development\appSettings.config"/>

Then for each file it's similar to

<?xml version="1.0" encoding="utf-8" ?>
<dataConfiguration defaultDatabase="defaultDatabase"/>

After that I create folders for each environment Dev/Staging/Prod etc and use a different sub config file in each folder so all the settings can be checked into TFS. I have my web deployment project setup to pull in the appropriate files per release configuration. Later when I configure TFS to manage my releases this can easily be achieved by just having it copy the correct config.

At this point you can check in a baseline for the program and then when developers need to change things for themselves only that they don't plan to check in they can easily just make the file not readonly and edit it.

Chris Marisic
A: 

Darcs provides this. You can record a patch of the changes to the settings in your local repository and never push it to another repository. Sadly, the FAQ states it's not possible to flag a patch as local only.

Once could work around this limitation though by having a second repository that only contains the patches that are unique for your repository.

Georg
A: 

You can sort of kinda do this with SVN.

If you check a file out of the library and make changes to it, and then later do an "update", you get the new copy of the file from the library with your changes applied to it. I use this regularly for configuration files.

It's not quite like what you're describing because every time you do a commit, you do have to exclude the configuration file. I suppose if you forget to do that at some point, you will update the repository with your local changes and cause everybody else some grief.

And when you do need to change the "public" part of the config file, you have to do a separate check out so you could separate the "public" changes from the "local" changes.

I'd also endorse schemes like Chris Marisic describes. I have a couple of Web apps where I've created multiple configuration files, and the program dynamically decides which one to use based on the environment.

In one case, the configuration file includes paths to external files, and I was working with both a Windows server and a Linux server so the path names are different. So I ended up creating a "Linux configuration" and a "Windows configuration" and then picking based on which OS I was running under.

In the other case, the program at start up checks the name of the servlet context (it's a JSP/servlet app), then looks for a file named "WEB-INF/.properties". If it doesn't find that, it loads a default name. Then I run the development version under a different context name then the production version, and each automagically picks up the correct config file.

Jay
+6  A: 

You can do this in git with a master (or "vendor") branch and a local branch. Your local commits go on the local branch, and you rebase that on top of master when it changes. If you don't specify a remote for the local branch you won't be able to push it accidentally; if you accidentally commit something you want persisted to the local branch, just cherry-pick to master and push from there.

Ben Stiglitz
+1. This is the way to do it. Clean and non-intrusive, and you don't need to have dirty source trees with uncommitted changes lying around.
sunny256
A: 

Mercurial can add a local file to .hgignore and so be ignored - then it doesn't sit messing up your changed files or whatever.

J Fernyhough