AyJayKay
AyJayKay

Reputation: 103

Benefits of version control without commiting

Aka. Best practice to have version control on files that are in my ignore config (e.g. gitignore).

Hi, this question may seems weird first but let me explain: We have quite some ignored config files in our projects. They get brought to the developer by checked in .dist versions of the config, as this is a well known workflow. (Configs contain dev specific, sometimes sensitive, information that should not be committed.)

Now what I would like to have are the local advantages of version control on those files, too. I want to see what I changed. I want to be able to revert back or stash changes. All without pushing to any remote of corse.

Specific working examples:

All I came up with was mixing in a different version control system. Or temporarily adding the files to my git and redoing that again, once I am done. Both feel wrong.

Did you ever though about that or use something to prevent mentioned problems?

UPDATE:

Thanks for existing answers! I would like to not have to have separate folders and I would like to have the version control system recognize "new" (but ignored) files by itself.

Upvotes: 1

Views: 149

Answers (2)

LeGEC
LeGEC

Reputation: 51780

You can tell git to use an alternate .git/ folder using the GIT_DIR environment variable or git --git-dir <dir> cli parameter.

You can use this to track your config files in a separate directory.

A note about the caveats : tracking files in an unrelated repo makes your config files not follow automatically the version of the code, e.g : after running git checkout v1.0, you would need to separately update your configuation.
You may also have clashes between your main repo and the auxiliary config repo in case the directory structure change.

  • choose a place to store your config files : either a repo which will be global to your machine (~/.projectcfg) or next to your repo (../projectcfg)

  • add an alias in your repo's config, to target this gitdir, and also keep the same worktree as your main project :

# replace <cfgdir> with either '~/.projectcfg' or '$rootdir/../projectcfg'
git config alias.projectcfg '! _f () { local rootdir=$(git rev-parse --show-toplevel); git --git-dir <cfgdir> --work-tree "$rootdir" "$@"; }; _f'
  • run git projectcfg init once, to initialize this repo
  • edit the info/exclude file in that repo, to only keep config files :
# vi <cfgdir>/info/exclude :

# ignore all files :
*
# keep the config files you want to target :
!*.ini
!*.conf
  • you can now issue commands to that commit through git projectcfg <command> :
git projectcfg checkout -b myproject
git projectcfg add -f path/to/config.ini
git projectcfg commit
git projectcfg log
...

You probably named your config files in .gitignore files stored in the repository.
If this is the case, the files will not appear as untracked if you git projectcfg status, and you will need to use the add -f option to add these files.

One way to change this behavior can be :

  • place an ignore pattern in the .git/info/exclude file of your main project,
  • remove the rules from your various .gitignore files

The main impact here is : since the info/exclude file is not versioned with the repository, other users won't get it automatically.
What you can do is : commit a script, which sets up the above elements.

Upvotes: 0

ElpieKay
ElpieKay

Reputation: 30858

My solution is to use annotated tag. A tag can point to a git object, and an annotated one has a message like a commit does.

1.Create a blob object for a config file, foo.config, and get its SHA1, even if it's ignored.

sha1=$(git hash-object -w foo.config)

2.Create an annotated tag, config_v0.1, referring to the blob.

git tag -m"init foo.config" config_v0.1 $sha1
# or combine the 2 commands
git tag -m"init foo.config" config_v0.1 $(git hash-object -w foo.config)

3.Disable these tags from being pushed with the help of the hook pre-push.

#!/bin/bash

# pre-push sample
while read local_ref local_sha remote_ref remote_sha;do
    if [[ "${local_ref}" =~ refs/tags/config ]];then
        echo Forbidden tag detected ${local_ref}
        exit 1
    fi
done

4.List these config tags with messages.

git tag -l config_* -n999

5.Compare the diff between two config files.

git diff config_v0.1 config_v0.2

6.Overwrite foo.config with the content of config_v0.2. Be careful. Tag the current foo.config first if it's not been tagged yet.

git show config_v0.2^{} > foo.config

It would be troublesome if there are lots of config files. You can wrap these commands with git alias or shell functions. And to manage multiple config files under the same folder, tree objects are worth a try. Suppose there are foo.config and bar.config.

1.Convert them to blobs.

foosha1=$(git hash-object -w foo.config)
barsha1=$(git hash-object -w bar.config)

2.Write a tree file tree.txt. Suppose foosha1 is ce013625030ba8dba906f756967f9e9ca394464a and foosha2 is aa2fc61b2166263d32d25192df25ee218692b05d. Note that <SP> stands for a space and <TAB> for a tab.

10064<SP>blob<SP>ce013625030ba8dba906f756967f9e9ca394464a<TAB>foo.config
10064<SP>blob<SP>aa2fc61b2166263d32d25192df25ee218692b05d<TAB>bar.config

3.Create a tree object from tree.txt.

treesha1=$(git mktree < tree.txt)

4.Tag the tree.

git tag -m"init config files" config_tree_v0.1 ${treesha1}
# or combine the 2 commands
git tag -m"init config files" config_tree_v0.1 $(git mktree < tree.txt)

5.List the tree.

git ls-tree config_tree_v0.1

6.Overwrite foo.config with the version in config_tree_v0.1. Tag the current foo.config first if it's not tagged yet.

git show config_tree_v0.1:foo.config > foo.config

This is not a convenient solution. I hope it could give you some hints. You could also put these config files in a nested git repository and don't add it as a submodule.

Upvotes: 2

Related Questions