Reputation: 1263
I have a master branch and a few working branches which each should implement a specific feature and then be merged into master and deleted.
The problem I have is that the working branches track all the files that are also on master, and whenever master gets updated, I have to manually merge master into all other working branches to keep them up-to-date, even if none of the files relevant to the working branche's feature got affected.
Is there a strategy with which I can only track specific files on a branch and keep other files free to be updated by other branches? Is there a better way than using .gitignore, which would always have to be manually updated to ignore/include specific files? I would like to automatically track only those files which have changes committed on that branch.
Upvotes: 3
Views: 2370
Reputation: 45649
For whatever reason, I see more and more questions about this proposed workflow. It always leads to problems. I'll discuss how to get close to what you're asking for, but I'll also discuss why it always leads to problems.
First let's get this out of the way: You mention .gitignore
. It won't help. It doesn't matter if you do maintain branch-specific versions by hand, it still wont' help. Ignore rules do exactly one thing: They keep untracked files untracked (by default). They don't affect merges, fetches, pushes, etc. What's in the repo is unaffected by .gitignore
.
So... In git, it is assumed that branches (especially branches that will ever be merged) are different versions of the same content. That means, more or less, the same set of files - not subsets of files. ("More or less" because one branch may have added or deleted a file not yet present in another branch; but in that case, it's assumed that the subsequent merge will add or delete the file from the other branch.)
I've seen a number of people try a method where they branch, and then delete all but a subset of files. Then when they merge, those files are deleted from master
- which of course is what happens, they told git that the branch work included deleting those files.
A commit is a project snapshot; this is the intended unit of content in git. (You can argue that the object is the most basic unit of content, and in the physical model of git I'd agree. But in the conceptual model of git as source control, it's the commit.) So a commit can represent a consistent version of the project. You can build and test any commit.
("But I can build the subset of code that's on each branch independently!" Then you probably have multiple projects in the same repo, and should revisit that. More on that later.)
So a couple questions before I get into a "how" discussion:
Your stated concern is that you have to merge master
to all the branches to keep them up to date, even if the branch doesn't care about the modified file. So my first question is: why? If the branch doesn't care about the file, what's the difference that it contains an out-of-date version of the file? When you merge or rebase, git will know that the branch version is out of date, so it seems that either a file matters (in which case you can't "not have it" on the branch) or it doesn't (in which case updating it isn't a reason to merge master
).
And my second question is, "so what?", because even if you do decide to merge master
, the branch won't have a conflict for the file (since it doesn't care about the file). The only way this is a problem is if you're preemptively merging to every branch on every update to master... and that would take me back to "why?"
But ok, fine. Let's assume you're unconvinced and have a use case where you just have to have branches with partial source code.
How can it be done?
As I said above, if you start by creating the entire project tree, and then on the branch you delete the files, you're going to have a bad time. Merges from master
will conflict, and merges to master
, when they don't conflict, will wipe out files you still want on master
.
You can do a little better by creating the branches either as orphans, or as children of an "empty" commit on master
. Then creation of each sub-project tree occurs on the corresponding branch.
Now, this is better because there are no deletes to work around, but it only works if the branches are disjoint - i.e. share no code. If they are to share code... where do you create it?
You could create just the shared code on master
, before the branches are created. Only now you have a situation where updates to the shared code will require merges to the branches, and that will cause code merged to master
from other branches to "spill over" as git gradually corrects you back to the condition where all merge-related branches are versions of the same content.
And that points to another issue: Even if there is no shared code, you cannot merge master
to the branches in this model (because, again, code would "spill over". So you also have to impose some discipline, that code is only modified on the branches. If you update master
directly, or receive an update to master
from an outside source, you won't have a good way to apply that update to the branches.
So what should you do instead?
In the very limited case where each branch has a set of files that are present in no other branch, I've outlined a model that will probably mostly work... but why use it? It doesn't make sense. What you have are independent code bases, so just put them in separate repos. If you want one coordinating branch to pull them together (like master
would be), you could create a "master repo" and use submoduels to tie each project in.
In the case where some code is shared, you still should treat each "branch-specific" set of code as its own project (which should get its own repo), and additionally you should treat the shared code as its own project. Then use build tools to declare dependencies on the shared code from the specific projects.
This not only gets you out of the business of working against the grain of your source control tool (as you try to solve what aren't source control problems), it gives you a bunch of power (in the ability to build a sophisticated build infrastructure) that you may not even realize you're missing.
Upvotes: 7