简体   繁体   中英

Is there any way to migrate a pull request from a bitbucket hg repo to a github git repo?

I have an old pull request lying around in my bitbucket hg repo that I'd like to try and integrate, but in the mean time I've migrated the repo to github and converted it to git. Is there any way to migrate this pull request? If not, what's the least painful way to go about integrating it?

I mentioned the basic process in a comment . Essentially, you want your original Mercurial repository, with its changesets that you sent to the original Bitbucket servers to make a PR.

(Note: I have not actually done this. This is all theoretical. The theory is pretty straightforward, though. This same technique would work with a conversion from CVS or SVN or Perforce as well. The key is to build a temporary Git repository with the right commits leading up to, and then including, the set of to-be-copied PR commits. We'll then copy just the PR commits to the Git repository that's linked to the GitHub repository.)


The best (or shortest or easiest) way to do this is likely going to vary somewhat, depending on some fine detail. Presumably, however, you have, or at least can clone, the entire original Mercurial repo and its pull request from bitbucket. This will give you a complete, self-contained Hg repository with a bunch of changesets, including all the changesets that were originally converted to Git, plus your changesets since then that are part of your PR.

You can now convert this whole thing to a new Git repository, by whatever means you like. This Git repository will have one commit per original Mercurial changeset, more or less. (The "more or less" happens when you deliberately don't convert some hg commits and/or where you have hg tags, which result in new commits, that are converted to Git tags, which don't use commits. Some details are likely to depend on your conversion software.)

This new Git repository might be completely unrelated to the one on GitHub, with different commit hashes from the original conversion to Git. Or, if the conversion was straightforward, when you build this new Git repository, it will actually turn out to use the original hash IDs and hence be strongly related!

If it really is that closely related, the problem virtually vanishes. Just set up a remote for this Git repository that is the GitHub repository (or your fork thereof) and git push the additional commits there and make a GitHub PR as usual.

If it's not closely related, what you most likely should do—this seems like it would be the easiest path—is to use git cherry-pick . There are a lot of repositories involved here, so let's use some simple one-letter names to name each one:

  • G main is the GitHub repository (or your fork of it), to which you want to make a new PR.

  • G local is your clone of G main on your own computer. (I'll say "your laptop" for short, even if you're not actually using a laptop, to distinguish it from the, perhaps many, Git repositories on various servers.)

  • R 0 is your Mercurial repository, with the extra commits in it, that you already have or re-cloned from the PR on Bitbucket.

  • R g is your repository after conversion to Git. This repository can just live right on your laptop. Note that it has the new commits on some branch, almost—but not quite, in this case—ready to be git push ed. The problem is that these commits are on a history that is not related to the history in G main .

In G local , on your laptop, you will now use git remote add to add a remote for R g :

git remote add recoverer <url>

The is just going to be the path to R g on your laptop. (You can put file:/// in front of it if you like, although if you don't—if you just use the full path to R g directly—your Git will be cleverer about not using extra disk space. If for some reason R g is on some server, use a URL that allows your Git to reach that server, and your Git will need to allocate new local disk space to copy in all the commits, for a while.)

Now run:

git fetch recoverer

This will fill your G local with commits—the entire history—from R g , creating remote-tracking names of the form recoverer/origin , recoverer/develop , and so on. The commits you want—except for their incorrect history linkage—are now on a remote-tracking name. For concreteness I'll call this recoverer/pr123 , but the actual name depends on what branch or bookmark name you used in R 0 that got created by your conversion software when it made R g .

Note that these commits, on branch recoverer/pr123 , are preceded by many (tens, hundreds, thousands, whatever) of commits that are also on recoverer/pr123 . Git, in its bizarre notion of what it means for commits to be on branches, often has commits on more than one branch. The set of branches that contain a commit change dynamically over time! This is very unlike Mercurial, where commits politely stay in their original branch. So your goal is now to copy the commits that are only on recoverer/pr123 , and not those on that and on, say, recoverer/master , or whatever other branches they appear on.

You can find these commits by their hash IDs, one at a time, or you can figure out which name(s) work for excluding them. For instance, if git log recoverer/master..recoverer/pr123 shows exactly the right set of commits, the name recoverer/master works for excluding the unwanted commits. One way or another you'll run git log recoverer/pr123 , perhaps with some exclusion-name in front like this, to verify that you're getting the right set of commits to copy.


Having found the commits to copy for your PR—whether by individual hash IDs, or by this sort of name, you now just run:

git checkout <branch>

(where branch is the place you want to copy these commits to , which can be a new branch if you like) and then:

git cherry-pick <hashes-or-string>

eg, git cherry-pick recoverer/master..recoverer/pr123 . Git will now attempt to duplicate——or in Mercurial terms, graft —these commits to the current branch. The new commits will make the same changes as the original commits did, but will be a new chunk of history—a new set of commits—growing from the commit you checked out in your git checkout branch step.

Once all has gone well, you can git push origin as usual and make a GitHub pull request as usual. You can also git remote remove recoverer to remove all recoverer/* names from G local and remove the link from G local to R g , and you can remove R g itself. (You can do all of this any time after you're done cherry-picking.) If Git was sharing stuff in G local with R g , the disk space is freed up once you've removed R g . If not, Git will eventually "garbage collect" the extra disk space, now that its link to R g is gone.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM