How to use 'hg mv' in a teamwork environment? - version-control

I'm part of a team, my current task is restructure, i.e. move around a bunch of files/folders.
I would like to use 'hg mv', but then how to sync it with team work? remember that team is currently working on those files i need to move around. would they be able to pull/update after i push the restructure? Would hg be able to merge their local changes even though i moved their files?
Another issue is 'hg up' in my end before the restructure is being pushed. Can hg deal with incoming modifications on files i've moved, but not yet pushed?

Let's make three repositories named main, tom and mine to understand what happens. main is a central repository where you and Tom(one of your co-worker) push changes and use it to collaborate. tom is Tom's repository and mine is your repository. tom and mine will obviously be clones of main.
$ hg init main
$ hg clone main tom
$ hg clone main mine
Let's have a file to play with. Tom created a file and pushed it to the main repository.
$ cd tom
tom$ echo 'print hello' > a.py
tom$ hg add
tom$ hg ci -m "Added a.py"
tom$ hg push
pushing to /home/main
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
Now you get started by pulling the changes.
$ cd mine
mine$ hg pull
pulling from /home/main
requesting all changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)
mine$ hg up
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
Now you notice that Tom is not good at naming as he named the file as a.py which is least descriptive. You decided to rename it to hello.py.
mine$ hg mv a.py hello.py
mine$ hg ci -m "Rename a.py to hello.py"
At the same time, Tom ran a.py and realised that quotes (print hello must be print "hello") are missing. He fixed that and pushed another commit. You decided to pull that one before you push.
mine$ hg pull
pulling from /home/main
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
mine$ hg merge
merging hello.py and a.py to hello.py
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
mine$ hg ci -m "Merge commit"
Now when you look at hello.py, you find the fix by Tom is present and you decided to push.
mine$ cat hello.py
print "hello"
mine$ hg push
pushing to /home/main
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 1 files
Let's switch back and see how things will be happening after this push on Tom's side. Tom made one more change locally which he didn't pushed. The change was about adding parentheses to print function.
tom$ hg pull
pulling from /home/main
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
tom$ hg merge
merging a.py and hello.py to hello.py
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
tom$ hg ci -m "Merge heads"
tom$ hg push
pushing to /home/main
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files
When you will pull this time, your are Tom repository will have the same state with file renamed and all the changes intact.
mine$ hg pull
pulling from /home/main
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files
(run 'hg update' to get a working copy)
$ hg up
$ cat hello.py
print("hello")
You can see that the change which added parentheses is also there. So its very safe renaming files in Mercurial using hg mv.

Related

Git in Xcode 6.3: Master branch showing new files from other branches in red and won't compile

I am using Xcode's integrated Source Control with Git and I have the following problem:
I have a perfectly working master branch and I want to work on two new features. So I create two new branches, where I add one new file at each branch.
Now when I switch back to the master branch or the other branch, after committing the changes and without merging (I don't want to merge yet), the files from ALL the branches appear in the project navigator (the ones that don't belong to the current branch are in red colour) and prevent my code from compiling as the compiler complains that these files don't exist.
My master at least should compile regardless of what I've done in other branches right?
Am I missing something trivial here?
Untracked files and unstaged changes do not belong to any branch. They only live in your working tree. When you switch branches, those files/changes are left untouched. If you want them to exist only in a certain branch, you have to add and commit all of them.
This should help understanding:
$ git status
On branch master
nothing to commit, working directory clean
$ >>file echo 'added line'
$ touch new_file
$ git status
On branch master
Changes not staged for commit:
modified: file
Untracked files:
new_file
$ git checkout -b new_branch
M file
Switch to a new branch 'new_branch'
$ git status
On branch new_branch
Changes not staged for commit:
modified: file
Untracked files:
new_file
So, as you see, unstaged changes are carried over when switching branches (this is by design). When checking out another branch, Git tells you which files contain changes (the line starting with Modified). This also holds for untracked files (Git cannot delete them, since you might need them), but their names are not explicitely output when checking out a branch.
To have Git delete files when switching a branch, you have to add (and commit!) them to a branch first.

Move a specific branch to new repository

I have a mercurial repository in which I had created a branch 7-8 months back. And now this branch is the one in which I do most of the development and I don't have anything fruitful in default branch and other branches that I have.
I want to create a new repository that represent only this branch. i.e. I want to move this branch to a new repository with history.
I tried to use HG convert tool with following syntax:
hg convert --filemap ~filemap.txt --branchmap branchmap.txt --source-type hg --dest-type hg "E:\MyStuff\Dev\MyOldRepo" "E:\NewRepo"
File map I have defined all my file that I want to include. In branchmap file i had defined
MyOldNamedBranch default
Convert tool do rename MyOldNamedBranch to default but it also brings the changesets from other branch that I don't need.
I also tried to set the following in setting file but no results:
[convert]
hg.usebranchnames=0
hg.startrev=5262
Please suggest how I can move a branch to new repository with history and leaving other branches behind.
I have set the start revision number in command only and it worked.
hg convert --config convert.hg.startrev=5262 --branchmap branchmap.txt "E:\MyStuff\Dev\MyOldRepo" "E:\NewRepo"
And it worked like a charm.
Try this:
Clone only the branch you need:
hg clone E:\MyStuff\Dev\MyOldRepo -b MyOldNamedBranch .\NewRepo
Then inside the NewRepo, convert all the changesets to the draft phase:
hg phase -r 0 -d -f
Then update to the patent of MyOldBranch (I assume, that the parent is in the default branch)
hg update -r "parents(min(branch(MyOldBranch)))"
Then rebase MyOldBranch on the exactly the same changeset.
hg rebase -s "min(branch(MyOldBranch))" -d .
Do exactly the same with the rest of the branches.
To be honest I'm not sure if this is the best method but it worked for me.

Mercurial merge creates new head in subrepos

I am working on a large project, with a main repository and multiple subrepos, organized like this:
main-repo
sub-a
sub-b
sub-c
Each person working has one or more named branches of their own. I am working in a branch that we will call shastings-dev and a co-worker has a branch that we will call coworker-dev. (Both of these branches exist in main-repo, sub-a, and sub-b; neither exists in sub-c and we are both using someone else's branch for that one.)
My problem is that I can no longer merge changes from coworker-dev without creating a new head somehow. This sequence of commands has the unexpected effect:
hg pull
hg branch # prints: shastings-dev
hg merge --tool internal:other coworker-dev
hg commit -m "merge coworker-dev" --subrepos
hg push
The hg push prints this message:
abort: push creates new remote head 945a60694252 on branch 'shastings-dev'! (in subrepo sub-a)
EDIT -- I am breaking the question here and putting the answer. The rest of the question will be left intact below for anyone who is really interested in all the details.
The solution: for each subrepo that needed a merge, change directory to the subrepo and follow these steps:
hg update --clean shastings-dev
hg merge --tool internal:other coworker-dev
hg commit -m "merge coworker-dev"
hg push
(I did this for subrepos sub-a and sub-b. For sub-c I just ran hg update correct_branch_name to make sure that it was at the head as well.)
Then, once each subrepo has been properly updated, return to the containing repo, and:
hg commit -m "commit .hgsubstate after merging in subrepos"
hg push
As a result of this experience, I am going to adopt a new rule:
If you have problems with subrepos, always retry the commands in the individual subrepos.
What confused me was that hg update --clean switched all the subrepos, but didn't actually update them. The heads for the subrepos belonged to a different branch than the one showing with hg branch. As a result, #zerkms's comment was correct: the subrepos actually were not on the head and the merge really was creating a new head.
In other words, even if hg id shows that the main repo is at the head, that doesn't mean the subrepos updated properly and are also at their respective heads. Check the subrepos individually to figure out the real state of things.
EDIT -- rest of original question is below.
My first thought was that my local copy of the repo was somehow scrambled. So I cloned an absolutely fresh copy of the repo, verified that hg pull didn't bring anything new in and tried the merge/commit/push again. Same problem.
I have tried doing the merge/commit/push in the subrepo then doing commit in the main repo (to update .hgsubstate) and then doing merge/commit/push in the main repo. No joy.
I have tried using TortoiseHg on Windows to clone the repo, update to branch shastings-dev, and then merge/commit/push. Same problem.
So now my questions:
Why is a new head being created? Am I doing something wrong, is there something wrong with my branch, or is this expected behavior?
What should I do? Can I eliminate this multiple heads problem, or should I push with -f and then close the older of the two heads with hg commit --close-branch?
What bad things might happen if I use hg push -f? My personal rule is "don't do that unless you are a Mercurial expert" and I'm not a Mercurial expert. (So my actual personal rule is "never use hg push -f.")
By the way, I am not working on any files that coworker is working on. At this time I have only minor differences from coworker-dev and I would be willing to totally lose all history on branch shastings-dev, as long as I can get a working up-to-date branch with the same name. (If I lose my changes I am willing to copy the changed files over again. I just want Mercurial to work as expected again.)
EDIT: I am on a branch head when I do the above.
$ hg heads
changeset: 1515:803ea844dc8a
branch: coworker2-dev
tag: tip
user: coworker2
date: Tue Oct 08 17:33:31 2013 -0700
files: .hgsubstate
description:
Fixed some stuff in the foo bar.
changeset: 1513:1e76e6a43d83
branch: coworker-dev
parent: 1509:5e5392aded0a
user: coworker#place_where_i_work.com
date: Tue Oct 08 16:44:04 2013 -0700
files: foo.java bar.java baz.java quux.java
description:
Added more support to the foo bar for release.
changeset: 1422:8705d62db8f2
branch: shastings-dev
user: shastings#place_where_i_work.com
date: Wed Oct 02 21:08:39 2013 -0700
files: .hgsubstate
description:
Finish adding files
...many others not copied here...
$ hg id
8705d62db8f2 (shastings-dev)
$ hg update --clean shastings-dev
resolving manifests
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg id
8705d62db8f2 (shastings-dev)
$ hg merge --tool internal:other coworker-dev
...much output not copied here...
$ hg commit -m "merge coworker-dev" --subrepos
...much output not copied here...
$ hg push
pushing to https://path/to/repo/dir/main-repo
pushing subrepo sub-a to https://path/to/repo/dir/sub-a
searching for changes
new remote heads on branch 'shastings-dev'
new remote head 85d8faada6c4
new remote head 90ce145db695
abort: push creates new remote head 85d8faada6c4 on branch 'shastings-dev'! (in subrepo sub-a)
(you should pull and merge or use push -f to force)
As I said, when I had problems, I re-cloned everything. So the status is clean: I have no modified files.
Also, I have one other branch, and I tried the above sequence with the other branch. It happened again just as described. So, if anything is wrong, I figure it must be wrong in branch coworker-dev and not in my own branches.
Why is a new head being created? Am I doing something wrong, is there something wrong with my branch, or is this expected behavior?
You're not on the branch head, so merge creates another one
-- A (you're here) --- B --- C (head #1)
\
D (merge, head #2)
So you need to make sure you're on the head using hg id and hg heads. And if no - hg up
What should I do? Can I eliminate this multiple heads problem, or should I push with -f and then close the older of the two heads with hg commit --close-branch?
You should solve the real issue. See the advice above
What bad things might happen if I use hg push -f? My personal rule is "don't do that unless you are a Mercurial expert" and I'm not a Mercurial expert. (So my actual personal rule is "never use hg push -f.")
My personal rule is - to never use -f. After 4 years of experience with mercurial I don't see any good reason to use it at all. What may happen - as soon as there are 2 heads, what would hg up branchname do? What would hg up do?

Subclipse tree conflicts

I'm trying to merge a trunk to a branch, but ending up with a lot of tree conflicts, leaving no files merged. To resolve the conflicts, I'm just opening the file and copying contents by hand which just defeats the purpose of a merge operation.
What is the right way to merge a trunk to a branch (in subclipse) ?
How was that branch created? Was it created by using svn cp, or were those files manually copied into that branch?
Let's look at the following:
$ svn mkdir trunk
$ vi trunk/foo trunk/bar
$ svn add trunk/foo trunk/bar
$ svn commit -m"Added foo and bar to trunk"
You now have two files on trunk.
$ svn mkdir --parents branches/1.0
$ cp trunk/* branches/1.0/
$ svn add branches/1.0/*
$ svn commit -m"Duplicated files onto branch"
What I have done is create two entirely different foo and bar on the 1.0 branch. These two files, according to Subversion have absolutely nothing to do with each other. If you make a change on the 1.0 branch, and attempt to merge these changes back to trunk, you will get a lot of conflicts with messages like "local add, incoming add".
What the above user should have done is this:
$ svn cp --parents trunk branches/1.0
$ svn commit -m"Branched trunk and not merely duplicate files"
Now, there's a relationship that Subversion understands between the files on trunk and on the 1.0 branch. Merging will go smoothly.
Here's another way to break a merge:
$ svn delete trunk/foo
$ svn commit -"deleted foo"
$ svn cat -rPREV trunk/foo#PREV > foo
$ svn add foo
$ svn commit -m"Added foo back in. Shouldn't have deleted it.
According to Subversion, there are now two completely different files named foo in the trunk. There's the file you deleted, and there's the file you added. These two files have nothing to do with each other. Imagine if I branched (the correct way using svn cp) to the 1.0 branch, then did my delete and copy of foo. The merge of the 1.0 branch back to trunk will have a conflict because the foo on the branch has no relationship with the foo on trunk.
To restore a file, you need to copy the revision that was deleted (or use svn merge -c).
$ svn cp -rPREV http://svn.repo/svn/trunk/foo#PREV .
$ svn commit -m"Actually old foo now has been restored! Merges will work"
If you branched incorrectly, or deleted and re-added files back to trunk, you will get conflicts. You can try using the --ignore-ancestory parameter, and you can use --dry-run to test your merge before running the actual merge.
If you manually merge, you can use svn merge --record-only to just record the fact you did a merge without actually doing one. This might help the next time you do a merge since you're at least recoding what you've manually done.

Mercurial: Merging one file between branches in one repo

When I have two branches in Hg repo, how to merge only one file with another branch, without having all other files from changeset merged?
Is it possible to merge only certain files, instead of whole changeset?
WARNING: a "dummy merge", as is recommended by #Martin_Geisler, can really mess you up, if later you want to do a true merge of the two branches. The dummy merge will be recorded, and say that you merge into the branch you did the dummy merge to -- you will not see the changes. Or if you merge into the other branch, the changes on that other branch will be undone.
If all you want is to copy an entire file from one branch to another, you can simply do:
hg update -r to-branch
hg revert -r from-branch file
hg ci -m 'copied single file from from-branch to to-branch
If you want to select different parts of that file, then "hg record" is useful.
I just did this on my home directory .hgignore.
If both branches have made changes to a file that you want to keep, a dirty trick would be to create a merge of the two branches using hg merge, possibly/probably on still another branch, check that in, and then copy a single file between the merge and the to-branch:
hg update -r to-branch
branch merge-branch
hg merge -r from-branch
hg ci -m 'temp merge to be discarded"
hg update -r to-branch
hg revert -r merge-branch single-file
hg ci -m 'merged single-file from from-branch to to-branch"
hg strip merge-branch
It is worth mentioning: the way to "copy a single file between branches" (or revisions, or from revision to merge, or....) is "hg revert". I.e.
hg update -r Where-you-want-to-copy-to
hg revert -r Where-you-want-to-copy-from file-you-want-to-copy
...
hg ci
For some reason I, and some of my coworkers, find this VERY confusing. "revert"=="copy" ... makes sense for some usage patterns, but not all.
Nope. Mercurial works on a changeset basis.
But you can do a "dummy merge" where you ignore the incoming changes from one of the branches. Before you commit you could then revert selected files to whatever state you want:
% HGMERGE=internal:local hg merge # keep my files
% hg revert --rev other-branch a.txt # update a.txt to other branch
% hg commit -m 'Dummy merge to pick a.txt from other-branch.'
Maybe that will help you a bit.
One fairly clean way of getting the desired result is to do it in two steps: first use graft, then second use histedit.
Say this is the starting point and you need to select some portions of C and D to "merge" after E:
A---B---C---D
\
-E
Then you would graft C and D on top of E:
A---B---C---D
\
-E--C'--D'
Then use hg histedit to edit C' and D'. During the edit you can make any changes you want, but in this case you would just revert any unwanted files, (or even portions of them).
(Note that histedit edit works by temporarily updating your working folder to match the content of the given changeset as though it were not committed yet. So you can easily revert unwanted files and then hg histedit --continue which will effectively replace the edited changeset.)
So the final result would be:
A---B---C---D
\
-E--C''--D''
Where the '' revisions were modified as required.
I would say this approach is more beneficial when you have large changesets that probably should have been multiple smaller commits in the first place; this approach allows you to "disentangle" only the parts that you need. Using this for just a single file would be fine but could be overkill.
I would just use an external tool like vimdiff to diff the two files that I want to merge and then merge them. The advantage of this is that you can do selective editing on parts of the file. E.g:
hg update -r branch-merging-to
hg extdiff -p vimdiff -r branch-merging-from file-I-am-merging
To do this you need to enable the external tools in your .hgrc, which just means adding these lines:
[extensions]
hgext.extdiff =
If you are using an IDE:
Merge the old branch with new branch
Go inside the the IDE and remove the unwanted changes
Generate the diff file
Update and clean the new branch
Apply the diff in the new branch