Git - 如何压缩对忽略文件的更改而不会丢失这些更改? [英] Git - How do I squash changes to ignored files without losing those changes?
问题描述
我想用git来保存一段时间内应用程序使用的实际依赖关系的历史记录,其保真度比我能从包管理器获得的要高。
我正在使用这些分支:
- master:仅限源代码。依赖于
.gitignore
- 构建:源代码和依赖项
- build- $ TIMESTAMP:用于强制提交忽略文件的临时分支
此脚本 build-release.sh
$ b $ $ p $ DEV_MODULES =mocha chai bower coffeelint
BUILT_FILES =node_modules build
b DATE = $(date)
TIMESTAMP = $(date +%s)
BRANCH = $(git rev-parse --abbrev-ref HEAD)
#用当前的依赖和二进制文件创建一个临时分支
npm卸载$ DEV_MODULES
git checkout -b build- $ TIMESTAMP
git add --all --force $ BUILT_FILES
git commit -m从$ BRANCH复制$ BUILT_FILES
#将临时分支合并到构建分支中
git branch build || echo构建分支已经存在
git checkout构建 - 强制
git合并构建 - $ TIMESTAMP --strategy =子树-m构建自$ DATE
git分支-D构建 - $ TIMESTAMP
#恢复原始分支
git checkout $ BRANCH
git checkout build - $ BUILT_FILES
git rm -r --cached $ BUILT_FILES
其中的一个工作原理,给我一个有用的视图,说明从一个发行版到另一个发行版对源代码,依赖项和二进制文件的更改下一个:
但需要的提交次数是其中的两倍。我希望树看起来像这样:
如何将复制构建文件提交与构建为提交结合起来?
当我尝试 git merge --squash
时,它最终会出现在 build
上的状态,而不是该状态位于 build- $ TIMESTAMP
上,这是不正确的(我想将更改导入被忽略的文件,但合并似乎没有语言来执行此操作)。当我尝试 git rebase --onto build build- $ TIMESTAMP
我失去了新提交的血统。
我只想记录我在 build-$ TIMESTAMP
分支上获得的确切文件,但是同时使用 build
然后将 build
分支指向该提交。
这是简单直接的管道领域。您正在使用瓷器命令,这是在git内容跟踪器核心的基础上构建的源代码控制系统,它可以让瓷器变成您想做的事情,但直接与内容跟踪器直接交流会更简单。
要获得最简单的阅读内容,即希望构建分支记录当前结帐的快照以及选择什么内容$ BUILT_FILES
路径/目录,它是
#knobs
DEV_MODULES =mocha chai bower coffeelint
BUILT_FILES =node_modules build
DATE = $(日期)
#清理我们不在乎的东西
npm卸载$ DEV_MODULES
#记录当前结帐加上$ BUILT_FILES到`build`分支
git add --all --force $ BUILT_FILES
build =` git rev-parse -q --verify build`
git update-ref refs / heads / build $(
git commit-tree $ {build:+ - p $ build} -p HEAD \
-m从$ DATE开始构建\
`git write-tree`
)
#重置索引到HEAD
git reset#`git read- tree HEAD`会有同样的效果,也许更安静
作为一个快速概览或提醒可能是,git的对象数据库是散列码索引的键值存储。你可以通过它的类型和散列值向git寻求任何东西,它有意地从它的对象db中回想起它。索引只不过是一个平面文件,一个路径索引清单,显示对象数据库中的内容在哪条路径上,以及用于跟踪正在进行的操作的一些元数据和注释。 git add
路径中的内容只是将对象数据库中的内容和内容的哈希放在该路径的索引条目中。
(这里有点咆哮,如果不是在鼓吹的心情就跳过)要理解的是git完全是残酷的具体。关于对象数据库之外的存储库的一切都是纯粹的约定。 git checkout
使 HEAD
指的是纯粹按照惯例签出的提交。你可以在 git read-tree -um
附近实现 git checkout
将 HEAD
设置为您从中获取该树的提交。 git commit
使 HEAD
是纯粹按照惯例提交的内容。您可以自己实现 git commit
作为 git write-tree
,主要的额外操作是提供 HEAD
作为父项,并且tu opdate HEAD
到新的提交。名称 HEAD
本身就是传统的。构建这些内容的内容跟踪器无需关心 HEAD
,或者分支和标签之间的区别,或者任何类型的内容。由于(a)不需要抽象化,内容模型已经完全符合要求,(b)git的整个要点是核心缺乏抽象:它是愚蠢的,愚蠢的, 。
I want to use git to keep a historical record of the actual dependencies an application has used over time, with higher fidelity than I can get from the package manager.
I am using these branches:
- master: source code only. dependencies in
.gitignore
- build: source code and dependencies
- build-$TIMESTAMP: temporary branch used to force commit of ignored files
And this script, build-release.sh
:
DEV_MODULES="mocha chai bower coffeelint"
BUILT_FILES="node_modules build"
DATE=$(date)
TIMESTAMP=$(date +"%s")
BRANCH=$(git rev-parse --abbrev-ref HEAD)
# create a temporary branch with the current dependencies and binaries
npm uninstall $DEV_MODULES
git checkout -b build-$TIMESTAMP
git add --all --force $BUILT_FILES
git commit -m "copy $BUILT_FILES from $BRANCH"
# merge the temporary branch into the build branch
git branch build || echo "build branch already exists"
git checkout build --force
git merge build-$TIMESTAMP --strategy=subtree -m "Build as of $DATE"
git branch -D build-$TIMESTAMP
# restore the original branch
git checkout $BRANCH
git checkout build -- $BUILT_FILES
git rm -r --cached $BUILT_FILES
Which works, and gives me a useful view of changes to source, dependencies, and binaries from one release to the next:
But it takes twice as many commits as necessary. I want the tree to look like this:
How can I combine the "copy built files" commit with the "build as of" commit?
When I try to git merge --squash
, it ends up with the state that was on build
instead of the state that was on build-$TIMESTAMP
, which is incorrect (I want to import changes to ignored files, but merge seems to have no language to do this). When I try to git rebase --onto build build-$TIMESTAMP
I lose the parentage of the new commit.
I just want to record the exact files I get on the build-$TIMESTAMP
branch, but with both the build
and master
branches as parents, then point the build
branch to that commit.
This is straightforward plumbing territory. You're using "porcelain" commands, the source-control system built on top of git's content-tracker core, in ways that happen to winkle that porcelain into doing what you want, but it's much simpler to just talk to the content tracker directly.
For the simplest reading of what's in your question, namely that you want your "build" branch to record snapshots of the current checkout along with a selection of what's in the the "$BUILT_FILES"
paths/directories, it's
# knobs
DEV_MODULES="mocha chai bower coffeelint"
BUILT_FILES="node_modules build"
DATE=$(date)
# clean out stuff we don't care about
npm uninstall $DEV_MODULES
# record current checkout plus "$BUILT_FILES" to `build` branch
git add --all --force $BUILT_FILES
build=`git rev-parse -q --verify build`
git update-ref refs/heads/build $(
git commit-tree ${build:+-p $build} -p HEAD \
-m "build as of $DATE" \
`git write-tree`
)
# reset index to HEAD
git reset # `git read-tree HEAD` will have the same effect, perhaps more quietly
As a quick overview or reminder as the case may be, git's object database is a hashcode-indexed key-value store. You ask git for anything by its type and hash, it obligingly regurgitates exactly that from its object db. The index is nothing more than a flat file, a path-indexed manifest, showing what content in the object db goes at what path, along with some metadata and notes for tracking in-flight operations. git add
ing content at a path just puts the content in the object db and the content's hash in the index entry for that path.
(somewhat of a rant here, skip if not in the mood to be preached at) The thing to understand is that git is utterly, brutally concrete. Everything about a repository beyond the object db is pure convention. git checkout
makes HEAD
refer to the commit you checked out purely by convention. You can implement git checkout
as an extremely thin wrapper around git read-tree -um
-- the chief extra operation being to set HEAD
to the commit you got that tree from . git commit
makes HEAD
the parent of what you're committing purely by convention. You can implement git commit
yourself as an extremely thin wrapper around git commit-tree
and git write-tree
, the chief extra operation being to supply HEAD
as a parent and tu opdate HEAD
to the new commit. The name HEAD
is itself purely conventional. The content tracker on which those are built couldn't care less about HEAD
, or the distinction between branches and tags, or anything of the sort. The conventions are intentionally, aggressively and brutally simple, because (a) there's no need for abstraction, the content model already matches the requirements perfectly, and (b) the whole point of git is the lack of abstraction at the core: it's "stupid".
这篇关于Git - 如何压缩对忽略文件的更改而不会丢失这些更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!