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:

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

Popular posts from this blog

php - render data via PDO::FETCH_FUNC vs loop -

c++ - OpenCV Error: Assertion failed <scn == 3 ::scn == 4> in unknown function, -

The canvas has been tainted by cross-origin data in chrome only -