Add subdirectory of remote repo with git-subtree -
is there way add subdirectory of remote repository subdirectory of repository git-subtree?
suppose have main repository:
/ dir1 dir2
and library repository:
/ libdir some-file some-file-to-be-ignored
i want import library/libdir main/dir1 looks this:
/ dir1 some-file dir2
using git-subtree, can specify import dir1 --prefix
argument, can specify take contents of specific directory in subtree?
the reason using git-subtree can later synchronize 2 repositories.
i've been experimenting this, , found partial solutions, though none quite perfect.
for these examples, i'll consider merging 4 files contrib/completion/
of https://github.com/git/git.git third_party/git_completion/
of local repository.
1. git diff | git apply
this best way i've found. tested one-way merging; haven't tried sending changes upstream repository.
# first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git # next line optional. without it, upstream commits # squashed; included in local history. $ git merge -s ours --no-commit gitgit/master # trailing slash important here! $ git read-tree --prefix=third_party/git-completion/ -u gitgit/master:contrib/completion $ git commit # in future, can merge in additional changes follows: # next line optional. without it, upstream commits # squashed; included in local history. $ git merge -s ours --no-commit gitgit/master # replace sha1 below commit hash # merged in using technique (i.e. recent commit on # gitgit/master @ time). $ git diff --color=never 53e53c7c81ce2c7c4cd45f95bc095b274cb28b76:contrib/completion gitgit/master:contrib/completion | git apply -3 --directory=third_party/git-completion # fix conflicts if you'd modified third_party/git-completion. $ git commit
since it's awkward having remember recent commit sha1 merged upstream repository, i've written bash function hard work (grabbing git log):
git-merge-subpath() { local squash if [[ $1 == "--squash" ]]; squash=1 shift fi if (( $# != 3 )); local params="[--squash] source_commit source_prefix dest_prefix" echo "usage: ${funcname[0]} $params" return 1 fi # friendly parameter names; strip trailing slashes prefixes. local source_commit="$1" source_prefix="${2%/}" dest_prefix="${3%/}" local source_sha1 source_sha1=$(git rev-parse --verify "$source_commit^{commit}") || return 1 local old_sha1 local git_root=$(git rev-parse --show-toplevel) if [[ -n "$(ls -a "$git_root/$dest_prefix" 2> /dev/null)" ]]; # old_sha1 remain empty if there no match. local re="^${funcname[0]}: [0-9a-f]{40} $source_prefix $dest_prefix\$" old_sha1=$(git log -1 --format=%b -e --grep="$re" \ | grep --color=never -e "$re" | tail -1 | awk '{print $2}') fi local old_treeish if [[ -n $old_sha1 ]]; old_treeish="$old_sha1:$source_prefix" else # first time git-merge-subpath run, diff against # empty commit instead of last commit created git-merge-subpath. old_treeish=$(git hash-object -t tree /dev/null) fi && if [[ -z $squash ]]; git merge -s ours --no-commit "$source_commit" fi && git diff --color=never "$old_treeish" "$source_commit:$source_prefix" \ | git apply -3 --directory="$dest_prefix" || git mergetool if (( $? == 1 )); echo "uh-oh! try cleaning |git reset --merge|." else git commit -em "merge $source_commit:$source_prefix/ $dest_prefix/ # feel free edit title , body above, make sure keep # ${funcname[0]}: line below intact, ${funcname[0]} can find # again when grepping git log. ${funcname[0]}: $source_sha1 $source_prefix $dest_prefix" fi }
use this:
# first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git $ git-merge-subpath gitgit/master contrib/completion third_party/git-completion # in future, can merge in additional changes follows: $ git fetch gitgit $ git-merge-subpath gitgit/master contrib/completion third_party/git-completion # fix conflicts if you'd modified third_party/git-completion.
2. git read-tree
if you're never going make local changes merged in files, i.e. you're happy overwrite local subdirectory latest version upstream, similar simpler approach use git read-tree
:
# first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git # next line optional. without it, upstream commits # squashed; included in local history. $ git merge -s ours --no-commit gitgit/master $ git read-tree --prefix=third_party/git-completion/ -u gitgit/master:contrib/completion $ git commit # in future, can *overwrite* latest changes follows: # above, next line optional (affects squashing). $ git merge -s ours --no-commit gitgit/master $ git rm -rf third_party/git-completion $ git read-tree --prefix=third_party/git-completion/ -u gitgit/master:contrib/completion $ git commit
i found blog post claimed able merge (without overwriting) using similar technique, didn't work when tried it.
3. git subtree
i did find solution uses git subtree
, http://jrsmith3.github.io/merging-a-subdirectory-from-another-repo-via-git-subtree.html, it's incredibly slow (each git subtree split
command below takes me 9 minutes 28 mb repo 39000 commits on dual xeon x5675, whereas other solutions found take less second).
if can live slowness, should workable:
# first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git $ git checkout gitgit/master $ git subtree split -p contrib/completion -b temporary-split-branch $ git checkout master $ git subtree add --squash -p third_party/git-completion temporary-split-branch $ git branch -d temporary-split-branch # in future, can merge in additional changes follows: $ git checkout gitgit/master $ git subtree split -p contrib/completion -b temporary-split-branch $ git checkout master $ git subtree merge --squash -p third_party/git-completion temporary-split-branch # fix conflicts if you'd modified third_party/git-completion. $ git branch -d temporary-split-branch
note pass in --squash
avoid polluting local repository lots of commits, can remove --squash
if you'd prefer preserve commit history.
it's possible subsequent splits can made faster using --rejoin
(see https://stackoverflow.com/a/16139361/691281) - didn't test that.
4. whole repo git subtree
the op stated want merge subdirectory of upstream repository subdirectory of local repository. if instead want merge entire upstream repository subdirectory of local repository, there's simpler, cleaner, , better supported alternative:
# first time: $ git subtree add --squash --prefix=third_party/git https://github.com/git/git.git master # in future, can merge in additional changes follows: $ git subtree pull --squash --prefix=third_party/git https://github.com/git/git.git master
or if prefer avoid repeating repository url, can add remote:
# first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git $ git subtree add --squash --prefix=third_party/git gitgit/master # in future, can merge in additional changes follows: $ git subtree pull --squash --prefix=third_party/git gitgit/master # , can push changes upstream follows: $ git subtree push --prefix=third_party/git gitgit/master # or possibly (not sure difference is): $ git subtree push --squash --prefix=third_party/git gitgit/master
see also:
- http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/
- https://hpc.uni.lu/blog/2014/understanding-git-subtree/
- http://getlevelten.com/blog/tom-mccracken/smarter-drupal-projects-projects-management-git-subtree
5. whole repo git submodule
a related technique git submodules, come annoying caveats (for example people clone repository won't clone submodules unless call git clone --recursive
), didn't investigate whether can support subpaths.
Comments
Post a Comment