একটি গিট ফোল্ডারটি একটি উপ-মডেলটিতে পূর্ববর্তী স্থানে রূপান্তর করবেন?


115

বেশিরভাগ ক্ষেত্রেই এমন হয় যে আপনি কোনও প্রকারের একটি প্রকল্প লিখে চলেছেন এবং কিছুক্ষণ পরে এটি স্পষ্ট হয়ে যায় যে প্রকল্পের কিছু উপাদান প্রকৃতপক্ষে একটি পৃথক উপাদান (সম্ভবত একটি গ্রন্থাগার) হিসাবে কার্যকর। যদি আপনি প্রথম থেকেই এই ধারণাটি পেয়ে থাকেন, তবে সেই কোডটির বেশিরভাগ অংশই তার নিজস্ব ফোল্ডারে রয়েছে fair

গিট প্রকল্পের উপ ডিরেক্টরিগুলির মধ্যে একটিকে একটি উপমোডিয়ায় রূপান্তর করার কোনও উপায় আছে কি?

আদর্শভাবে এটি এমন ঘটবে যে ডিরেক্টরিটিতে থাকা সমস্ত কোড প্যারেন্ট প্রোজেক্ট থেকে সরিয়ে দেওয়া হয় এবং সাবমোডিয়াল প্রকল্পটি তার যথাযথ ইতিহাস সহ তার জায়গায় যুক্ত করা হয় এবং যেমন সমস্ত প্যারেন্ট প্রকল্প সঠিক সাবমোডিয়াল অঙ্গীকারকে নির্দেশ করে ।


stackoverflow.com/questions/1365541/… কিছু সাহায্য করতে পারে :)
রব পার্কার

এটি মূল প্রশ্নের অংশ নয়, তবে ফোল্ডারের বাইরে যে ফাইলগুলি শুরু হয়েছিল এবং সেগুলিতে স্থানান্তরিত হয়েছিল তার ইতিহাসের ইতিহাস রাখার উপায়টি কী শীতলতর হবে। এই মুহুর্তে, উত্তরগুলির সমস্ত পদক্ষেপের আগে সমস্ত ইতিহাস হারিয়ে যায়।
nnot101

উত্তর:


84

উপ-ডিরেক্টরিকে তার নিজস্ব ভান্ডারে আলাদা করতে, filter-branchমূল সংগ্রহস্থলের ক্লোন ব্যবহার করুন:

git clone <your_project> <your_submodule>
cd <your_submodule>
git filter-branch --subdirectory-filter 'path/to/your/submodule' --prune-empty -- --all

এটি তখন আপনার মূল ডিরেক্টরি মুছতে এবং আপনার প্যারেন্ট প্রোজেক্টে সাবমডিউল যুক্ত করা ছাড়া আর কিছুই নয়।


18
আপনি সম্ভবত git remote rm <name>ফিল্টার শাখা পরে করতে চান , এবং তারপরে সম্ভবত একটি নতুন রিমোট যুক্ত করুন। এছাড়াও, যদি উপেক্ষা করা ফাইল থাকে তবে এগুলি কার্যকর git clean -xd -fহতে পারে
নট 101

-- --allএই শাখা থেকে যদি সাবমডিউলটি কেবল উত্তোলন করা উচিত তবে কোনও শাখার নাম দিয়ে প্রতিস্থাপন করা যেতে পারে।
অ্যাডিয়াস

git clone <your_project> <your_submodule>কেবলমাত্র আপনার_সুবমডুলের জন্য ফাইলগুলি ডাউনলোড করবেন ?
ডোমিনিক

@ ডোমিনিকটোবিয়াস: git clone source destinationআপনার ক্লোন করা ফাইলগুলি কোথায় রাখবেন তার অবস্থানটি কেবল গিটকে বলে। আপনার সাবমডিউলের ফাইলগুলিকে ফিল্টার করার আসল যাদু তখন filter-branchপদক্ষেপে ঘটে ।
নিটল

filter-branchহয় অবচিত আজকাল। আপনি ব্যবহার করতে পারেন git clone --filterতবে ফিল্টারিংয়ের অনুমতি দেওয়ার জন্য আপনার গিট সার্ভারটি অবশ্যই কনফিগার করা উচিত, অন্যথায় আপনি পাবেন warning: filtering not recognized by server, ignoring
ম্যাথিয়াস ব্রাউন

24

প্রথমে ডিয়ার ফোল্ডারে পরিবর্তন করুন যা একটি সাবমডিউল হবে। তারপর:

git init
git remote add origin repourl
git add .
git commit -am'first commit in submodule'
git push -u origin master
cd ..
rm -rf folder wich will be a submodule
git commit -am'deleting folder'
git submodule add repourl folder wich will be a submodule
git commit -am'adding submodule'

9
এটি সেই ফোল্ডারের সমস্ত ইতিহাস হারাবে।
naught101

6
ফোল্ডারের ইতিহাস মূল সংগ্রহস্থলে সংরক্ষণ করা হবে এবং নতুন
কমিটগুলি উপকমে

11

আমি জানি এটি একটি পুরানো থ্রেড, তবে উত্তরগুলি এখানে অন্যান্য শাখায় সম্পর্কিত কোনও কমিটসকে স্কোয়াশ করে।

এই সমস্ত অতিরিক্ত শাখা এবং প্রতিশ্রুতিবদ্ধ করতে এবং ক্লোন করার সহজ উপায়:

1 - নিশ্চিত করুন যে আপনার এই গিটের নাম আছে

git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t'

2 - রিমোটটি ক্লোন করুন, সমস্ত শাখা টানুন, রিমোট পরিবর্তন করুন, আপনার ডিরেক্টরি ফিল্টার করুন, ধাক্কা দিন

git clone git@github.com:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin git@github.com:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags

1
আমার
আসলটির

1

এটি করা যেতে পারে, তবে এটি সহজ নয়। আপনি যদি অনুসন্ধান করেন git filter-branch, subdirectoryএবং submodule, প্রক্রিয়াটিতে কিছু শালীন লেখার ব্যবস্থা রয়েছে। এটির মূলত আপনার প্রকল্পের দুটি ক্লোন তৈরি করা, git filter-branchএকের মধ্যে একটির উপ-ডিরেক্টরি বাদে সমস্ত কিছু সরিয়ে ফেলতে এবং অন্যটিতে কেবল সেই সাব-ডিরেক্টরিকে অপসারণ করা আবশ্যক। তারপরে আপনি প্রথমটির সাবমডিউল হিসাবে দ্বিতীয় সংগ্রহস্থল স্থাপন করতে পারেন।


0

স্থিতিশীল

ধরে নেওয়া যাক আমাদের নামে একটি সংগ্রহস্থল আছে repo-oldযার একটি সাব ডিরেক্টরি রয়েছে যা subআমরা তার নিজস্ব রেপো সহ একটি উপ মডিউল রূপান্তর করতে চাইrepo-sub

এটি আরও লক্ষ্যযুক্ত যে আসল রেপো repo-oldএকটি পরিবর্তিত রেপোতে রূপান্তরিত করা উচিত repo-newযেখানে পূর্ববর্তী বিদ্যমান সাব-ডিরেক্টরিকে স্পর্শ করে সমস্ত কমিট subএখন আমাদের উত্তোলিত সাবমডিউল রেপো সম্পর্কিত সংশ্লিষ্ট কমিটগুলিকে নির্দেশ করবেrepo-sub

আসুন পরিবর্তন করা যাক

git filter-branchএকটি দুটি পদক্ষেপ প্রক্রিয়া সাহায্যে এটি অর্জন করা সম্ভব :

  1. থেকে সাব নিষ্কাশন repo-oldকরা repo-sub(ইতিমধ্যে গৃহীত উল্লেখ উত্তর )
  2. সাব প্রতিস্থাপন থেকে repo-oldথেকে repo-new(সঠিক কমিট ম্যাপিং সহ)

মন্তব্য : আমি জানি যে এই প্রশ্নটি পুরানো এবং এটি ইতিমধ্যে উল্লেখ করা হয়েছে যে git filter-branchএক প্রকার অবচিত এবং বিপজ্জনক হতে পারে। তবে অন্যদিকে এটি অন্যের ব্যক্তিগত সংগ্রহস্থলগুলির সাথে সহায়তা করতে পারে যা রূপান্তরের পরে বৈধ হওয়া সহজ। সুতরাং সতর্ক করা উচিত ! এবং দয়া করে আমাকে জানান যে অন্য কোনও সরঞ্জাম রয়েছে যা অবহেলিত না হয়ে একই কাজ করে এবং ব্যবহারে নিরাপদ কিনা!

আমি নীচের গিট সংস্করণ সহ লিনাক্সের উভয় পদক্ষেপ কীভাবে উপলব্ধি করব তা ব্যাখ্যা করব। পুরানো সংস্করণগুলি কিছুটা প্রসারিত করতে পারে তবে এটি পরীক্ষা করা দরকার।

সরলতার জন্য আমি নিজেকে সেই ক্ষেত্রে সীমাবদ্ধ করব যেখানে মূল রেপোতে কেবল একটি masterশাখা এবং একটি originরিমোট রয়েছে repo-old। এছাড়াও সতর্ক করে দেওয়া হবে যে আমি উপসর্গের সাথে অস্থায়ী গিট ট্যাগগুলির উপর নির্ভর করি temp_যা প্রক্রিয়াটিতে মুছে ফেলা হচ্ছে। সুতরাং ইতিমধ্যে একইভাবে নামযুক্ত ট্যাগগুলি থাকলে আপনি নীচের উপসর্গটি সামঞ্জস্য করতে পারেন। এবং অবশেষে দয়া করে সচেতন হন যে আমি এটির ব্যাপকভাবে পরীক্ষা করে দেখিনি এবং এমন কোনও কোণে থাকতে পারে যেখানে রেসিপি ব্যর্থ হয়। সুতরাং এগিয়ে যাওয়ার আগে দয়া করে সবকিছু ব্যাকআপ করুন !

নিম্নলিখিত ব্যাশের স্নিপেটগুলি একটি বড় স্ক্রিপ্টে সংমিশ্রিত করা যেতে পারে যা একই ফোল্ডারে চালানো উচিত যেখানে রেপো repo-org বাস করে । সরাসরি কমান্ড উইন্ডোতে সবকিছু অনুলিপি করে আটকানোর পরামর্শ দেওয়া হয় না (যদিও আমি এটি সফলভাবে পরীক্ষা করেছি)!

0. প্রস্তুতি

ভেরিয়েবল

# Root directory where repo-org lives
# and a temporary location for git filter-branch
root="$PWD"
temp='/dev/shm/tmp'

# The old repository and the subdirectory we'd like to extract
repo_old="$root/repo-old"
repo_old_directory='sub'

# The new submodule repository, its url
# and a hash map folder which will be populated
# and later used in the filter script below
repo_sub="$root/repo-sub"
repo_sub_url='https://github.com/somewhere/repo-sub.git'
repo_sub_hashmap="$root/repo-sub.map"

# The new modified repository, its url
# and a filter script which is created as heredoc below
repo_new="$root/repo-new"
repo_new_url='https://github.com/somewhere/repo-new.git'
repo_new_filter="$root/repo-new.sh"

ফিল্টার স্ক্রিপ্ট

# The index filter script which converts our subdirectory into a submodule
cat << EOF > "$repo_new_filter"
#!/bin/bash

# Submodule hash map function
sub ()
{
    local old_commit=\$(git rev-list -1 \$1 -- '$repo_old_directory')

    if [ ! -z "\$old_commit" ]
    then
        echo \$(cat "$repo_sub_hashmap/\$old_commit")
    fi
}

# Submodule config
SUB_COMMIT=\$(sub \$GIT_COMMIT)
SUB_DIR='$repo_old_directory'
SUB_URL='$repo_sub_url'

# Submodule replacement
if [ ! -z "\$SUB_COMMIT" ]
then
    touch '.gitmodules'
    git config --file='.gitmodules' "submodule.\$SUB_DIR.path" "\$SUB_DIR"
    git config --file='.gitmodules' "submodule.\$SUB_DIR.url" "\$SUB_URL"
    git config --file='.gitmodules' "submodule.\$SUB_DIR.branch" 'master'
    git add '.gitmodules'

    git rm --cached -qrf "\$SUB_DIR"
    git update-index --add --cacheinfo 160000 \$SUB_COMMIT "\$SUB_DIR"
fi
EOF
chmod +x "$repo_new_filter"

1. উপ-ডিরেক্টরি এক্সট্রাকশন

cd "$root"

# Create a new clone for our new submodule repo
git clone "$repo_old" "$repo_sub"

# Enter the new submodule repo
cd "$repo_sub"

# Remove the old origin remote
git remote remove origin

# Loop over all commits and create temporary tags
for commit in $(git rev-list --all)
do
    git tag "temp_$commit" $commit
done

# Extract the subdirectory and slice commits
mkdir -p "$temp"
git filter-branch --subdirectory-filter "$repo_old_directory" \
                  --tag-name-filter 'cat' \
                  --prune-empty --force -d "$temp" -- --all

# Populate hash map folder from our previously created tag names
mkdir -p "$repo_sub_hashmap"
for tag in $(git tag | grep "^temp_")
do
    old_commit=${tag#'temp_'}
    sub_commit=$(git rev-list -1 $tag)

    echo $sub_commit > "$repo_sub_hashmap/$old_commit"
done
git tag | grep "^temp_" | xargs -d '\n' git tag -d 2>&1 > /dev/null

# Add the new url for this repository (and e.g. push)
git remote add origin "$repo_sub_url"
# git push -u origin master

২. উপ-ডিরেক্টরি প্রতিস্থাপন

cd "$root"

# Create a clone for our modified repo
git clone "$repo_old" "$repo_new"

# Enter the new modified repo
cd "$repo_new"

# Remove the old origin remote
git remote remove origin

# Replace the subdirectory and map all sliced submodule commits using
# the filter script from above
mkdir -p "$temp"
git filter-branch --index-filter "$repo_new_filter" \
                  --tag-name-filter 'cat' --force -d "$temp" -- --all

# Add the new url for this repository (and e.g. push)
git remote add origin "$repo_new_url"
# git push -u origin master

# Cleanup (commented for safety reasons)
# rm -rf "$repo_sub_hashmap"
# rm -f "$repo_new_filter"

মন্তব্য: নতুন তৈরি করা রেপো যদি repo-newস্তব্ধ হয়ে যায় git submodule update --initতবে তার পরিবর্তে একবার পুনরায় সংগ্রহের পুনরায় ক্লোন করার চেষ্টা করুন:

cd "$root"

# Clone the new modified repo recursively
git clone --recursive "$repo_new" "$repo_new-tmp"

# Now use the newly cloned one
mv "$repo_new" "$repo_new-bak"
mv "$repo_new-tmp" "$repo_new"

# Cleanup (commented for safety reasons)
# rm -rf "$repo_new-bak"

0

এটি স্থানান্তরিত হয়, আপনি যে কোনও ফিল্টার-শাখা (আমি ব্যবহার করি git fetch . +refs/original/*:*) হিসাবে আপনি এটি ব্যাকআপ করতে পারেন ।

আমার একটি utilsলাইব্রেরি সহ একটি প্রকল্প রয়েছে যা অন্যান্য প্রকল্পগুলিতে কার্যকর হতে শুরু হয়েছিল এবং এর ইতিহাসকে একটি সাবমডিউলে বিভক্ত করতে চেয়েছিল। প্রথমে এসও দেখার চেষ্টা করিনি তাই আমি নিজের লেখা লিখলাম, এটি স্থানীয়ভাবে ইতিহাস তৈরি করে তাই এটি বেশ দ্রুততর হয়, যার পরে আপনি যদি চান তবে আপনি সাহায্যকারী কমান্ডটি সেট আপ করতে পারেন.gitmodules ফাইল এবং এই এবং সাবমোডুলের ইতিহাসগুলি নিজেরাই যে কোনও জায়গায় চাপতে পারেন তুমি চাও.

স্ট্রিপড কমান্ড নিজেই এখানে রয়েছে, ডক এর মন্তব্যে রয়েছে, তারপরে থাকা স্ট্রাস্ট্রিপড একটিতে। সেটটিকে নিজের কমান্ড হিসাবে চালিত করুন subdir, যেমন subdir=utils git split-submoduleআপনি utilsডিরেক্টরিটি বিভক্ত করছেন । এটি হ্যাকি কারণ এটি এক-বন্ধ, তবে আমি গিট ইতিহাসে ডকুমেন্টেশন সাব-ডিরেক্টরিতে এটি পরীক্ষা করেছি।

#!/bin/bash
# put this or the commented version below in e.g. ~/bin/git-split-submodule
${GIT_COMMIT-exec git filter-branch --index-filter "subdir=$subdir; ${debug+debug=$debug;} $(sed 1,/SNIP/d "$0")" "$@"}
${debug+set -x}
fam=(`git rev-list --no-walk --parents $GIT_COMMIT`)
pathcheck=(`printf "%s:$subdir\\n" ${fam[@]} \
    | git cat-file --batch-check='%(objectname)' | uniq`)
[[ $pathcheck = *:* ]] || {
    subfam=($( set -- ${fam[@]}; shift;
        for par; do tpar=`map $par`; [[ $tpar != $par ]] &&
            git rev-parse -q --verify $tpar:"$subdir"
        done
    ))
    git rm -rq --cached --ignore-unmatch  "$subdir"
    if (( ${#pathcheck[@]} == 1 && ${#fam[@]} > 1 && ${#subfam[@]} > 0)); then
        git update-index --add --cacheinfo 160000,$subfam,"$subdir"
    else
        subnew=`git cat-file -p $GIT_COMMIT | sed 1,/^$/d \
            | git commit-tree $GIT_COMMIT:"$subdir" $(
                ${subfam:+printf ' -p %s' ${subfam[@]}}) 2>&-
            ` &&
        git update-index --add --cacheinfo 160000,$subnew,"$subdir"
    fi
}
${debug+set +x}

#!/bin/bash
# Git filter-branch to split a subdirectory into a submodule history.

# In each commit, the subdirectory tree is replaced in the index with an
# appropriate submodule commit.
# * If the subdirectory tree has changed from any parent, or there are
#   no parents, a new submodule commit is made for the subdirectory (with
#   the current commit's message, which should presumably say something
#   about the change). The new submodule commit's parents are the
#   submodule commits in any rewrites of the current commit's parents.
# * Otherwise, the submodule commit is copied from a parent.

# Since the new history includes references to the new submodule
# history, the new submodule history isn't dangling, it's incorporated.
# Branches for any part of it can be made casually and pushed into any
# other repo as desired, so hooking up the `git submodule` helper
# command's conveniences is easy, e.g.
#     subdir=utils git split-submodule master
#     git branch utils $(git rev-parse master:utils)
#     git clone -sb utils . ../utilsrepo
# and you can then submodule add from there in other repos, but really,
# for small utility libraries and such, just fetching the submodule
# histories into your own repo is easiest. Setup on cloning a
# project using "incorporated" submodules like this is:
#   setup:  utils/.git
#
#   utils/.git:
#       @if _=`git rev-parse -q --verify utils`; then \
#           git config submodule.utils.active true \
#           && git config submodule.utils.url "`pwd -P`" \
#           && git clone -s . utils -nb utils \
#           && git submodule absorbgitdirs utils \
#           && git -C utils checkout $$(git rev-parse :utils); \
#       fi
# with `git config -f .gitmodules submodule.utils.path utils` and
# `git config -f .gitmodules submodule.utils.url ./`; cloners don't
# have to do anything but `make setup`, and `setup` should be a prereq
# on most things anyway.

# You can test that a commit and its rewrite put the same tree in the
# same place with this function:
# testit ()
# {
#     tree=($(git rev-parse `git rev-parse $1`: refs/original/refs/heads/$1));
#     echo $tree `test $tree != ${tree[1]} && echo ${tree[1]}`
# }
# so e.g. `testit make~95^2:t` will print the `t` tree there and if
# the `t` tree at ~95^2 from the original differs it'll print that too.

# To run it, say `subdir=path/to/it git split-submodule` with whatever
# filter-branch args you want.

# $GIT_COMMIT is set if we're already in filter-branch, if not, get there:
${GIT_COMMIT-exec git filter-branch --index-filter "subdir=$subdir; ${debug+debug=$debug;} $(sed 1,/SNIP/d "$0")" "$@"}

${debug+set -x}
fam=(`git rev-list --no-walk --parents $GIT_COMMIT`)
pathcheck=(`printf "%s:$subdir\\n" ${fam[@]} \
    | git cat-file --batch-check='%(objectname)' | uniq`)

[[ $pathcheck = *:* ]] || {
    subfam=($( set -- ${fam[@]}; shift;
        for par; do tpar=`map $par`; [[ $tpar != $par ]] &&
            git rev-parse -q --verify $tpar:"$subdir"
        done
    ))

    git rm -rq --cached --ignore-unmatch  "$subdir"
    if (( ${#pathcheck[@]} == 1 && ${#fam[@]} > 1 && ${#subfam[@]} > 0)); then
        # one id same for all entries, copy mapped mom's submod commit
        git update-index --add --cacheinfo 160000,$subfam,"$subdir"
    else
        # no mapped parents or something changed somewhere, make new
        # submod commit for current subdir content.  The new submod
        # commit has all mapped parents' submodule commits as parents:
        subnew=`git cat-file -p $GIT_COMMIT | sed 1,/^$/d \
            | git commit-tree $GIT_COMMIT:"$subdir" $(
                ${subfam:+printf ' -p %s' ${subfam[@]}}) 2>&-
            ` &&
        git update-index --add --cacheinfo 160000,$subnew,"$subdir"
    fi
}
${debug+set +x}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.