简体   繁体   English

Git 来自几个(不是全部)未暂存文件的补丁

[英]Git patch from several (not all) unstaged files

I read about git patch command, but all of the examples show how to get a patch for all not staged or cached files or even all files that some commit includes (from one commit till another).我阅读了有关git patch命令的信息,但所有示例都显示了如何为所有未暂存或缓存的文件,甚至是某些提交包含的所有文件(从一个提交到另一个提交)获取补丁。 But suppose I have 10 not staged files and I need to create one proper patch just for 6 of them.但是假设我有10 个未暂存的文件,我需要为其中的 6 个创建一个适当的补丁 How can I do this?我怎样才能做到这一点? And if there is a way to create such patch, how can I apply it?如果有办法创建这样的补丁,我该如何应用它?
Sorry if obvious.很抱歉,如果很明显。

You'd have to define the phrase proper patch first.您必须首先定义短语适当的补丁 What makes a patch proper?什么使补丁正确? What makes one improper?是什么让一个人不合适? For that matter, what's the difference between a patch and a diff?就此而言,补丁差异有什么区别? There is no one fixed answer, but see Difference between patch and diff files .没有一个固定的答案,但请参阅补丁和差异文件之间的区别

That said, git diff produces diffs, and is quite flexible.也就是说, git diff产生差异,并且非常灵活。 It's meant for human use, not for use by machines, so its output may or may not be what you want (especially since you did not define any of your terms).它是供人类使用的,而不是供机器使用的,因此它的 output 可能是也可能不是您想要的(特别是因为您没有定义任何术语)。 The git format-patch program is less flexible and meant more for use by machines: it produces output that can be digested easily by git apply , which is meant to apply a single patch without committing it, or git am , which is meant to apply a whole series of patches stored on your computer in "mailbox format", committing each one. The git format-patch program is less flexible and meant more for use by machines: it produces output that can be digested easily by git apply , which is meant to apply a single patch without committing it, or git am , which is meant to apply一整套补丁以“邮箱格式”存储在您的计算机上,提交每个补丁。 ( am here is short for Apply Mailbox, more or less.) (这里am应用邮箱的缩写,或多或少。)

Because you haven't defined your terms, there's no single right answer to your question.因为你还没有定义你的术语,所以你的问题没有一个正确的答案。 If we assume you mean produce a file that git apply can apply , we get one possible answer.如果我们假设您的意思是生成git apply可以应用的文件,我们会得到一个可能的答案。 If we assume you mean produce a mailbox-format patch that git am can apply and commit , we get a different answer.如果我们假设您的意思是生成git am可以应用和提交的邮箱格式补丁,我们会得到不同的答案。

The git format-patch command will produce a mailbox formatted patch (or series of patches) from some commit or commits . git format-patch命令将从一些提交或提交中生成一个邮箱格式的补丁(或一系列补丁)。 So to use it, you must make a commit .所以要使用它,你必须做出一个 commit You can simply commit those particular files you wish to have in your patch, on a new branch if you like.如果您愿意,您可以简单地在新分支上提交您希望在补丁中包含的那些特定文件。 (See the long details below.) (请参阅下面的详细信息。)

The git diff program, or any of its more machine-oriented related commands ( git diff-tree , git diff-files , git diff-index ) will produce a human-readable diff. The git diff program, or any of its more machine-oriented related commands ( git diff-tree , git diff-files , git diff-index ) will produce a human-readable diff. If it is not colorized, it will be suitable for use with git apply .如果没有上色,则适合与git apply一起使用。 To use these, you need not make a commit.要使用这些,您无需提交。

As LeGEC noted , you can use git diff on specific files.正如LeGEC 所指出的,您可以在特定文件上使用git diff Note that by default, git diff -- paths compare the index copy of each specified path to the work-tree copy of the same path.请注意,默认情况下, git diff -- paths将每个指定路径的索引副本与同一路径的工作树副本进行比较。 This is probably what you want.这可能就是你想要的。 If you have configured git diff to always produce colorized output, turn that off for the duration of the one git diff operation.如果您已将git diff配置为始终生成彩色 output,请在一个git diff操作期间将其关闭。 Save the output somewhere:将 output 保存在某处:

git diff -- file1 ... fileN > /tmp/patch

or:或者:

git diff --color=never -- file1 ... fileN > /tmp/patch

if you need to disable colorization.如果您需要禁用着色。

Long: about commits长:关于提交

If the above suffices, there's no need to read the rest of this.如果以上就够了,就没有必要再看这篇的rest了。

If your definition of proper patch means one that git am can turn into a commit , you will need to make a new commit.如果您对正确补丁的定义意味着git am可以变成一个提交,那么您将需要进行新的提交。 This is where knowing what staged vs not staged , and how branch names and commits all work, becomes very important.这就是了解stagednot staged以及分支名称和提交如何工作变得非常重要的地方。

Git is really, at its heart, all about commits. Git 的核心其实就是提交。 Branches—or more specifically, branch names —are useful, especially if you are a human, but they're not really what Git is about.分支——或者更具体地说,分支名称——很有用,特别是如果你是人类,但它们并不是 Git 的真正含义。 Git is all about the commits . Git 都是关于提交的。

Every commit is numbered, but the numbers are not simple sequential counts.每个提交都有编号,但这些数字不是简单的顺序计数。 We can't find commit #1, then commit #2, and so on.我们找不到提交#1,然后提交#2,依此类推。 Instead, each commit gets a unique hash ID .相反,每个提交都会获得一个唯一的hash ID This thing looks random, but in fact is entirely non-random, and is carefully computed so that every Git will number a 100%-identical, bit-for-bit-the-same commit the same way.这个东西看起来是随机的,但实际上完全是非随机的,并且是经过仔细计算的,因此每个Git 都会以相同的方式编号 100% 相同的、逐位相同的提交。 That way, every Git everywhere will agree that this commit gets this hash ID, and no other commit gets this hash ID.这样,每个地方的每个 Git 都会同意这个提交得到这个hash ID,并且没有其他提交得到这个 hash ID。

What goes into a commit comes in two parts: the data , and the metadata:提交的内容分为两部分:数据元数据:

  • The data in a commit is pretty simple: it's a full snapshot of every file that Git knows about .提交中的数据非常简单:它是 Git 知道的每个文件的完整快照。

    This obviously makes Git repositories grow enormously fat, since every commit stores every file.这显然使 Git 存储库变得非常庞大,因为每个提交都存储每个文件。 But they don't (grow enormously fat).但他们不会(变得非常肥胖)。 The reason is that the files are stored in a special, read-only, Git-only, compressed and de-duplicated format.原因是文件以特殊的、只读的、仅限 Git 的、压缩的和去重复的格式存储。 If you make a thousand commits, each with 1000 files, but re-use 999 of the 1000 files each time, then all thousand commits share 999 files with other commits.如果您进行一千次提交,每个提交有 1000 个文件,但每次重用1000 个文件中的 999 个,那么所有一千次提交与其他提交共享999 个文件。

    (There is more to it than this, but the de-duplication is the first step, and a very big one.) (除此之外还有更多,但重复数据删除是第一步,而且是非常大的一步。)

  • The metadata in a commit is information about the commit.提交中的元数据是有关提交的信息。 This is where Git stores the name and email address of the person who made the commit, for instance.例如,这是 Git 存储提交人的姓名和 email 地址的地方。 In this metadata, Git stores one particular set of information that Git needs for itself, though.不过,在此元数据中,Git 存储了 Git 自身需要的一组特定信息。 Every commit stores the commit number —the hash ID—of its parent commit.每个提交都存储其父提交的提交编号——hash ID。 This is how history exists in a repository.这就是历史在存储库中的存在方式。

Since the files in the commits are read-only —and Git-only, frozen-for-all-time, compressed into this special freeze-dried format that only Git itself can use—the files you work with have to be extracted from commits.由于提交中的文件是只读的——而且只有Git,永远冻结,压缩成这种特殊的冻干格式,只有 Git 本身可以使用——使用的文件必须从提交中提取. This is what git checkout (or, since Git 2.23, git switch ) does.这就是git checkout (或者,由于 Git 2.23, git switch )所做的。 You pick a commit and tell Git: Extract all the files from this commit, so that I can see them and work with / on them.您选择一个提交并告诉 Git:从该提交中提取所有文件,以便我可以看到它们并使用 / 处理它们。

Since each commit remembers its previous commit, all you need to do, to use Git, is have Git remember for you the unique hash ID of the last commit.由于每个提交都会记住其先前的提交,因此您需要做的就是使用Git,让 Git 记住最后一次提交的唯一 hash ID。 This is where branch names come in. A branch name simply holds the hash ID of the last commit that is to be treated as part of that branch.这是分支名称的来源。分支名称仅包含要被视为该分支一部分的最后一次提交的 hash ID。

This means we can draw a branch like this:这意味着我们可以像这样绘制一个分支:

... <-F <-G <-H   <--master

The name master holds the actual hash ID of the last commit, which at this point is hash ID H .名称master持有最后一次提交的实际 hash ID,此时为 hash ID H That commit holds a snapshot of all of your files, and also holds the hash ID of earlier commit G .该提交包含您所有文件的快照,并且还包含先前提交G的 hash ID。

Git can look up any commit—any internal object, really—by its hash ID, so Git can look up commit H , extract all of its files, and let you work on it. Git can look up any commit—any internal object, really—by its hash ID, so Git can look up commit H , extract all of its files, and let you work on it. Or, Git can look up commit H , find its parent hash ID G , and look up commit G .或者, Git 可以查找提交H ,找到其父 hash ID G ,并查找提交G Git can then extract G 's files, or look up its parent hash ID F . Git 然后可以提取G的文件,或查找其父 hash ID F This is the first of the big secrets of Git.这是 Git 的第一个大秘密。

Commits, your work-tree, and the index提交、你的工作树和索引

Given the above—read-only commits, and read/write files—we have already seen that Git must extract a commit into an area where you can see and work on your files.鉴于上述只读提交和读/写文件,我们已经看到 Git 必须将提交提取到您可以查看和处理文件的区域中。 This area is your working tree or work-tree .该区域是您的工作树工作树 So there are two copies of each file of interest: the one in the current commit, which is frozen for all time, and the one in your work-tree, which you can use.因此,每个感兴趣的文件都有两个副本:一个在当前提交中,它一直被冻结,另一个在您的工作树中,您可以使用它。 It's pretty straightforward, with one twist: you can create files in this area that Git doesn't know about.它非常简单,有一个转折点:您可以在 Git知道的区域创建文件。

The really tricky part here, though, is how Git makes a new commit , and what files Git does know about.不过,这里真正棘手的部分是 Git 如何进行新的提交,以及 Git确实知道哪些文件。 You might think that Git would just keep a list of file names, for instance, and use your work-tree files to make new commits... but it doesn't.例如,您可能认为 Git 只会保留文件名列表,并使用您的工作树文件进行新提交......但事实并非如此。

Instead, Git keeps a third copy—well, a de-duplicated copy, in the freeze-dried format—in an in-between place.取而代之的是,Git 在中间位置保留了第三个副本——好吧,一个去重的副本,采用冻干格式。 Between the current commit and your work-tree, Git has this other "copy" (already de-duplicated, so not exactly a copy) of each file that Git took out of the commit at checkout time .在当前提交和您的工作树之间,Git 具有Git 在结帐时从提交中取出的每个文件的另一个“副本”(已经去重,所以不完全是副本)。

These in-between "copies" of each file are in what Git calls, variously, the index , or the staging area , or (rarely these days) the cache .每个文件的这些中间“副本”位于 Git 中,不同地称为indexstaging area ,或(最近很少) cache Note that these copies are ready to go into a new commit, as they're already in the Git-only, freeze-dried format.请注意,这些副本已准备好将 go 放入新的提交中,因为它们已经是仅 Git 的冻干格式。 Unlike the copies in the commits themselves, they can be replaced, though.与提交本身的副本不同,它们可以被替换。

This is what git add is all about.这就是git add的全部内容。 The git add command means Make the index copy of some file(s) match the work-tree copy. git add命令意味着使某些文件的索引副本与工作树副本匹配。 If you have changed a file in your work-tree, you must tell Git to copy the updated file back into Git's index.如果您更改了工作树中的文件,则必须告诉 Git 将更新的文件复制回 Git 的索引。

This is what a staged file is.这就是暂存文件 At all times, Git's index has copies of each file that Git knows about.在任何时候,Git 的索引都有 Git 知道的每个文件的副本。 If it's in the index, it's ready to be committed—but maybe it's the same as the file in the current commit, If so, it's already de-duplicated.如果它在索引中,它就可以提交了——但它可能与当前提交中的文件相同,如果是,它已经被重复数据删除了。 and Git can easily tell that it's the same.和 Git 可以很容易地看出它是相同的。

If the index copy of a file is different from the current commit copy, or is entirely new, then what will go into the next commit is different from what is in the current commit.如果文件的索引副本与当前提交副本不同,或者是全新的,那么 go 进入下一个提交的内容与当前提交中的内容不同 Git calls that staged . Git 调用上演. If the index copy is the same as the committed copy, though, Git says nothing at all.但是,如果索引副本与提交的副本相同,则 Git 什么也不说。

Meanwhile, the index copy of a file might match the work-tree copy, or not.同时,文件的索引副本可能与工作树副本匹配,也可能不匹配。 If the index copy does match the work-tree file, Git doesn't say anything about it.如果索引副本确实与工作树文件匹配,则 Git 不会对此进行任何说明。 If not, Git says that the file is unstaged .如果不是, Git 说文件是unstaged

This means a file can be both staged for commit and not staged for commit , at the same time, If the committed copy (which can't be changed) doesn't match the index copy, and the index copy doesn't match the work-tree copy, you have a file that is both staged for commit and not staged for commit .这意味着一个文件可以同时为提交暂存和不为提交暂存,如果提交的副本(无法更改)与索引副本不匹配,并且索引副本与索引副本不匹配工作树副本,您有一个文件,该文件既为 commit暂存,又不为 commit 暂存 You can get this state by doing:您可以通过以下方式获得此 state:

git checkout somebranch
edit file.ext             # change something in a file
git add file.ext          # copy the updated file back into Git's index
edit file.ext             # change something else in the same file

When you run git commit , what Git does is to make a new commit from whatever is in Git's index at that time.当您运行git commit时, Git 所做的是从当时 Git 索引中的任何内容进行新的提交。 So if you have ten unstaged files right now, and you git add six of them and then run git commit , you get a new commit in which:因此,如果您现在有十个未暂存的文件,并且您git add其中六个然后运行git commit ,您将获得一个新的提交,其中:

  • six files don't match the previous commit (the one that's current before you run git commit ), but六个文件与上一个提交不匹配(运行git commit之前的当前提交),但是
  • all the other files do match the previous commit.所有其他文件与之前的提交匹配。

Now that you have made the new commit, the new commit is the current commit.现在您已经做出了新的提交,新的提交就是当前的提交。 You made it from the files that are in the index, so all files in the new, now-current commit match all the files that are in the index.您是索引中的文件创建的,因此新的当前提交的所有文件都与索引中的所有文件匹配。 No files are "staged for commit", but the four files you didn't git add are still there in your work-tree, still different from the corresponding four files in the index and those four files in the current commit.没有文件被“暂存以供提交”,但是您没有git add的四个文件仍然存在于您的工作树中,与索引中相应的四个文件和当前提交中的这四个文件仍然不同。 So these four files are still "not staged for commit".所以这四个文件仍然是“未暂存提交”。

If you like, you can now git add these four files to copy the work-tree versions back into Git's index, and then git commit the result.如果你喜欢,你现在可以git add这四个文件,将工作树版本复制回 Git 的索引,然后git commit结果。 You now have two new commits that you did not have before.您现在有两个以前没有的新提交。 The last one matches your work-tree, so that the current commit, Git's index, and your work-tree all match: no files are staged and no files are unstaged.最后一个与您的工作树匹配,因此当前提交、Git 的索引和您的工作树都匹配:没有文件被暂存,也没有文件被取消暂存。

More about branch names更多关于分支名称

Note that each time you do make a new commit, Git has to update the current branch name .请注意,每次进行新提交时, Git 都必须更新当前分支名称 Suppose you're on your master branch initially, like this:假设您最初在master分支上,如下所示:

...--F--G--H   <-- master

Now you create a new branch name, such as feature .现在您创建一个的分支名称,例如feature This new name also identifies commit H .这个新名称标识了提交H We'll add the name HEAD , in all capital letters, to one of the branch names to show which one we're using:我们将所有大写字母的名称HEAD添加到分支名称之一,以显示我们正在使用的分支名称:

...--F--G--H   <-- feature (HEAD), master

Now we'll git add some file(s) and make a new commit.现在我们将git add一些文件并进行新的提交。 It will get a new random-looking hash ID;它将获得一个新的随机外观 hash ID; we'll just call this I :我们称之为I

...--F--G--H   <-- master
            \
             I

The trick is that Git now writes I 's hash ID into the name feature —the one HEAD is attached-to—so that the name points to I now:诀窍是 Git 现在将I的 hash ID 写入名称feature (即HEAD附加到),因此名称现在指向I

...--F--G--H   <-- master
            \
             I   <-- feature (HEAD)

If we add some more files and git commit again, we get another new commit J :如果我们添加更多文件并git commit ,我们会得到另一个新的提交J

...--F--G--H   <-- master
            \
             I--J   <-- feature (HEAD)

Note that each commit has a full snapshot of every file, as it appeared in Git's index at the time you ran git commit .请注意,每个提交都有每个文件的完整快照,因为它在您运行git commit时出现在 Git 的索引中。 When you use git format-patch to turn a commit into a patch, Git:当您使用git format-patch将提交变成补丁时,Git:

  • extracts the files from commit's parent (eg, H for I );从提交的级中提取文件(例如, H代表I );
  • extracts the files from the commit ( I );从提交中提取文件( I );
  • compares the extracted files;比较提取的文件; and
  • tells you which files are different , giving you a recipe for changing the older versions into the newer ones.告诉您哪些文件不同,为您提供将旧版本更改为新版本的秘诀。

Since this is a commit-able patch, Git adds a header to it giving the name and email address of the person who made the commit, an appropriate date-and-time stamp, and the log message in which whoever made the commit explains why they made that commit.由于这是一个可提交的补丁,Git 添加了一个 header 给它,给出了名称和 email 的人的地址,为什么做出提交,一个适当的日期和时间标记的日志消息他们做出了承诺。 The patch itself comes after this header.补丁本身出现在此 header 之后。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM