Creating tags//branches in version control with only some files

80 Views Asked by At

I'm already working with PVCS control version, but I'm about to start a new project and would like to migrate to Git/SVN keeping the same structure as I had.

The code is C, and it's structured so that each C "object" has its own tag, in which there are only it's C files and the neccesary H files to compile.

Is it possible in Git/SVN to create a branch from the trunk that contains only the necessary files for that "object" to compile, make a tag, and merge the changes into the trunk?

I've tried Git, but every time I create a branch, I have all the files, including C and H files from other "objects" that I don't need.

I don't know if there's another VCS that allows to create these kind of partial tags.

3

There are 3 best solutions below

1
On BEST ANSWER

PVCS appears to be file-oriented (like CVS or RCS), with a centralized store (like CVS) and locking (also like CVS).

SVN is commit-oriented (and centralized) but treats branches as file sub-trees, with a merge model rather than a lock model (though actually SVN offers file locking as well). In some cases, this may be compatible with the way you're using tags. But it may not: if you tag individual files differently despite being in the same file system location, e.g., dir/sub/file1.c has tags A and B while dir/sub/file2.c has entirely different tags D and E, that's not going to play well together either.

Git and other modern VCSes are distributed rather than centralized, and commit-oriented. When the system is distributed, the whole concept of locking a file is pretty much toast: there's no central authority declaring who has the lock. So these all use merge models. Git furthermore has no concept of trunk: branches are all equal—or more precisely, branches don't really mean anything at all. What matters in Git is the commit graph, with the branch names really being a supplemental trick to remember how to work your way through the graph.

(Combining this with the idea of reachability provides Git the ability to discard unwanted objects: unlike some other modern graph-directed version control systems, commits are no more special than any other internal object. This can be a bit disorienting, to put it mildly: in other commit-oriented VCSes, once you make a commit, it's permanently affixed to a single branch and you can always find it in that branch. So Git is even more of a leap here than, say, Mercurial.)

Any time you have a file-oriented version control system, it's easy to pick out particular files, because that's how the VCS works underneath. Once you go to a commit model, the "work area" (whatever it gets called in that particular VCS—Git uses the phrase work-tree or working directory or anything along those lines) must be matched to the specific commit, because you're not getting version V of fileX.c, you're getting all the files that make up version V. It is, in theory, possible to combine this with sparse checkout and multiple work-areas, so that work area 1 has version V1 of sparsely extracted file1.c, work area 2 has version V2 of sparsely extracted file2.c, and so on; but you'll be fighting with the model all the time. This is probably not a good idea.

In the end, I think that unless you stick with a file-oriented VCS, you will have to change your workflow.

2
On

Although there are some confusion in your understanding of branches with Git, it could be doable I guess.

First, about branches: in Git, they are just a reference to their last commit (under the hood, a branch is a file with only a commit hash in it).

Git, PVCS and SVN all work in very different ways.

In Git, if you want to separate files per branch, it is possible (although unusual). A suggestion to create your base branches would be as follow:

Create your git repository:

ghislain@linux (1): /tmp/example (master) ✔
> git init .
Initialized empty Git repository in /tmp/example/.git/

Create your files (although you probably have them already):

ghislain@linux (1): /tmp/example (master #) ✔
> touch foo.c foo.h bar.c bar.h

Make an initial commit (which will serve as base for all your branches):

ghislain@linux (1): /tmp/example (master #) ✔
> touch README.md
ghislain@linux (1): /tmp/example (master #) ✔
> git add README.md && git commit -m 'Initial commit'
[master (root-commit) a9a05f3] Initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md

Create a branch per files type with the concerned files:

ghislain@linux (1): /tmp/example (master) ✔
> git checkout -b files-foo master
Switched to a new branch 'files-foo'
ghislain@linux (1): /tmp/example (files-foo) ✔
> git add foo.*
ghislain@linux (1): /tmp/example (files-foo +) ✔
> git commit -m 'foo added'
[files-foo 558cdc6] foo added
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foo.c
 create mode 100644 foo.h

Do some modifications, update, work on the said files:

ghislain@linux (1): /tmp/example (files-foo) ✔
> vim foo.h
ghislain@linux (1): /tmp/example (files-foo *) ✔
> git add foo.h 
ghislain@linux (1): /tmp/example (files-foo +) ✔
> git commit -m 'foo.h updated'
[files-foo 9565594] foo.h updated
 1 file changed, 1 insertion(+)

Do the same for other files:

ghislain@linux (1): /tmp/example (master) ✔
> git checkout -b files-bar master
Switched to a new branch 'files-bar'
ghislain@linux (1): /tmp/example (files-bar) ✔
> git add bar.*
ghislain@linux (1): /tmp/example (files-bar +) ✔
> git commit -m 'bar files added'
[files-bar 9f78382] bar files added
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bar.c
 create mode 100644 bar.h

Your commit log will end up looking as follow, with one branch per file "type":

ghislain@linux (1): /tmp/example (files-bar) ✔
> git log --graph --oneline --all
* 9f78382 (HEAD -> files-bar) bar files added
| * 9565594 (files-foo) foo.h updated
| * 558cdc6 foo added
|/  
* a9a05f3 (master) Initial commit

When you want to work on a set of files, you'll need to git checkout <related-branch> and commit there.

It might however end up tricky if you want some cross references, or common work. For the latter, you'll have to commit in master and rebase the branches on top of it (if you want to keep your log clean).

All in all, it could work, but that might end up being very cumbersome to manage. I don't know how your project work, but it might be simpler to be all together and use a maybe more classic branch workflow, especially if you are new to Git.

1
On

I can't speak to the specifics of how Subversion would handle this; really git and subversion are entirely separate questions. I can say generally that I don't believe that's how subversion branches work.

I can say definitely, that is not how git branches work. Per other answers, you could hack something together to approximate that behavior, but I recommend against it. It will lead to trouble sooner or later.

If you want to track and compile sets of files separately, they should be kept in separate repos and maybe brought together with a "parent repo" that treats them as submodules.

More generally, the goal of "switch tools, but keep the structure from my current tool" isn't very realistic. Each source control tool has its own model of a project and its content and history. If you like the structure you have, you may want to stay with the tool you have. If the tool you have isn't working well, that's probably a symptom of the structure that tool uses.