如何将包含所有分支的 Git 仓库从 Bitbucket 迁移到 GitHub?

365

如何使用最佳方式将包含所有分支和完整历史记录的Git仓库从Bitbucket迁移到GitHub?

是否有脚本或命令列表需要使用?


11
Github 提供工具和文档来实现这个功能: https://help.github.com/articles/importing-from-other-version-control-systems-to-github/ https://help.github.com/articles/importing-a-git-repository-using-the-command-line/ - mems
有点晚了,但这是一个迁移脚本:https://gist.github.com/chinmaya-n/cff02f1277c811deab2e550f2aad9967 - inblueswithu
12个回答

439

非常简单。

  1. 在GitHub中创建一个新的空的代码库(不需要README或许可证,您可以稍后添加),然后会显示以下屏幕。

  2. 导入代码 选项中,粘贴你的Bitbucket代码库的URL,完成了!!

点击 导入代码


14
你在这里真正回答了这个问题,因为被接受的答案只是一个通用的Git相关方法。你的答案简单明了! - Emile Bergeron
6
只要您是创建裸库的人,这个答案就很好。如果其他人为您创建了存储库(例如负责分支创建的管理员),则此方法无效。对于这种情况,接受的答案才是正确的选择。 - Master.Aurora
5
提醒其他使用此方法的人,务必先在使用此服务之前移除您以前存储库的两步验证,否则您将陷入无尽的循环中,试图删除在 GitHub 中新创建的存储库。唯一的解决方法是将网址末尾的“/import”替换为“/settings”,以便进入设置并删除它。 - Dave
1
小提示- 如果您遇到问题,将无法获得详细的错误消息。 - RobertH
新的Bitbucket身份验证已经无法正常工作了。 - Jyaif
显示剩余3条评论

426

你可以参考GitHub页面 "复制仓库"

它使用:

这将得到:

git clone --mirror https://bitbucket.org/exampleuser/repository-to-mirror.git
# Make a bare mirrored clone of the repository

cd repository-to-mirror.git
git remote set-url --push origin https://github.com/exampleuser/mirrored
# Set the push location to your mirror

git push --mirror

评论L S所说:


3
这种方法给我带来了一个问题(不确定是否是问题)。当我将仓库从Bitbucket镜像到GitHub时,有5个分支在GitHub中显示为“比较和拉取请求”,而不是显示为分支。我该怎么办? - Siddharth
2
那么问题和维基怎么办呢? - Vladislav Rastrusny
3
如果你想将一个仓库从Github迁移到Bitbucket,这个方法同样适用。 - simeg
2
我需要使用这个方法来复制一个仓库,因为它包含了一个大文件(>100MB)。GH 不允许在它们的仓库中存储大文件。我在将仓库推送到 GH 之前删除了这个大文件。不过,在其他问题不存在的情况下,使用 GH 的 "导入仓库" 功能**95%** 的情况下是正确的选择,正如 另一个答案 中所描述的那样。然而,如果失败,它不会给出有用的错误信息,你必须联系 GH 的支持部门。正是 GH 的支持部门告诉我有文件大小限制。 - Mr. Lance E Sloan
@LS 谢谢。我已经将您的评论包含在答案中,以增加其可见性。 - VonC
显示剩余6条评论

128

如果您在GitHub上找不到“导入代码”按钮,您可以:

  1. 直接打开 GitHub导入工具 并输入URL。URL看起来像这样:

    GitHub导入工具截图

  2. 给它一个名称(或它会自动导入名称)

  3. 选择 公共私有 仓库

  4. 点击 开始导入

2016年5月,GitHub 宣布支持“导入包含大文件的代码库”。


很遗憾,我的尝试从CodebaseHQ迁移到GitHub没有成功,显示“此URL不受支持”。:( - sjmcdowall
@sjmcdowall 抱歉,它没有起作用,但我相信它应该可以工作,因为codebasehq的URL指向一个git存储库。 - biniam
URL不再存在。 - Coded Container
@CodedContainer,您可以查看https://youtu.be/aDRGgK4wVA8视频,它向您展示了当我单击链接时如何显示页面。也许这可能是您的互联网或DNS或其他问题... - biniam
1
如果您未登录GitHub,则该URL将仅返回404错误。如果您已登录,则导入工具将加载。 - Jason Hurt
显示剩余3条评论

45

请执行以下步骤将代码仓库从Bitbucket迁移到GitHub

这可以帮助我从一个Git提供商迁移到另一个Git提供商。最终,所有提交记录都被移动到了目标Git仓库中。操作简单明了。

git remote rename origin bitbucket
git remote add origin https://github.com/edwardaux/Pipelines.git
git push origin master

Once I was happy that the push had been successful to GitHub, I could delete the old remote by issuing:

git remote rm bitbucket

2
请在您的回答中包含链接的相关部分,以便它能够独立存在。 - k0pernikus

13

我有一个与从GitHub到Bitbucket的现有存储库相反的用例。

Bitbucket也提供了一个导入工具。唯一必要的步骤是添加存储库URL。

它看起来像这样:

Bitbucket导入工具截图


6

这里是使用GitHub导入工具导入仓库

如果您有一个托管在其他版本控制系统(如Mercurial)上的项目,您可以使用GitHub导入工具自动将其导入到GitHub。

  1. 在任何页面的右上角,单击并单击导入存储库。
  2. 在“您的旧存储库的克隆URL”下,输入要导入的项目的URL。
  3. 选择您的用户帐户或组织以拥有该存储库,然后在GitHub上输入存储库的名称。
  4. 指定新存储库是公共的还是私有的。
  • 公共存储库对GitHub上的任何用户都可见,因此您可以从GitHub的协作社区中受益。
  • 公共或私有存储库单选按钮私有存储库仅对存储库所有者以及您选择共享的任何协作者可用。
  1. 查看您输入的信息,然后单击开始导入

当存储库完全导入时,您将收到一封电子邮件。

  1. 将项目导入GitHub
  2. 使用GitHub导入工具导入存储库

5

几个月前,当我试图做同样的事情时,我发现了这个问题,并对给出的答案感到失望。它们似乎都是处理逐个从Bitbucket导入到GitHub一个存储库,要么通过单独的命令下达,要么通过GitHub导入工具。

我从名为gitter的GitHub项目中抓取了代码,并修改了它以适应我的需求。

你可以分叉gist,或者从这里获取代码:

#!/usr/bin/env ruby
require 'fileutils'

# Originally  -- Dave Deriso        -- deriso@gmail.com
# Contributor -- G. Richard Bellamy -- rbellamy@terradatum.com
# If you contribute, put your name here!
# To get your team ID:
# 1. Go to your GitHub profile, select 'Personal Access Tokens', and create an Access token
# 2. curl -H "Authorization: token <very-long-access-token>" https://api.github.com/orgs/<org-name>/teams
# 3. Find the team name, and grabulate the Team ID
# 4. PROFIT!

#----------------------------------------------------------------------
#your particulars
@access_token = ''
@team_id = ''
@org = ''


#----------------------------------------------------------------------
#the version of this app
@version = "0.2"

#----------------------------------------------------------------------
#some global parameters
@create = false
@add = false
@migrate = false
@debug = false
@done = false
@error = false

#----------------------------------------------------------------------
#fancy schmancy color scheme

class String; def c(cc); "\e[#{cc}m#{self}\e[0m" end end
#200.to_i.times{ |i| print i.to_s.c(i) + " " }; puts
@sep = "-".c(90)*95
@sep_pref = ".".c(90)*95
@sep_thick = "+".c(90)*95

#----------------------------------------------------------------------
# greetings

def hello
  puts @sep
  puts "BitBucket to GitHub migrator -- v.#{@version}".c(95)
  #puts @sep_thick
end

def goodbye
  puts @sep
  puts "done!".c(95)
  puts @sep
  exit
end

def puts_title(text)
   puts  @sep, "#{text}".c(36), @sep
end

#----------------------------------------------------------------------
# helper methods

def get_options
  require 'optparse'

  n_options = 0
  show_options = false

  OptionParser.new do |opts|
    opts.banner = @sep +"\nUsage: gitter [options]\n".c(36)
    opts.version = @version
    opts.on('-n', '--name [name]', String, 'Set the name of the new repo') { |value| @repo_name = value; n_options+=1 }
    opts.on('-c', '--create', String, 'Create new repo') { @create = true; n_options+=1 }
    opts.on('-m', '--migrate', String, 'Migrate the repo') { @migrate = true; n_options+=1 }
    opts.on('-a', '--add', String, 'Add repo to team') { @add = true; n_options+=1 }
    opts.on('-l', '--language [language]', String, 'Set language of the new repo') { |value| @language = value.strip.downcase; n_options+=1 }
    opts.on('-d', '--debug', 'Print commands for inspection, doesn\'t actually run them') { @debug = true; n_options+=1 }
    opts.on_tail('-h', '--help', 'Prints this little guide') { show_options = true; n_options+=1 }
    @opts = opts
  end.parse!

  if show_options || n_options == 0
    puts @opts
    puts "\nExamples:".c(36)
    puts 'create new repo: ' + "\t\tgitter -c -l javascript -n node_app".c(93)
    puts 'migrate existing to GitHub: ' + "\tgitter -m -n node_app".c(93)
    puts 'create repo and migrate to it: ' + "\tgitter -c -m -l javascript -n node_app".c(93)
    puts 'create repo, migrate to it, and add it to a team: ' + "\tgitter -c -m -a -l javascript -n node_app".c(93)
    puts "\nNotes:".c(36)
    puts "Access Token for repo is #{@access_token} - change this on line 13"
    puts "Team ID for repo is #{@team_id} - change this on line 14"
    puts "Organization for repo is #{@org} - change this on line 15"
    puts 'The assumption is that the person running the script has SSH access to Bitbucket,'
    puts 'and GitHub, and that if the current directory contains a directory with the same'
    puts 'name as the repo to migrated, it will deleted and recreated, or created if it'
    puts 'doesn\'t exist - the repo to migrate is mirrored locally, and then created on'
    puts 'GitHub and pushed from that local clone.'
    puts 'New repos are private by default'
    puts "Doesn\'t like symbols for language (ex. use \'c\' instead of \'c++\')"
    puts @sep
    exit
  end
end

#----------------------------------------------------------------------
# git helper methods

def gitter_create(repo)
  if @language
    %q[curl https://api.github.com/orgs/] + @org + %q[/repos -H "Authorization: token ] + @access_token + %q[" -d '{"name":"] + repo + %q[","private":true,"language":"] + @language + %q["}']
  else
    %q[curl https://api.github.com/orgs/] + @org + %q[/repos -H "Authorization: token ] + @access_token + %q[" -d '{"name":"] + repo + %q[","private":true}']
  end
end

def gitter_add(repo)
  if @language
    %q[curl https://api.github.com/teams/] + @team_id + %q[/repos/] + @org + %q[/] + repo + %q[ -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ] + @access_token + %q[" -d '{"permission":"pull","language":"] + @language + %q["}']
  else
    %q[curl https://api.github.com/teams/] + @team_id + %q[/repos/] + @org + %q[/] + repo + %q[ -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ] + @access_token + %q[" -d '{"permission":"pull"}']
  end
end

def git_clone_mirror(bitbucket_origin, path)
  "git clone --mirror #{bitbucket_origin}"
end

def git_push_mirror(github_origin, path)
  "(cd './#{path}' && git push --mirror #{github_origin} && cd ..)"
end

def show_pwd
  if @debug
    Dir.getwd()
  end
end

def git_list_origin(path)
  "(cd './#{path}' && git config remote.origin.url && cd ..)"
end

# error checks

def has_repo
  File.exist?('.git')
end

def has_repo_or_error(show_error)
  @repo_exists = has_repo
  if !@repo_exists
    puts 'Error: no .git folder in current directory'.c(91) if show_error
    @error = true
  end
  "has repo: #{@repo_exists}"
end

def has_repo_name_or_error(show_error)
  @repo_name_exists = !(defined?(@repo_name)).nil?
  if !@repo_name_exists
    puts 'Error: repo name missing (-n your_name_here)'.c(91) if show_error
    @error = true
  end
end

#----------------------------------------------------------------------
# main methods
def run(commands)
  if @debug
    commands.each { |x| puts(x) }
  else
    commands.each { |x| system(x) }
  end
end

def set_globals

  puts_title 'Parameters'

  @git_bitbucket_origin =   "git@bitbucket.org:#{@org}/#{@repo_name}.git"
  @git_github_origin = "git@github.com:#{@org}/#{@repo_name}.git"

  puts 'debug: ' + @debug.to_s.c(93)
  puts 'working in: ' + Dir.pwd.c(93)
  puts 'create: ' + @create.to_s.c(93)
  puts 'migrate: ' + @migrate.to_s.c(93)
  puts 'add: ' + @add.to_s.c(93)
  puts 'language: ' + @language.to_s.c(93)
  puts 'repo name: '+ @repo_name.to_s.c(93)
  puts 'bitbucket: ' + @git_bitbucket_origin.to_s.c(93)
  puts 'github: ' + @git_github_origin.to_s.c(93)
  puts 'team_id: ' + @team_id.to_s.c(93)
  puts 'org: ' + @org.to_s.c(93)
end

def create_repo
  puts_title 'Creating'

  #error checks
  has_repo_name_or_error(true)
  goodbye if @error

  puts @sep

  commands = [
      gitter_create(@repo_name)
  ]

  run commands
end


def add_repo
  puts_title 'Adding repo to team'

  #error checks
  has_repo_name_or_error(true)
  goodbye if @error

  puts @sep

  commands = [
      gitter_add(@repo_name)
  ]

  run commands
end

def migrate_repo

  puts_title "Migrating Repo to #{@repo_provider}"

  #error checks
  has_repo_name_or_error(true)
  goodbye if @error

  if Dir.exists?("#{@repo_name}.git")
    puts "#{@repo_name} already exists... recursively deleting."
    FileUtils.rm_r("#{@repo_name}.git")
  end

  path = "#{@repo_name}.git"
  commands = [
    git_clone_mirror(@git_bitbucket_origin, path),
    git_list_origin(path),
    git_push_mirror(@git_github_origin, path)
  ]

  run commands
end

#----------------------------------------------------------------------
#sequence control
hello
get_options

#do stuff
set_globals
create_repo if @create
migrate_repo if @migrate
add_repo if @add

#peace out
goodbye

然后,要使用该脚本:
# create a list of repos
foo
bar
baz

# execute the script, iterating over your list
while read p; do ./bitbucket-to-github.rb -a -n $p; done<repos

# good enough

4
我编写了以下Bash脚本,旨在将我的Bitbucket(用户)存储库全部克隆到GitHub作为私有存储库。

要求:

  • jq(命令行 JSON 处理器)| macOS: brew install jq

步骤:

  1. 前往个人访问令牌并创建一个访问令牌。我们只需要“repo”范围。

  2. move_me.sh脚本保存到工作文件夹中,并根据需要编辑该文件。

  3. 不要忘记chmod 755

  4. 运行!./move_me.sh

  5. 享受你节省的时间。


注意事项:

  • 此脚本会将Bitbucket存储库克隆到其所在的目录中(即您的工作目录)。

  • 此脚本不会删除您的Bitbucket存储库。


需要将项目移动到GitHub的公共存储库吗?

在下方查找并更改"private": true"private": false

移动组织的存储库?

查看开发者指南。只需进行几个编辑即可。


愉快的移动。

#!/bin/bash

BB_USERNAME=your_bitbucket_username
BB_PASSWORD=your_bitbucket_password

GH_USERNAME=your_github_username
GH_ACCESS_TOKEN=your_github_access_token

###########################

pagelen=$(curl -s -u $BB_USERNAME:$BB_PASSWORD https://api.bitbucket.org/2.0/repositories/$BB_USERNAME | jq -r '.pagelen')

echo "Total number of pages: $pagelen"

hr () {
  printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
}

i=1

while [ $i -le $pagelen ]
do
  echo
  echo "* Processing Page: $i..."
  hr
  pageval=$(curl -s -u $BB_USERNAME:$BB_PASSWORD https://api.bitbucket.org/2.0/repositories/$BB_USERNAME?page=$i)

  next=$(echo $pageval | jq -r '.next')
  slugs=($(echo $pageval | jq -r '.values[] | .slug'))
  repos=($(echo $pageval | jq -r '.values[] | .links.clone[1].href'))

  j=0
  for repo in ${repos[@]}
  do
    echo "$(($j + 1)) = ${repos[$j]}"
    slug=${slugs[$j]}
  git clone --bare $repo
  cd "$slug.git"
  echo
  echo "* $repo cloned, now creating $slug on GitHub..."
  echo

  read -r -d '' PAYLOAD <<EOP
  {
    "name": "$slug",
    "description": "$slug - moved from Bitbucket",
    "homepage": "https://github.com/$slug",
    "private": true
  }
  EOP

  curl -H "Authorization: token $GH_ACCESS_TOKEN" --data "$PAYLOAD" \
      https://api.github.com/user/repos
  echo
  echo "* mirroring $repo to GitHub..."
  echo
  git push --mirror "git@github.com:$GH_USERNAME/$slug.git"
  j=$(( $j + 1 ))
  hr
  cd ..
  done
  i=$(( $i + 1 ))
done

3

如果您想将本地的Git仓库移动到另一个上游仓库,您也可以这样做:

获取当前远程仓库URL:

git remote get-url origin

将显示类似于:https://bitbucket.com/git/myrepo

设置新的远程存储库:

git remote set-url origin git@github.com:folder/myrepo.git

现在将当前(develop)分支的内容推送:

git push --set-upstream origin develop

您现在在新的远程仓库中拥有该分支的完整副本。

可选地,返回到此本地文件夹的原始 git-remote:

git remote set-url origin https://bitbucket.com/git/myrepo

现在你可以从GitHub获取你的新Git仓库并将其放置在另一个文件夹中,这样你就有了两个本地文件夹,分别指向不同的远程仓库,旧的(Bitbucket)和新的都可用。


2
以下是私有Git存储库迁移的步骤:
第一步:创建GitHub存储库
首先,在GitHub上创建一个新的私有存储库。重要的是要保持存储库为空,例如,在创建存储库时不勾选“使用README文件初始化此存储库”选项。
第二步:移动现有内容
接下来,我们需要将Bitbucket存储库中的内容填充到GitHub存储库中。
  1. Check out the existing repository from Bitbucket:

    git clone https://USER@bitbucket.org/USER/PROJECT.git
    
  2. Add the new GitHub repository as upstream remote of the repository checked out from Bitbucket:

    cd PROJECT
    git remote add upstream https://github.com:USER/PROJECT.git
    
  3. Push all branches (below: just master) and tags to the GitHub repository:

    git push upstream master
    git push --tags upstream
    

步骤三:清理旧仓库

最后,我们需要确保开发人员不会因为同一个项目有两个仓库而感到困惑。以下是如何删除Bitbucket仓库的方法:

  1. 仔细检查GitHub仓库是否具有所有内容

  2. 进入旧Bitbucket仓库的Web界面

  3. 选择菜单选项设置删除仓库

  4. 将新GitHub仓库的URL添加为重定向URL

这样,仓库就彻底地迁移到了GitHub的新家。让所有开发人员都知道!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接