Mercurial: Merging one file between branches in one repo
Asked Answered
S

5

54

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?

Scandic answered 3/7, 2009 at 11:9 Comment(0)
K
62

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.

Kanaka answered 26/10, 2012 at 4:19 Comment(11)
This should be the accepted answer instead of the one Martin Geisler gave. While it will not actually merge the file, copying it is what one will want to do most of the time and it will not mess up the branch.Abrahamsen
This worked great for bringing a single file from a branch to the trunk. One thing I notice, however, is that the checkin to the trunk does not appear to show up in the file's history, which is unfortunate.Poussin
hello and thanks for your answer. I am having trouble however, in my situation my source branch contains a file that doesn't exist in my default branch, but i want to copy it back without merging the rest of the branch. When i try to do what you described i get 'no such file in rev' Any suggestions?Celanese
So the workaround for my issue is to copy the files manually and then run the copy commands, but i wonder if there's a solution for new files that don't exist in the destination branch?Celanese
@propagated: branch #1 and #2. In branch#2 you have created the file, Foo. To propagate Foo to branch#1. If adding file Foo was the only change, you do a merge. But if there are other things on Branch#2 that you do not want to merge at this time, and which you do not want a whole repo merge to misremember - i.e. if you just want to copy a file (or files) from Branch#2 to Branch#1, then do: hg update -r Branch#1; hg revert -r Branch#2 Foo; hg ci. The biggest pain about this is that filename completion doesn't work, since file Foo does not yet exist in Branch#1.Kanaka
@Brad_Oestreicher: yes, it is unfortunate that there is no indication in the Mercurial history of what happened, apart from whatever comment you provided. E.g. I depend on "hg glog" for nice tree diagrams. Ot would be nice if a line could be drawn between the revisions between which the file is copied. I.e. it would be nice to track partial merges as well as full merges. Too many DVCSes have a "whole repository only" viewpoint.Kanaka
@KrazyGlew which DVCS doesn't?Dragoon
Is there a way to rename the copied file in one go? With hg revert, I think it can only be hg mv-ed after a hg commit (for the revert). So like this it's two commits.Dragoon
@Dragoon Q: Is there a way to rename a copied file in one go? A: I don't know - I am not using Mercurial right now. But even if not: why care? Start a (sub)branch, copy the file, rename it, and then merge back onto the parent branch => only one step on the parent branch, although more than one in reality. Not quite as nice, but general approach.Kanaka
@Dragoon Q: which DVCS doesn't [have a whole repo viewpoint]? A1: I know of no current DVCS that is not whole repo (except for various imports which are painful). A2: CVS is not a DVCS, but supports partial checkouts and checkins. A3: My cvs-file-merge made CVS into a DVCS - but I stopped work on that when Linus announced git. A4: Linus told me that git's underlying structure could support partial co/ci and other subrepo stuff, but that it would be a big change to some porcelain.Kanaka
Is the file added again to the repository as the result? So will it consume twice the space after such operation? Or will Hg manage to see that the file is already in the repo?Ardine
P
19

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.

Permutation answered 3/7, 2009 at 14:2 Comment(4)
WARNING: such a "dummy merge" 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 "hg update -r to-branch; hg revert -r file" // if you want to select different parts of that file, then "hg record" is useful. // I just did this on my home directory .hgignore.Kanaka
A dirty trick would be to create a merge of the two branches using hg merge, check that in, and then copy a single file between the merge and the to-branch using "hg update -t 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-u-want; hg ci -m 'merged single file from from-branch to to-branch"; hg strip merge-branch.Kanaka
Oh, heck, I might as well make my comments into a real answer.Kanaka
You can specify the merge tool (such as internal:local) using the --tool switch too.Ephemera
E
2

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.

Emmery answered 10/6, 2021 at 16:55 Comment(0)
B
1

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 =  
Bigname answered 20/3, 2015 at 11:11 Comment(0)
K
0

If you are using an IDE:

  1. Merge the old branch with new branch
  2. Go inside the the IDE and remove the unwanted changes
  3. Generate the diff file
  4. Update and clean the new branch
  5. Apply the diff in the new branch
Kiaochow answered 25/3, 2022 at 6:31 Comment(1)
Sounds just like a more complicated variant of "manually copy everything you want to your destination branch".Whitelaw

© 2022 - 2024 — McMap. All rights reserved.