如何将包含所有分支的 Git 存储库从 Bitbucket 移动到 GitHub?

Posted

技术标签:

【中文标题】如何将包含所有分支的 Git 存储库从 Bitbucket 移动到 GitHub?【英文标题】:How can I move a Git repository with all branches from Bitbucket to GitHub? 【发布时间】:2014-05-19 08:54:45 【问题描述】:

将包含所有分支和完整历史记录的 Git 存储库从 Bitbucket 移动到 GitHub 的最佳方式是什么?

是否有我必须使用的脚本或命令列表?

【问题讨论】:

Github 为此提供了工具和文档:help.github.com/articles/…help.github.com/articles/… 晚会有点晚,但这里有一个迁移脚本:gist.github.com/chinmaya-n/cff02f1277c811deab2e550f2aad9967 【参考方案1】:

这很简单。

    在 GitHub 中创建一个新的空存储库(没有README 或许可证,您可以稍后添加它们),然后将显示以下屏幕。

    导入代码选项中,粘贴您的 Bitbucket 存储库的 URL,然后瞧!!

【讨论】:

你真的在这里回答了这个问题,因为接受的答案只显示了一个通用的 Git 相关方法。你的答案更简单! 这个答案很好,只要您是创建存储库的人并且它是裸露的。如果其他人为您创建了存储库(例如负责创建分支的管理员),那么这不起作用。接受的答案是在这种情况下要走的路。 提醒其他考虑使用此方法的人,在使用此服务之前,请在之前的存储库主机上删除您的双因素身份验证,否则您将陷入无限循环尝试删除GitHub 中新创建的存储库,摆脱这种情况的唯一方法是将 URL 末尾的 /import 替换为 /settings 以访问设置并将其删除。 小警告 - 如果您遇到问题,您将不会收到描述性错误消息。 95%的情况下,使用 GH 的“Import repository”功能是正确的选择。但是,如果失败,它不会提供有用的错误消息,您必须联系 GH 支持。我不能将它用于一个 repo,因为它包含一个大文件 (>100MB)。我需要使用 accepted CLI method,在将 repo 推送到 GH 之前删除大文件。【参考方案2】:

可以参考GitHub页面“Duplicating a repository”

它使用:

git clone --mirror: 克隆每个引用(提交、标签、分支) git push --mirror: 推动一切

这会给出:

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

如in the comments L S 所述:

使用MarMass描述的Import Code feature from GitHub更方便。 见https://github.com/new/import 除非...您的存储库包含一个大文件:问题是,导入工具将失败而没有明确的错误消息。只有GitHub Support 能够诊断出发生了什么。

【讨论】:

这个方法给我带来了问题(不确定是不是问题)。当我将 repo 从 bitbucket 镜像到 github 时,对于 5 个分支,它在 github 中显示为“Compare and Pull Request”。它在 github 中没有显示为分支。我该怎么办? 那么问题和维基呢? @FractalizeR wiki 只是另一个存储库,您也可以复制它 (github.com/blog/699-making-github-more-open-git-backed-wikis)。但是,没有简单的方法可以复制问题。您需要使用 Api(各种 GitHub repo 备份程序就是这样做的:addyosmani.com/blog/backing-up-a-github-account) 如果您想将存储库从 Github 移动到 Bitbucket 也可以使用。 我需要使用这种方法来复制一个 repo,因为它包含一个大文件 (>100MB)。 GH 不允许在其存储库中使用大文件。在将 repo 推送到 GH 之前,我删除了大文件。但是,除非出现其他问题,否则使用 GH 的“Import repository”功能在 95% 的情况下是正确的选择,如another answer 中所述。但是,如果失败,它不会提供有用的错误消息,您必须联系 GH 支持。是 GH 支持通知我文件大小限制。【参考方案3】:

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

    直接打开GitHub Importer,输入网址。它看起来像:

    给它一个名字(或者它会自动导入这个名字)

    选择 PublicPrivate 存储库

    点击开始导入

2016 年 5 月,GitHub 宣布支持“Import repositories with large files”。

【讨论】:

不幸的是,这对我从 codebasehq 转移到 git-hub 的尝试不起作用,给出了“不支持此 URL”。 :( @sjmcdowall 对不起,它没有,但我相信它应该可以工作,因为 codebasehq URL 指向一个 git 存储库。 网址不再存在。 @CodedContainer 我刚刚检查过,它可以工作。我什至更新了刚才的屏幕截图。 如果您没有登录 GitHub,该 url 只会返回 404。如果您已登录,将加载导入工具。【参考方案4】:

Moving Repository from Bitbucket to GitHub

这帮助我从一个 Git 提供者转移到另一个。最后,所有提交都在目标 Git 存储库中。简单明了。

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

一旦我对成功推送到 GitHub 感到高兴,我就可以 通过发出删除旧遥控器:

git remote rm bitbucket

【讨论】:

请在您的答案中包含链接的相关部分,因为它应该能够独立存在。 我收到一条错误消息,提示“错误:未能将 som refs 推送到 'url.gi' 提示:更新被拒绝,因为远程包含您在本地没有的工作。这通常是由另一个存储库推送到相同的参考。您可能希望在再次推送之前先集成远程更改(例如拉...)。有关详细信息,请参阅 git push --help 中有关快进的说明。 这是在 2019 年 3 月 3 日仍然有效的唯一答案 :)【参考方案5】:

我有将现有存储库从 GitHub 导入 Bitbucket 的反向用例。

Bitbucket 也提供Import tool。唯一必要的步骤是将 URL 添加到存储库。

看起来像:

【讨论】:

【参考方案6】:

几个月前,当我尝试做同样的事情时,我发现了这个问题,并且对给出的答案感到不知所措。他们似乎都处理从 Bitbucket 一次导入一个存储库到 GitHub,或者通过点菜发布的命令,或者通过 GitHub 导入器。

我从一个名为 gitter 的 GitHub 项目中获取了代码,并对其进行了修改以满足我的需要。

你可以 fork 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[#ccm#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

【讨论】:

【参考方案7】:

使用 GitHub Importer 导入存储库

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

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

完全导入存储库后,您会收到一封电子邮件。

    Importing your projects to GitHub Importing a repository with GitHub Importer

【讨论】:

【参考方案8】:

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

获取当前远程 URL:

git remote get-url origin

将显示如下内容: https://bitbucket.com/git/myrepo

设置新的远程仓库:

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

现在推送当前(开发)分支的内容:

git push --set-upstream origin develop

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

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

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

它带来的好处是,您现在可以从 GitHub 中的另一个文件夹中获取新的 Git 存储库,这样您就有两个本地文件夹都指向不同的远程,前一个 (Bitbucket) 和新的都可用。

【讨论】:

【参考方案9】:

以下是移动私有 Git 存储库的步骤:

第 1 步:创建 GitHub 存储库

首先,在 GitHub 上创建一个新的私有存储库。将存储库保持为空很重要,例如,在创建存储库时不要使用自述文件检查选项初始化此存储库

第 2 步:移动现有内容

接下来,我们需要用我们的 Bitbucket 存储库中的内容填充 GitHub 存储库:

    从 Bitbucket 查看现有存储库

    git clone https://USER@bitbucket.org/USER/PROJECT.git
    

    将新的 GitHub 存储库添加为从 Bitbucket 签出的存储库的上游远程

    cd PROJECT
    git remote add upstream https://github.com:USER/PROJECT.git
    

    推送所有分支(下图:仅master)和标签到GitHub 仓库:

    git push upstream master
    git push --tags upstream
    

第 3 步:清理旧存储库

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

    仔细检查 GitHub 存储库是否包含所有内容

    转到旧 Bitbucket 存储库的 Web 界面

    选择菜单选项设置删除存储库

    将新 GitHub 存储库的 URL 添加为重定向 URL

这样,存储库就完全融入了它在 GitHub 的新家。让所有开发者知道!

【讨论】:

【参考方案10】:

为了将我的Bitbucket(用户)存储库的所有作为私有存储库克隆到 GitHub,我制作了以下 Bash 脚本。


要求:

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

步骤:

    转到 Personal access tokens 并创建访问令牌。我们只需要“repo”范围。

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

    别忘了chmod 755

    快跑! ./move_me.sh

    享受您节省的时间。


注意事项:

它将克隆脚本所在目录(您的工作目录)内的 Bitbucket 存储库。

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


需要迁移到 GitHub 上的公共存储库?

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

移动组织的存储库?

Check out the developer guide。需要进行几次修改。


移动愉快。

#!/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

【讨论】:

有趣的脚本,结合了一个令牌。整洁的。赞成。 @VonC 谢谢! 非常感谢【参考方案11】:

最简单的方法:

git remote rename origin repo_bitbucket

git remote add origin https://github.com/abc/repo.git

git push origin master

推送到 GitHub 成功后,运行以下命令删除旧远程:

git remote rm repo_bitbucket

【讨论】:

以上是关于如何将包含所有分支的 Git 存储库从 Bitbucket 移动到 GitHub?的主要内容,如果未能解决你的问题,请参考以下文章

将 Git 存储库从 GitHub 分叉到 GitLab

将 git 存储库从 GitLab 转移到 GitHub - 我们可以,如何转移和陷阱(如果有的话)?

Maven 发布分支失败,错误=7,参数列表太长

Eclipse:将 git 存储库从项目重定位到工作区

Git,将开发从一个分支转移到另一个分支

如何设计一个git存储库架构,以便在所有分支上进行统一测试? [关闭]