Maintaining multiple branches of the same base project in VS - visual-studio

I've looked around the site but I couldn't find an answer that covers mine entirely, so please excuse me in advance if I missed it.
I inherited a VB.NET project that didn't have source control (it started as a pet project of a long-gone dev and nobody ever bothered after that to put it in), and by a friend's suggestion I thought about using Git for source control.
The project is a niche product that is customized and sold according to the customer's specs, so that brings the problem that even if 95% of the code is the same for all the customers, sometimes up to 10% of the code is changed and tailored for each customer, by changing or adding lines to existing functions, sometimes adding whole blocks of code, but there's no commonality in the changes between different customers (a function changed in one might not be changed in another).
To complicate things further, due to maintenance contracts, updates made to the baseline app have to be replicated in the customer's branches should they want them, and sometimes changes we make for a specific customer are good enough that we want to put them in the baseline app and replicate them to the other customers, BUT keeping the customizations for each customer!
So with my little knowledge of Git, I thought it would be like:
(customer 1)
C1-----
(main) /
A------B------D
\
\ (customer 2)
C2-----
\
\ (customer 3)
C3-----
...but I can't see how it's going to work after that:
Can I merge SOME changes from the customer's branches into the main trunk WITHOUT merging others that are only useful for that customer?
Can I merge SOME changes from the main trunk into each customer's branches WITHOUT losing the customizations in those branches?
Can I "mark" specific lines of code so they are not merged/committed?
Three or more devs will be working in this, each in his own machine but pushing changes to the company's repository for synchronization. What are the implications for this process?
Right now, every customer has a separate folder and separate project files with all their source code. How would be the import process to put those folders them into Git?
All of this must be done with Visual Studio, with Gitextensions and the Git Source provider for VS. Is it supported, or it has to be done with the console?
Thanks and sorry again if it overlaps with another answer.

I'm relatively new to git and normally use PoshGit for all my operations, so while I may not be able to help you with everything, I hope I can help with some things:
Can I merge SOME changes from the customer's branches into the main trunk WITHOUT merging others that are only useful for that customer?
Can I merge SOME changes from the main trunk into each customer's branches WITHOUT losing the customizations in those branches?
From what I understand, both of these operations can be achieved by using git cherry pick, which allows you to pick a particular commit from one branch, and add it to another without merging the branches together.
For example, assuming you want to add a change made to customer1's repository, to customer2:
First you get the hash ID of the commit from customer 1 that you want to insert into customer2
git checkout customer1Branch
git log
commit 2e8c40025939e8cf41dec70f213da75aa462184b
Author: xxxxxxx
Date: xxxxxx
This made a change that you want...
You then copy the first few characters of the hash you want to cherry pick, change to customer 2's branch and cherry pick it into the branch.
git checkout customer2Branch
git cherry-pick 2e8c40025939e8c
Now, if you do a git log, you'll see your cherry pick at the top. A similar tutorial can be found here (http://nathanhoad.net/how-to-cherry-pick-changes-with-git)
Can I "mark" specific lines of code so they are not merged/committed?
You may find help from a similar question was asked and answered here:
Commit only part of a file in Git
Three or more devs will be working in this, each in his own machine but pushing changes to the company's repository for synchronization. What are the implications for this process?
Since GIT is a fully Distributed VCS, each dev on your team will effectively have a full clone of the central repo on his own machine (complete with full history of that repo.) This means that log history queries and other requests (such as finding out who did what) don't need to go through your central server, but can be done privately and offline by each dev.
Similarly, the changes that each dev makes will become available to all of you (for example, all new branches will be available), but it can sometimes be frustrating to be working on the same features if you're not quite used to git.
As always its a good idea to commit early and often, this will decrease the tension you're likely to face when changes clash. you should also set some structure to when pushes are done, especially if you rely on each other's work to continue.
Another idea you may want to try is having one person in charge of the repo and having him merge changes and patches to help coordinate your efforts.
Right now, every customer has a separate folder and separate project files with all their source code. How would be the import
process to put those folders them into Git?
EDIT
Thanks for clarifying what you meant by this question. You could expand on a similar approach adapted from the answer given here: How do you create a remote Git branch?
Create a new mainline branch for your BASE project and push it to your remote repository.
cd baseProjectDirectory # navigate to your main project directory
git init # git initialize the dir
git add . # recursively add all files in directory to git repo
git remote add <remote-branch-name> <remote-url> # Add the url to your remote directory to your git repo
git commit -m "Initial commit of base project"
git push <remote-branch-name> <local-branch-name>
This will establish your Baseline project on a remote repository called remote-branch-name under a branch called local-branch-name.
You can then navigate to your other projects and repeat these steps putting your repositories under different branches on the same remote, by using new local branch names, i.e. instead of using the local-branch-name when creating a branch, just use a new branch name, such as git checkout -b new-local-branch-name
so if, for example your base project push (the last line of code) was:
git push clientproject base
Where "clientproject" is the name of your remote, and "base" is the name of your local branch, you can just change the line to:
git checkout -b client1 # Creates new branch named client1
git branch -d base # Deletes base branch
git push clientproject client1
Note that while it's not strictly necessary to delete the "base" branch before continuing, it does keep your repository cleaner and is thus considered good practice. Don't worry about losing anything though, your entire git history from base will be copied to client1 on checkout.
Also note: Since your situation requires you to do this from different directories, you'll probably be deleting a branch named "master" and not "base".
Pushing like this will keep client1 on the "clientproject" remote, but will place the project under on a new branch called client1, complete with its own history.
The same steps can be used for the rest of the projects. If I've lost you anywhere along the way, I suggest reading the above link (it's much more concise than I am).
All of this must be done with Visual Studio, with Gitextensions and the Git Source provider for VS. Is it supported, or it has to be done
with the console?
I haven't yet used VS with Git, but I assume most if not all these operations would be supported since they are native git commands.
Hope this helps.

Related

Have new major version in new Git repo but keep old repo and have full history

I am going to copy Visual Studio solution that is now in repo A to new solution B and create new repo on github for it.
The A will not be developed at all. But I need whole A history to be visible in B.
How can I do it?
Can I do it in VS without installing git?
Visual Studio 2022 Pro / Windows 11
EDIT: quick summary (practical steps for #torek solution):
started VS 2022, selected Clone a Repository
set url to repo A, cloned to a local folder B
from cmd used git that VS has in its install, e.g. :
"C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\cmd\git.exe"
git remote set-url origin https://github.com/xx/B.git <-- note B
git push -u origin master
in VS renamed solution file, committed through VS
in Github checked remote repo A - no new commit visible, B - commit visible, history from A also preserved
A Git repository consists, at its heart, of two databases:
One database—usually larger by far—contains Git's commit objects and other objects. Each object has a hash ID (OID or Object ID ), which is how Git will retrieve the object. Any given commit contains a full snapshot of all the files that go with that particular commit, frozen for all time in that commit. (Git does this by making sub-objects and literally sharing identical copies of files, via de-duplication, and also compresses files, sometimes tremendously successfully, so the fact that every commit holds a full snapshot of every file doesn't bloat up the repository—some repositories are actually significantly smaller than the files they hold).
Each commit object holds a list of hash IDs of previous (parent ) commits as well. This list is typically exactly one entry long, though at least one commit (the very first one) has an empty list since there was no previous commit at that point, and merge commits have two (or potentially more) parents in this list.
This list of parents, stored in each commit, forms the commits into a Directed Acyclic Graph or DAG. The DAG, as stored in this big database, is the history. If you have the commits, you have the history. If you don't have the commits, you don't have the history. So that part is simple: just get all the commits and you'll have all the history.
A separate database contains human-readable names: branch names, tag names, remote-tracking names, and other names. Git needs to provide this to be usable by humans, because the hash IDs (OIDs) for the database objects—which Git needs to retrieve those objects from the database—are random-looking and impossible for humans to deal with. By storing particularly important hash IDs under names, Git makes it easy for you to ask Git to get you "version v2.1" or "the latest commit for branch main". Git supplies the hash ID, by looking up the stored hash ID in that name. Each name stores just one hash ID, which is sufficient due to the DAG.
So:
I am going to copy Visual Studio solution that is now in repo A to new solution B and create new repo on github for it.
Note that a VS "solution" is not just a Git repository, but as long as the entire solution is contained within a (single) Git repository, cloning the repository suffices.
To clone the original (repo A) repository, use git clone. This:
Copies the entire commits database to a new database that uses the same hash IDs (all Git hash IDs are universal: all Git software everywhere agrees to use the same OID for the same object, and this is the real magic in Git).
Reads and transforms the names out of the names database, to build a new names database. The new database keeps tag names unchanged, but turns the original repository's branch names into remote-tracking names in the new clone.
Last, does a final git switch or git checkout step to extract one particular commit from one particular branch. As the new clone has no branch names yet (remember, Git does not need these kinds of branches: it only needs the objects; it's humans who need names for them), this would normally fail, but Git will create a branch from a remote-tracking name automatically.
The branch name that Git creates, in this last step, is the one you tell it to use when you run git clone. If you don't tell it some particular one, your Git software asks the other Git software (in this case on GitHub perhaps, wherever repo A is stored anyway) which name they recommend. Your Git then creates that as a branch name, using the hash ID stored in the remote-tracking name that your Git made from their branch name. (Whew!)
This is a lot clearer with an example. If they have branches named main and develop and you clone their repository, you get an origin/main and an origin/develop: remote-tracking names that remember their branch names. If they then recommend that your Git should create main, your Git uses your origin/main—which your Git made from their main—to create your branch name main, selecting the same commit as origin/main, which selects the same commit as their main. So you now have one branch.
The history in your clone is the set of commits in your clone, as found by all the names: tag names, remote-tracking names, and the one new branch name. You may, if you desire, create additional branch names to remember specific commits; you can create a branch name for each remote-tracking name, for instance. But typically you'll probably just want main or master anyway: any commits that are currently only find-able through origin/develop, for instance, are not relevant and need not be retained.
Now that you have a clone, you can push to a new empty repo B on GitHub
It's now time to create a repository on GitHub: repo B.
You can do this with the gh command-line interface (see github-cli) from a shell like bash or zsh or PowerShell for instance, or you can do this with the web interfaces that GitHub provide. Either way you should make this as an empty repository. An empty repository has an empty pair of databases: no commits or other objects, and no names. But it still exists, even though it's empty.
Now that repo B exists on GitHub, you will use the command line interface (via bash or zsh or PowerShell or whatever, again) to tell your Git software to replace the origin URL. When you ran git clone, your Git software saved the URL for repo A, under the name origin. But you no longer want to talk to software that accesses repo A. You now want to talk to GitHub software that accesses repo B. So:
git remote set-url origin <url>
Replace the <url> part with the actual URL for repo B, whether that's https://github.com/... or ssh://git#github.com/... (either will work: the details involve GitHub setup things that you must do separately). Then use git remote -v to make sure you have set everything correctly.
Once the remote origin is your repo B, run git push --all origin to populate the two databases in the GitHub copy. This will fill in all reachable objects for all the branches you've created, and also set up all their branch names. Then run git push --tags origin to update their names database with all of your tags.
Note that commits that you have locally that are only findable via remote-tracking names (e.g., origin/develop) won't be sent to repo B here. If you want them to be sent, create a branch name:
git switch develop
for instance will notice that you don't have a develop, but you do have an origin/develop, and will use your origin/develop name and the saved commit hash ID to create a new develop using the same hash ID. You can then git push origin develop (or git push --all origin again) to create the name develop on GitHub and fill in those missing objects in their objects database.
If you create all the branch names you want before the git push --all origin step, you'll get all the objects and branch names transferred, since --all means send all branch names.
Optional: Saving space on GitHub and enabling pull requests
When you do all the above, you get an independent repo B on GitHub. That is, this new repo B has no connection to any other GitHub repository. You can't make pull requests from repo B on GitHub. If that's what you want, that's all fine.
If repo A exists on GitHub before you go to create repo B, though, GitHub offer you a way to make a full copy of repo A with one web-page button click. Using this button will copy both databases, i.e., you'll get all the objects (the full history) and all the branch and tag names. (GitHub do not use remote-tracking names, so there's nothing to copy here. Remote-tracking names are for your laptop Git repository.)
Using this button—a big green FORK button—helps them (GitHub) out by saving a bunch of disk space on their systems, and helps you out if you ever want to create pull requests. You said:
The A will not be developed at all.
which indicates that you never intend to create any pull requests to repo A. But if you ever change your mind about this, you might wish you had used the "fork the repo" button and saved space over on GitHub. That's entirely up to you, though.
A few last items
Can I do it in VS without installing git?
Probably not, but I don't use Visual Studio.
I am going to copy Visual Studio solution ...
As I understand it, a "solution" includes a .sln file. This file does go in the repository, so copying the commits will get you the original solution file. Since history—existing commits in any Git repository—is completely frozen for all time, this solution may have the wrong names in it. You cannot fix that except by replacing every historical commit with a new and different commit (that contains a different file). This is probably not worth doing, but again, I don't actually use VS.

Git setup question for shared code folder, keeping workflow speed (Win10)

Pretty new to Git - been using TFS and simple commit/push/branching, so any help appreciated - have spent all day reading and running tests and beginning to think my requirement may not be possible.
There are two of us in the office; Dev 1 doing mostly compiled C# server code, and Dev 2 mostly exclusively web page related work. However, as there are only two of us we do need to cross over fairly regularly, particularly with client Javascript functionality.
We've been doing the "mate I'm working on foo.js" method of source control for client side code, and its worked for a while, but we are doing bigger projects and it's becoming a liability.
Our set up is as below, all on an internal network:
Dev 1's machine
Dev 2's machine
Local Windows Server running IIS that serves the websites under development
Shared drive pointing to the IIS root
So, and this is the rapid development cycle I'd like to try and keep, Dev 2 browses to the site under development edits the script / css / html files on the shared drive, hits F5, and the updates are immediately visible. This is a huge benefit for fast working with client side code.
The problems usually occur when Dev 1 needs to make a change to some scripted functionality that happens to require a style change, the same files are opened and saved by both devs, and one of the change sets is lost.
So I'd like to prevent this! However as far as I understand, Git requires the devs to have local repositories so changes can be done without affecting anyone else at all, and then conflicts are merged on commit?
I have set up a test repository on the local server and tried a few scenarios, but as I kind of expected, the scenario where both devs save the same open file is not tracked because neither set of changes has been committed, so as before, only the last set of changes is visible anywhere.
Is there any way of having these type of changes to the same physical file tracked? Or if not, a setup that does track them properly but at least maintains a rapid workflow as close as possible to the above?
Use branches.
Git has a very good branch system. Just create a branch for the work you do - you can even create a branch for every feature you want to implement. And when you "finished" the implementation, merge the branch back to master. So both - and more - developers can work based on a working master version and add there code to the common codebase if it works.
So, the workflow could look like this:
Dev1 and Dev2 clone the repo: git clone ....
Dev1 works on feature A: git checkout -b A (this creates a lokal branch A)
Dev2 works on feature B: git checkout -b B
Dev2 finishes his work: git push (on the first call you get a error message about the upstream, the errormessage contains exactly the line you need to create correct upstream, just copy it)
git checkout master; git pull back to master branch and pull
git merge B this merges B into master
Dev1 need longer for the job and wants to update to newest codebase:
git checkout master; git pull; git checkout A; git merge master branch A of Dev1 is now on new codebase.
If you have to work on different features at the same time, there exists also a good system in git. Based from master branch, create a new branch in its own folder - so that both branches are checked out at the same time and there is no need to git checkout <branch> to switch between them:
git worktree add -b <branch> <path>, like git worktree add -b A ../A
now you can switch to it trough filesystem (cd ../A) and work on both (or others you created the same way)
If you use github or gitlab, you can protect the master branch and create rules to make merges into it (called pull requests). With appveyor, travis-ci and others there exists services where you can let unittests run and give the pull request free it the unittests do not fail. Based on such a workflow, every developer can work on a running codebase.
About conflicts: With the workflow up there they do not happen as long as both versions didn't modify the same line. But you get a message at the (local) merge, and in the files it is good explained what you can do:
(we create a file with a b c in each line, in master we edit b to e, in our branch A we edit b to d, we commit both and merge master into A)
a
<<<<<<< HEAD
d
=======
e
>>>>>>> master
c
Ideally, you would:
isolate the common files in one separate Git repository
separate source control (the remote Git repository) from deployment (files copied on IIS root)
That way, each of you can:
push to a common remote bare repository: configure it to deny any non-fast-forward push. In case of concurrent pushes, you will be forced to pull first, resolve any conflict locally, then push back: there won't be any change overridden or lost that way.
setup a server-side hook in order to (on the server) pull from said bare repository, through a post-receive hook (example here).
reference that common repository in your own development repo through a Git submodule.
The goal is to keep separate:
project-specific development from common client Javascript functionality.
versionning from deployment.
The problems usually occur when Dev 1 needs to make a change to some scripted functionality that happens to require a style change, the same files are opened and saved by both devs, and one of the change sets is lost.
…
Is there any way of having these type of changes to the same physical file tracked?
To get this you need some sort of collaborative editor, that's out-of-scope for any existing vcs I know of.
Or if not, a setup that does track them properly but at least maintains a rapid workflow as close as possible to the above?
You need separate files, separate saves (i.e. a vcs) and a workflow that automates as much as possible of the pull-and-push publishing loop.
Since you're not working on the same physical files, before publishing you need to sync your changes with whatever the other guy(s) on your team have published since last you looked. Decide how you want your final history to look; for small-team work like this rebasing onto a shared linear history is often a great place to start, so git config pull.rebase true. Then when you're ready to publish the changes you've saved, commit, pull, push is your cycle; if you and your buddy are making changes even in the same file it'll still apply cleanly in one go so long as the changes aren't immediately-adjacent or overlapping.

How do I allow 2 people to work on the same local copy of a git repository? Or can I?

This might be verbose, but I'm just starting out with git, so I'm still learning here.
Before now, I've had a team of developers all working off of the same hard drive with a local copy of all of our dev files. So we have 1 hard disk and 4 developers. All of us use sublime text and work on the projects together. We all work in the same room, so it's never been an issue to work on the same project at the same time. We just don't work on the same file at the same time. Not the greatest system, but it worked at the time.
Now we want to introduce git to the team for all of the reasons a VCS is important. The problem we're running into is files being locked by one user and no one else can use git on that repository.
Here's an example. I log into my mac in the morning and make some changes to files in Project X located at /Volumes/dev/projectx/. I open terminal and commit those changes to the local repository (stored also at /Volumes/dev/projectx/). My coworker gets in and logs into his mac. He opens up his terminal to check the status on the repo he's working on. So he moves into /Volumes/dev/projectx/ and runs git status. He gets an error message that says the index is locked. In order to allow him to run any git commands on the repo, I have to completely log out (maybe just kill some processes, but I don't know which ones). After I log out, he can work as though there's no problem.
Is there any way that we can both work on the same local repository at the same time?
I've also discovered that, if I'm working in a project that has a git repository in it and anyone else even opens the project folder in a finder window, it completely locks me out from using that git repo (same index lock error).
We're willing to change the way we work on files, but since there are literally thousands of projects on the drive, it isn't really practical for each of us to have our own local copies of all of the files. Also, since many of the changes are a very simple text change of some kind, it seems tedious to host all of these repositories remotely and have to pull down all the files anytime we only want to update a single file.
I'm really looking for workflow suggestions here, but the question I asked is kind of the starting point here.
The whole point of using git is that you don't have to do this kind of crazy stuff.
I know what you've said about why you don't think you should all have complete copies. Here's the hard truth. You're wrong. Mostly. But that's ok, you said your willing to rethink how you work and that's good. Ill try to explain why its not that big a deal to have everyone use their own clones.
A assume all code is already in a remote repository - if its not, sign up on http://github.com or http://bitbucket.com and get a free repository, add it as a remote to your git repo, and push it up. Its really very simple.
Each of your developers should then make their own directory locally on their machines where they can clone the whole repository.
git clone http://github.com/yuoraccount/yourrepo ~/clones/localproject
The first time they clone, it will take a little time to download everything, but from then on, only each minor diff will need to be downloaded uploaded. Git is made to be efficient that way.
When you make a change, commit it, and push it up.
git commit -am "i made a small change"
git push origin master
Then everyone can pull it down.
git pull origin master
You can even all work on different branches, so your not just pushing to the same branch. This should all be really simple, and very easy to do.
You can also split your project into multiple repositories, but you don't really need to. Thousands of files is not a big deal, git can handle it without a problem. That's not to say that you won't have some challenges. Git is easy to use, but you'll eventually run into merge conflicts. They will be a little bit frustrating at first, but stack overflow has a thousand answers explaining how to deal with them - you should be fine.
This is what git is for.
Git is a decentralized version control system. The way you want to use git could be described as the opposite of decentralized.
There are a number of perfectly reasonable workflows possible with git, but all of them are going to involve each developer working in a local clone of some repo.
i guess that your "single harddisk" is really a single "network storage" that can be accessed concurrently. otherwise i don't see the problem (the harddisk can only be attached to a single computer at any time; whenever you unplug the disk, any locks should be released!)
anyhow, though you currently consider it impractical, you still might consider using per-user clones of the repositories.
the normal workflow of a multi-user repository would consist of a local clone of each repository on each developer machine (that is: a clone on your personal mac).
then the trouble with concurrently accessing the locked central repository doesn't exist!
it's exactly the use-case for which git was designed.
this would allow your users to work on the repository even without having access to your central network storage.
if you are concerned about disk-space, you might be happy to hear that git does a pretty decent job of compressing the data (just run git gc every once in a while in your repository).
if (for whatever reasons) you cannot have local copies (on the dev-machine) of the repositories, you might consider having per-user clones on the central storage.
git can use hardlinks for local clones, so disk-space should not be an issue here.
Section 4 of the Pro Git book (version 2) is about "Git on the Server" and gives some information about what the OP is trying to achieve.
The OP's situation calls for the "Local Protocol".
Here is how this works:
1. Create a local remote from your project
cd into your project and create the remote in your preferred location (in your example /Volumes/dev/projectx):
git remote add origin /Volumes/dev/projectx.git
2. Set the permissions for the remote
git init --bare --shared /Volumes/dev/projectx.git
This will set the permissions properly so that different users can push and pull to/from the remote. This command does not affect your Git history and is safe to run.
The --bare flag is used because all that is needed for a remote is a bare repository, that is, a repository without a working directory (a repository with only the .git directory and nothing else).
Note: this step is not described in the Git Book for the Local Remote protocol and is only suggested when setting up a Git server, but in the experience of a colleague, without using this step, the permissions were not set properly.
3. Push the data to your new remote
Assuming you have a branch called main (do this for any branch you want your collaborators to have access to):
git push --set-upstream origin main
4. Have your collaborators clone the remote
This works as with any remote:
git clone /Volumes/dev/projectx.git
From here on, everybody can push and pull to/from the local remote.

Updating multiple local working copies using a single GitHub source?

Switching to GitHub and can't seem to find information on how to do something I used to do with SVN:
First of al I'm a tester so I never commit changes, but I do need to keep a number of local copies of the project each with a small difference to trunk - staging, production, decreased timer values etc
I used to use CornerStone on Mac (just fwiw/don't have it any more), and a single update from SVN would update all my various local 'dependent projects' (all of them the same, apart from one or two lines, usually in a config file). I'd review the inevitable conflicts and almost always reject them, provided the only difference in the conflicting file was my customisation for test purposes, or if there was something new I'd merge it in and leave my staging environment URLs or whatever as they were.
Can I do this sort of thing efficiently with GitHub? (preferably using GitHub's/Xcode's or some other Mac GUI client?)
I did look at the documentation, but can't seem to find anything on this so any help much appreciated/maybe it's kind of unusual to be pulling changes to lots of almost-identical local projects and always be rejecting certain conflicts.. :)
Of course, the ideal solution is to not have configuration data tracked in the repository, or at least make it possible to override via some untracked file.
However, there's no problem with what you want to do in git. You just locally make multiple clones of your GitHub repository. In each, you can make your changes specific to that local copy, and then commit them. Then, when you want to update from the latest version of GitHub, make sure that you pull with:
git pull --rebase
... which will fetch the latest version from the server, and then try to reapply your commits with local changes on top of them. Unless the same parts of the files that you've committed changes to have also been modified in the new commits on GitHub, you shouldn't even have conflicts to resolve.
If you always pull like this, then the commit graph (history) of your project will always be the same as the remote, but with any of your local changes as the most recent commits on top of the remote history.
(I would hope that the GUI clients that you are using have an option to rebase on pulling new changes. Otherwise you can set this to happen automatically with the config option branch.<name>.rebase, where <name> is the name of your local branch.)

how can I work on both default and branch at same time in Hg?

OK, I'm new to Mercurial and version control branching in general, so I may have a fundamental misunderstanding of what's going on here -- please be kind... ;)
We are a small development team (2 developers) working on a project, and we have a need to implement a fairly significant change that may take weeks or months. At the same time, the program is in daily use, so we have a need to make regular patches and fixes.
Because of the long-running nature of the significant change, I created a branch off the default branch (call it dev1). I will want to periodically merge the changes from the default branch into the dev1 branch, for reasons that don't need to be reiterated here. However, I do NOT want the changes from dev1 merged into the default branch until much later in the development.
I have tried several different ways to do this, but it always seems the merge affects both branches. After the merge, if I update to the default I now have changes from dev1 merged into the source.
Can I work on both branches using the same repository? If so, can someone please share the sequence of commands to use? If not, it seems to me I would not be able to push the dev1 branch up to the master repo until it was finished, and that just doesn't seem right.
We are running the latest TortoiseHg for Windows, and for the most part I love the graphical tool. However, I am perfectly willing to drop to the command line to do certain tasks when necessary.
Thanks for any help,
Dave
This depends on what sort of branch you've created.
If you have created a named branch, and are working in a single working directory, then you need to use one workflow, but if you have cloned your production repository, you need to use a different workflow.
Named branch workflow, single repo/working directory
In this case, you are using update to switch between the default branch and the dev1 feature branch.
When you want to work on the default branch, update to it, do your bug fixes, and commit those changes. Do not merge in changes from dev1 branch.
When you want to work on your dev1 branch, update to it, merge in your bug fixes from the default branch, work on your feature and commit when done.
If you are working on the dev1 branch and a colleague fixes a bug in default that you need, commit your work, fetch their changes, merge them in and then resume your work (there are shortcuts you can take here, but this way you can backout the merge if it gets messy)
Note: All of these assume that all of your changes are committed at the point you want to switch between dev1 and default branches.
The important thing to note is that you only get the changes from your dev1 branch in default when you merge them in. If you only merge default into dev1 then your feature branch will keep up to date with default so that when you are ready to deploy the feature into the default branch, you can do so with one simple merge operation.
Unnamed branch workflow using dev1 repo cloned from production repo
This workflow is similar, but allows you to work on the default and dev1 branches simultaneously, without having to update to switch between the two.
When you want to work on the default branch, use the repository where the tip is your production code. Do your bug fixes, and commit those changes just as you would normally.
When you want to work on your dev1 branch, use the repository where the tip is your dev1 feature branch. If there have been fixes in the default repository, pull in the changes and merge them into your clone, but do not push the merge changeset back. Only push your changeset back when you want to deploy you feature to production code. Once the changesets from default have been merged in, you can continue working on the feature.
If you are working on the dev1 branch and a colleague fixes a bug in default that you need, commit your work, fetch their changes from your shared repository into your default production clone, then pull those changes down into your dev1 feature clone, merge them in and then resume your work.
Again, the important thing to note is that you only get the changes from your dev1 branch in default when you push them up to your default production repository. If you only pull/merge default changesets into the dev1 clone then your feature branch will keep up to date with default so that when you are ready to deploy the feature into the default branch, you can do so with one simple push operation.
Yes, you can absolutely do this with Mercurial.
First, in case it isn't clear to you (it wasn't to me for some time), there are 3 types of 'branches' in Mercurial:
clone a repository
a 'named branch' (using the hg branch command)
an anonymous branch, which you can manage with bookmarks or just remembering the changeset
I'm guessing that you used the hg branch method. Be aware that this is often not what you want, because that branch name will live in the repo's history forever (well, there is the --close-branch option, but still...).
The essential workflow is:
update to dev branch with hg up devbranch
commit changes to dev branch
merge with main branch via hg merge default or just hg merge as desired
(repeat as desired)
And for working on the default branch:
update to default branch with hg up default
commit changes
(repeat as desired)
Do NOT do this:
update to default branch with hg up default
merge with dev branch with hg merge
I suspect that you are using the command hg merge without specifying a branch name. That will merge with any other head, which may or may not be what you want.
Edit: The above info is probably not your issue. Your issue is probably running a merge when your current branch is the default one.
You don't want to run hg merge from your default branch.
# bang on dev1
# more banging on dev1
# someone beats on default for a while
# update to dev1
hg up dev1
# bring in the changes from default
hg merge -r default
# validate successful merge
hg commit -m "merging"
The key is committing on dev1 when you bring changes over from default.
Note that I'm using named branches here.
This sentence:
After the merge, if I update to the default I now have changes from dev1 merged into the source.
tells me that you're doing something wrong. It is perfectly doable what you want to do, work on two branches in parallel, and merge from one to the other, without influencing both.
It is important to know that the merge is a directional merge. You merge from one branch to the other, and when you initiate the merge, you should be on the to-branch.
directional in the sense that the direction plays a role in the outcome. For the actual contents of the file, it doesn't matter which direction you merge, but the new merge-changeset you commit will be on the branch you was on when you initiated the merge (unless you override.)
So, update to the head of dev1 first, then merge with default, and after committing, you should have a new changeset on the dev1 branch, but default should be left undisturbed.
This is more of a tip than an answer, but...
I use this workflow a lot. I find the Transplant extension very useful for named branch workflows. TortoiseHg supports it, so you can enable it in the TortoiseHg options. It lets you cherry-pick from other branches, which is very useful - especially if you regularly commit to the wrong branch.

Resources