将Git存储库及其所有历史导入现有Git存储库


Import a git repository with all its history into an existing git repository

我有两个Git存储库,我希望将它们合并在一起,而不会丢失它们的提交历史记录。我试过了:

1
2
3
4
5
6
cd firstRepo
git remote add other path/to/otherRepo
git fetch other
git checkout -b otherRepoBranch other/master
echo"`git rev-list otherRepoBranch | tail -n 1` `git rev-list master | head -n 1`">> .git/info/grafts
git rebase otherRepoBranch master

现在,当我查看提交历史记录时,一切看起来都很好,但是我的存储库中只有来自Otherrepo的文件。

有什么想法吗?

干杯,默林


我认为Git存储库是无关的?就像它们是单独项目的不同存储库一样,您希望将它们合并在一起形成一个组合的存储库?如果是这样,那么以下参考可能会有所帮助:

组合多个Git存储库。

合并两个不相关的存储库。


这篇决定性的文章讨论了开头一段中的简单案例——并将更可能的案例作为其主要主题:如何使用子树合并策略

两个存储库的提交历史记录都会保留。

这是我的版本——基于上面提到的文章……

1
2
3
4
5
6
7
8
9
git remote add temp staging_path/(reponame)
git fetch temp
git fetch --tags temp ## optional -- may pull in additional history
for remote in $(git branch -r | grep temp/ ) ; do git branch --no-track imported_$(basename $remote) $remote ; done ## create local branches prefixed with 'imported_'
git remote rm temp ## optional -- assumes you no longer plan to use the source repo

git merge -s ours --no-commit imported_master ## mysterious"merge strategy"
git read-tree -u --prefix=(reponame)/ imported_master ## move to sub-folder
git commit


我知道现在已经很晚了,但是对于仍在寻找方法的人来说,我已经收集了很多关于stackoverflow等的信息,并设法将一个脚本放在一起,从而为我解决了这个问题。

这个脚本甚至可以处理特性分支和标记——在新项目中对它们进行重命名,以便您知道它们来自哪里。

我知道现在已经很晚了,但是对于仍在寻找方法的人来说,我已经收集了很多关于stackoverflow等的信息,并设法将一个脚本放在一起,从而为我解决了这个问题。

这个脚本甚至可以处理特性分支和标记——在新项目中对它们进行重命名,以便您知道它们来自哪里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#!/bin/bash
#
################################################################################
## Script to merge multiple git repositories into a new repository
## - The new repository will contain a folder for every merged repository
## - The script adds remotes for every project and then merges in every branch
##   and tag. These are renamed to have the origin project name as a prefix
##
## Usage: mergeGitRepositories.sh <new_project> <my_repo_urls.lst>
## - where <new_project> is the name of the new project to create
## - and <my_repo_urls.lst> is a file contaning the URLs to the respositories
##   which are to be merged on separate lines.
##
## Author: Robert von Burg
##            [email protected]
##
## Version: 0.2.0
## Created: 2015-06-17
##
################################################################################
#

# disallow using undefined variables
shopt -s -o nounset

# Script variables
declare SCRIPT_NAME="${0##*/}"
declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)"
declare ROOT_DIR="$PWD"


# Detect proper usage
if ["$#" -ne"2" ] ; then
  echo -e"ERROR: Usage: $0 <new_project> <my_repo_urls.lst>"
  exit 1
fi


# Script functions
function failed() {
  echo -e"ERROR: Merging of projects failed:"
  echo -e"$1"
  exit 1
}

function commit_merge() {
  current_branch="$(git symbolic-ref HEAD 2>/dev/null)"
  CHANGES=$(git status | grep"working directory clean")
  MERGING=$(git status | grep"merging")
  if [["$CHANGES" !="" ]] && [["$MERGING" =="" ]] ; then
    echo -e"INFO:   No commit required."
  else
    echo -e"INFO:   Committing ${sub_project}..."
    if ! git commit --quiet -m"[Project] Merged branch '$1' of ${sub_project}" ; then
      failed"Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}"
    fi
  fi
}


## Script variables
PROJECT_NAME="${1}"
PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}"
REPO_FILE="${2}"
REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}"


# Make sure the REPO_URL_FILE exists
if [ ! -e"${REPO_URL_FILE}" ] ; then
  echo -e"ERROR: Repo file ${REPO_URL_FILE} does not exist!"
  exit 1
fi


# Make sure the required directories don't exist
if [ -e"${PROJECT_PATH}" ] ; then
  echo -e"ERROR: Project ${PROJECT_NAME} already exists!"
  exit 1
fi


# create the new project
echo -e"INFO: Creating new git repository ${PROJECT_NAME}..."
echo -e"===================================================="
cd ${ROOT_DIR}
mkdir ${PROJECT_NAME}
cd ${PROJECT_NAME}
git init
echo"Initial Commit"> initial_commit
# Since this is a new repository we need to have at least one commit
# thus were we create temporary file, but we delete it again.
# Deleting it guarantees we don't have conflicts later when merging
git add initial_commit
git commit --quiet -m"[Project] Initial Master Repo Commit"
git rm --quiet initial_commit
git commit --quiet -m"[Project] Initial Master Repo Commit"
echo


# Merge all projects into th branches of this project
echo -e"INFO: Merging projects into new repository..."
echo -e"===================================================="
for url in $(cat ${REPO_URL_FILE}) ; do

  # extract the name of this project
  export sub_project=${url##*/}
  sub_project=${sub_project%*.git}

  echo -e"INFO: Project ${sub_project}"
  echo -e"----------------------------------------------------"

  # Fetch the project
  echo -e"INFO:   Fetching ${sub_project}..."
  git remote add"${sub_project}""${url}"
  if ! git fetch --no-tags --quiet ${sub_project} 2>/dev/null ; then
    failed"Failed to fetch project ${sub_project}"
  fi

  # add remote branches
  echo -e"INFO:   Creating local branches for ${sub_project}..."
  while read branch ; do
    branch_ref=$(echo $branch | tr"""\t" | cut -f 1)
    branch_name=$(echo $branch | tr"""\t" | cut -f 2 | cut -d / -f 3-)

    echo -e"INFO:   Creating branch ${branch_name}..."

    # create and checkout new merge branch off of master
    git checkout --quiet -b"${sub_project}/${branch_name}" master
    git reset --hard --quiet
    git clean -d --force --quiet

    # Merge the project
    echo -e"INFO:   Merging ${sub_project}..."
    if ! git merge --quiet --no-commit"remotes/${sub_project}/${branch_name}" 2>/dev/null ; then
      failed"Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}"
    fi

    # And now see if we need to commit (maybe there was a merge)
    commit_merge"${sub_project}/${branch_name}"

    # relocate projects files into own directory
    if ["$(ls)" =="${sub_project}" ] ; then
      echo -e"WARN:   Not moving files in branch ${branch_name} of ${sub_project} as already only one root level."
    else
      echo -e"INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..."
      mkdir ${sub_project}
      for f in $(ls -a) ; do
        if  [["$f" =="${sub_project}" ]] ||
            [["$f" =="." ]] ||
            [["$f" ==".." ]] ; then
          continue
        fi
        git mv -k"$f""${sub_project}/"
      done

      # commit the moving
      if ! git commit --quiet -m "[Project] Move ${sub_project} files into sub directory" ; then
        failed"Failed to commit moving of ${sub_project} files into sub directory"
      fi
    fi
    echo
  done < <(git ls-remote --heads ${sub_project})


  #&nbsp;checkout master of sub probject
  if ! git checkout"${sub_project}/master" 2>/dev/null ; then
    failed"sub_project ${sub_project} is missing master branch!"
  fi

  # copy remote tags
  echo -e"INFO:   Copying tags for ${sub_project}..."
  while read tag ; do
    tag_ref=$(echo $tag | tr"""\t" | cut -f 1)
    tag_name=$(echo $tag | tr"""\t" | cut -f 2 | cut -d / -f 3)

    # hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0
    tag_name="${tag_name%%^*}"

    tag_new_name="${sub_project}/${tag_name}"
    echo -e"INFO:     Copying tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}..."
    if ! git tag"${tag_new_name}""${tag_ref}" 2>/dev/null ; then
      echo -e"WARN:     Could not copy tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}"
    fi
  done < <(git ls-remote --tags ${sub_project})

  # Remove the remote to the old project
  echo -e"INFO:   Removing remote ${sub_project}..."
  git remote rm ${sub_project}

  echo
done


# Now merge all project master branches into new master
git checkout --quiet master
echo -e"INFO: Merging projects master branches into new repository..."
echo -e"===================================================="
for url in $(cat ${REPO_URL_FILE}) ; do

  # extract the name of this project
  export sub_project=${url##*/}
  sub_project=${sub_project%*.git}

  echo -e"INFO:   Merging ${sub_project}..."
  if ! git merge --quiet --no-commit"${sub_project}/master" 2>/dev/null ; then
    failed"Failed to merge branch ${sub_project}/master into master"
  fi

  # And now see if we need to commit (maybe there was a merge)
  commit_merge"${sub_project}/master"

  echo
done


# Done
cd ${ROOT_DIR}
echo -e"INFO: Done."
echo

exit 0

你也可以从http://paste.ubuntu.com/11732805获得它。/

首先使用每个存储库的URL创建一个文件,例如:

1
2
3
  [email protected]:eitchnet/ch.eitchnet.parent.git
  [email protected]:eitchnet/ch.eitchnet.utils.git
  [email protected]:eitchnet/ch.eitchnet.privilege.git

然后调用脚本,给出项目名称和脚本路径:

1
  ./mergeGitRepositories.sh eitchnet_test eitchnet.lst

脚本本身有很多注释,应该解释它的作用。

编辑后用更高级的脚本替换脚本。


在最简单的情况下,如果您希望合并后两个存储库中的所有文件都能使用git merge:

1
2
3
4
5
cd firstRepo
git remote add other path/to/otherRepo
git fetch other
git checkout -b merged
git merge --allow-unrelated-histories other/master


1
2
git checkout master
git merge otherRepoBranch

最后被遗忘了。

这对于在主分支上应用钢筋是必要的。


这些看起来都很难理解…为什么不只是:

1
2
3
4
5
6
7
8
9
# Make a bare clone of the external repo to a local directory without a working directory
git clone --bare https://githost.org/extuser/repo.git

# now push all history and repo to new repo
cd repo.git
git push --mirror https://github.com/ghuser/repo.git # Push mirror to new GitHub repo

cd ..
rm -rf repo.git # Remove temporary local repo