2

假设我有一个目录/home/userX/whatever结构:

./wrapper
./wrapper/folder_A
./wrapper/folder_A/file_1
./wrapper/folder_A/file_2
./wrapper/folder_B
./wrapper/folder_B/file_3
./wrapper/folder_B/file_4

我想将它们提交到/home/userX/somewhere/repo.gitgit 树中具有此结构的 git repo 中:

./blah
./blah/folder_A
./blah/folder_A/file_1
./blah/folder_A/file_2
./blah/folder_B
./blah/folder_B/file_3
./blah/folder_B/file_4

无需将这些文件 1~4 复制或移动到另一个文件夹或触摸系统权限(userX不是 sudoer)。

是否可以?有插图吗?

4

3 回答 3

3

在一般文件所有权区域,您的要求并不是我所说的非常清楚,但这里有一些方法可以做到;希望您能找到一款适合您实际需求的产品。

0级——不要那样做

您很可能可以说服自己不要真正需要这样做。它永远不会像您希望的那样具有原子性,因此请避免复杂性。

如果失败了……</p>

级别 1——硬链接

对于大多数操作系统,您可以通过在文件系统级别伪造复制来欺骗自己的出路。您似乎拒绝这样做,因为需要 root 或 sudo,但它比我必须首先提及的任何其他内容要容易得多。

在过去,您只需将whatever/wrapper 目录硬链接到cheat/blah.,但现在它似乎已被弃用,因为我的示例将使用绑定挂载。

git clone /home/userX/somewhere/repo.git level1
cd level1
mkdir blah
sudo mount --bind /home/userX/whatever/wrapper blah
git add blah
git commit
git push
sudo umount blah

简而言之:将要提交的文件挂载到对 git 有意义的地方。然后添加、提交并将它们推送到您希望它们存储的位置。

2级——没有cp

您可以通过直接在存储库中对文件进行哈希处理来节省副本。

git clone /home/userX/somewhere/repo.git level2
cd level2
for f in folder_A/file_1 folder_A/file_2 folder_B/file_3 folder_B/file_4
do
  sha=$(git hash-object -w /home/userX/whatever/wrapper/$f)
  git update-index --add --cacheinfo 644 $sha blah/$f
done

然后像往常一样提交和推送:

git commit
git push

请注意,该git hash-object -w命令(除了git push,显然)是复制文件的命令。

3级——无克隆

您可以从外部“直接”提交到本地裸存储库。blob 散列部分与级别 2 相同,只是没有克隆。构建实际的提交会有点棘手。

首先,我们需要创建一个引用新文件的更新索引。由于 git 并没有任何命令来操作外部树(而且我们不想过多地依赖内部的格式),我们将使用一个二级索引,我们最初将从存储库的头部复制. 除此之外,它看起来类似于第 2 级。

mkdir level3
cd level3
export GIT_DIR=/home/userX/somewhere/repo.git
export GIT_INDEX_FILE=hack
git read-tree HEAD
for f in folder_A/file_1 folder_A/file_2 folder_B/file_3 folder_B/file_4
do
  sha=$(git hash-object -w /home/userX/whatever/wrapper/$f)
  git update-index --add --cacheinfo 644 $sha blah/$f
done
tree=$(git write-tree)

现在您可以创建一个提交对象并更新 HEAD。

parent=$(git rev-parse HEAD)
commit=$(git commit-tree -p $parent -m Your_message_here $tree)
git update-ref HEAD $commit $parent

不要忘记在你身后清理:

rm hack
unset GIT_DIR GIT_INDEX_FILE

4 级——远程

可以远程完成所有这些操作,但据我所知,没有 git 管道命令可以帮助我们。 git send-pack假设一个本地存储库,这似乎超出了这里的范围。所以你需要编写一个专用的 git 协议客户端。

结束的想法

如果您的目标是所有这些的严重可移植性,您应该:

  • 进一步研究--path选项git hash-object
  • 确保避免二级索引名称上的命名冲突
于 2013-10-29T23:24:45.710 回答
2

通过边带索引导入你想要的

loaded_tree=$(
    export GIT_INDEX_FILE=$PWD/.git/throwaway
    export GIT_DIR=$PWD/.git
    rm -f .git/throwaway

    export GIT_WORK_TREE=/home/userX/whatever/wrapper
    cd $GIT_WORK_TREE

    git add folder_A folder_B  # add contents (relative to GIT_WORK_TREE)
    git add anything else      # . to repo, record locations ditto in index

    git write-tree             # make an honest tree of it
)

并将其加载到您想要的新分支中

git checkout --orphan newbranch
git read-tree --empty          # (wipe it because `--prefix=` merges)
git read-tree  $loaded_tree --prefix=blah/
git commit -m "You're done."       
于 2013-10-30T01:44:57.557 回答
1

这很可能使用git 内部命令。请注意以下命令,因为这些命令会干扰 git 用来表示文件结构的文件,如果使用不当,可能会导致损坏或不正确的状态。

这将仅在创建单个文件之后进行,其余的将遵循类似的过程。所有这些命令都从repo.git提到的文件夹运行,该文件夹被假定为--bare存储库:

> cd /home/userX/somewhere

第一步是创建包含您要提交的文件的 git 对象。此命令将在 git 对象存储中创建文件的压缩副本。这个副本是不可避免的,因为它是 git 需要保留文件版本的内部副本:

> git hash-object -w /home/userX/whatever/wrapper/folder_A/file_1
cafebabe00000000000000000000000000000000

打印的散列是为存储而创建的对象file_1。哈希当然是虚构的,因为它取决于文件的实际内容。

为了创建file_1要定位的子文件夹,您需要为每个子文件夹级别创建一个树对象。注意创建的哈希是如何用于下一个子文件夹树的:

> echo -e "100644 blob cafebabe00000000000000000000000000000000\tfile_1" | git mktree
1111face11111111111111111111111111111111

> echo -e "040000 tree 1111face11111111111111111111111111111111\tfolder_a" | git mktree
2222face22222222222222222222222222222222

最后一个散列是将添加到当前树的散列。如果在--barerepo 中工作,我们需要首先创建一个干净的索引。如果您在普通存储库中工作,则可以跳过此步骤,因为已经有索引文件,只需确保git status显示干净状态即可。

--barerepo 中不应该有索引文件,但以防万一,删除它:

> rm index

为了创建一个干净的索引文件,我们添加了我们想要提交的分支的内容,我们假设master. 此命令将打印当前存在的树的内容,master并将其设置为当前索引:

> git cat-file -p master^{tree} | git update-index --index-info

现在有了一个干净的索引,我们添加了包含指向我们手动添加的文件的树的子目录。read-tree将获取给定的树哈希并将其作为子文件夹添加到当前索引中,如下所述--prefix

> git read-tree --prefix=blah 2222face22222222222222222222222222222222

然后我们创建另一个代表索引当前状态的树。write-tree将从当前索引中读取并从中生成树哈希:

> git write-tree
deadbeafaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

最后一个散列是我们将用来创建提交的散列:

> echo "at last a commit" | git commit-tree deadbeafaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -p master
baddcafe11111111111111111111111111111111

最后但同样重要的是,我们更新master以指向新的提交:

> git branch --force master baddcafe11111111111111111111111111111111

有了这个,我们设法将一个文件添加到 git 中,而无需将其复制到工作区。所有这些步骤都是做什么git addgit commit最终做什么。通过 git 内部命令复制它们最终需要大量工作,而且很容易发生意外。如果你真的需要小心处理这个线程并了解 git 为你处理的底层结构,以便你可以使用它并修改它。

于 2013-10-29T08:44:34.980 回答