git: 3bbe81a675 - main - git: add FAQ

Warner Losh <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.doc
Message-ID <[email protected]>
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/doc/commit/?id=3bbe81a6753dfcd17b49dd4e770581babd6e1f72

commit 3bbe81a6753dfcd17b49dd4e770581babd6e1f72
Author:     Warner Losh <[email protected]>
AuthorDate: 2021-03-18 21:55:16 +0000
Commit:     Warner Losh <[email protected]>
CommitDate: 2021-03-18 21:55:16 +0000

    git: add FAQ
    
    Add in the FAQ from the github docs I did for the transition.
    
    Submissions from: rpokala@, jrm@, Marc Branchaud and "mirror176".
---
 .../en/articles/committers-guide/_index.adoc       | 410 +++++++++++++++++++++
 1 file changed, 410 insertions(+)

diff --git a/documentation/content/en/articles/committers-guide/_index.adoc b/documentation/content/en/articles/committers-guide/_index.adoc
index 9069eb8f42..e4ec27cb4c 100644
--- a/documentation/content/en/articles/committers-guide/_index.adoc
+++ b/documentation/content/en/articles/committers-guide/_index.adoc
@@ -1624,6 +1624,416 @@ author recommends that your upstream github repo remain the default
 push location so that you only push things into FreeBSD you intend to
 by making it explicit.
 
+[[git-faq]]
+=== Git FAQ
+
+This section  provides a number of targeted answers to questions that are likely to come up often for users, developer and integrators.
+
+Note, we use the common convention of having the origin for the FreeBSD repo being 'freebsd' rather than the default 'origin' to allow
+people to use that for their own development and to minimize "whoopse" pushes to source of truth.
+
+==== Users
+
+===== How do I track -current and -stable with only one copy of the repo?
+
+**Q:** Although disk space is not a huge issue, it's more efficient to use
+only one copy of the repository. With SVN mirroring, I could checkout
+multiple trees from the same repo. How do I do this with Git?
+
+**A:** You can use Git worktrees. There's a number of ways to do this,
+but the simplest way is to use a clone to track -current, and a
+worktree to track stable releases. While using a 'bare repository'
+has been put forward as a way to cope, it's more complicated and will not
+be documented here.
+
+First, you need to clone the FreeBSD repository, shown here cloning into
+`freebsd-current` to reduce confusion. $URL is whatever mirror works
+best for you:
+[source,shell]
+....
+% git clone -o freebsd --config remote.freebsd.fetch='+refs/notes/*:refs/notes/*' $URL freebsd-current
+....
+then once that's cloned, you can simply create a worktree from it:
+[source,shell]
+....
+% cd freebsd-current
+% git worktree add ../freebsd-stable-12 stable/12
+....
+this will checkout `stable/12` into a directory named `freebsd-stable-12`
+that's a peer to the `freebsd-current` directory. Once created, it's updated
+very similarly to how you might expect:
+[source,shell]
+....
+% cd freebsd-current
+% git checkout main
+% git pull --ff-only
+# changes from upstream now local and current tree updated
+% cd ../freebsd-stable-12
+% git merge --ff-only freebsd/stable/12
+# now your stable/12 is up to date too
+....
+I recommend using `--ff-only` because it's safer and you avoid
+accidentally getting into a 'merge nightmare' where you have an extra
+change in your tree, forcing a complicated merge rather than a simple
+one.
+
+Here's https://adventurist.me/posts/00296 a good writeup that goes into more detail.
+
+==== Developers
+
+===== Ooops! I committed to `main` instead of a branch.
+
+**Q:** From time to time, I goof up and commit to main instead of to a
+branch. What do I do?
+
+**A:** First, don't panic.
+
+Second, don't push. In fact, you can fix almost anything if you
+haven't pushed. All the answers in this section assume no push
+has happened.
+
+The following answer assumes you committed to `main` and want to
+create a branch called `issue`:
+[source,shell]
+....
+% git branch issue                # Create the 'issue' branch
+% git reset --hard freebsd/main   # Reset 'main' back to the official tip
+% git checkout issue              # Back to where you were
+....
+
+===== Ooops! I committed something to the wrong branch!
+
+**Q:** I was working on feature on the `wilma` branch, but
+accidentally committed a change relevant to the `fred` branch
+in 'wilma'. What do I do?
+
+**A:** The answer is similar to the previous one, but with
+cherry picking. This assumes there's only one commit on wilma,
+but will generalize to more complicated situations. It also
+assumes that it's the last commit on wilma (hence using wilma
+in the `git cherry-pick` command), but that too can be generalized.
+
+[source,shell]
+....
+# We're on branch wilma
+% git checkout fred		# move to fred branch
+% git cherry-pick wilma		# copy the misplaced commit
+% git checkout wilma		# go back to wilma branch
+% git reset --hard HEAD^	# move what wilma refers to back 1 commit
+....
+Git experts would first rewind the wilma branch by 1 commit, switch over to
+fred and then use `git reflog` to see what that 1 deleted commit was and
+cherry-pick it over.
+
+**Q:** But what if I want to commit a few changes to `main`, but
+keep the rest in `wilma` for some reason?
+
+**A:** The same technique above also works if you are wanting to
+'land' parts of the branch you are working on into `main` before the
+rest of the branch is ready (say you noticed an unrelated typo, or
+fixed an incidental bug). You can cherry pick those changes into main,
+then push to the parent repo. Once you've done that, cleanup couldn't
+be simpler: just `git rebase -i`. Git will notice you've done
+this and skip the common changes automatically (even if you had to
+change the commit message or tweak the commit slightly). There's no
+need to switch back to wilma to adjust it: just rebase!
+
+**Q:** I want to split off some changes from branch `wilma` into branch `fred`
+
+**A:** The more general answer would be the same as the
+previous. You'd checkout/create the `fred` branch, cherry pick the
+changes you want from `wilma` one at a time, then rebase `wilma` to
+remove those changes you cherry picked. `git rebase -i main wilma`
+will toss you into an editor, and remove the `pick` lines that
+correspond to the commits you copied to `fred`. If all goes well,
+and there are no conflicts, you're done. If not, you'll need to
+resolve the conflicts as you go.
+
+The other way to do this would be to checkout `wilma` and then create
+the branch `fred` to point to the same point in the tree. You can then
+`git rebase -i` both these branches, selecting the changes you want in
+`fred` or `wilma` by retaining the pick likes, and deleting the rest
+from the editor. Some people would create a tag/branch called
+`pre-split` before starting in case something goes wrong in the split,
+you can undo it with the following sequence:
+[source,shell]
+....
+% git checkout pre-split	# Go back
+% git branch -D fred		# delete the fred branch
+% git checkout -B wilma		# reset the wilma branch
+% git branch -d pre-split	# Pretend it didn't happen
+....
+the last step is optional. If you are going to try again to
+split, you'd omit it.
+
+**Q:** But I did things as I read along and didn't see your advice at
+the end to create a branch, and now `fred` and `wilma` are all
+screwed up. How do I find what `wilma` was before I started. I don't
+know how many times I moved things around.
+
+**A:** All is not lost. You can figure out it, so long as it hasn't
+been too long, or too many commits (hundreds).
+
+So I created a wilma branch and committed a couple of things to it, then
+decided I wanted to split it into fred and wilma. Nothing weird
+happened when I did that, but let's say it did. The way to look at
+what you've done is with the `git reflog`:
+[source,shell]
+....
+% git reflog
+6ff9c25 (HEAD -> wilma) HEAD@{0}: rebase -i (finish): returning to refs/heads/wilma
+6ff9c25 (HEAD -> wilma) HEAD@{1}: rebase -i (start): checkout main
+869cbd3 HEAD@{2}: rebase -i (start): checkout wilma
+a6a5094 (fred) HEAD@{3}: rebase -i (finish): returning to refs/heads/fred
+a6a5094 (fred) HEAD@{4}: rebase -i (pick): Encourage contributions
+1ccd109 (freebsd/main, main) HEAD@{5}: rebase -i (start): checkout main
+869cbd3 HEAD@{6}: rebase -i (start): checkout fred
+869cbd3 HEAD@{7}: checkout: moving from wilma to fred
+869cbd3 HEAD@{8}: commit: Encourage contributions
+...
+%
+....
+
+Here we see the changes I've made. You can use it to figure out where
+things when wrong. I'll just point out a few things here. The first
+one is that HEAD@{X} is a 'commitish' thing, so you can use that as an
+argument to a command. Though if that command commits anything to the
+repo, the X numbers change. You can also use the hash (first column)
+as well.
+
+Next 'Encourage contributions' was the last commit I did to `wilma`
+before I decided to split things up. You can also see the same hash is
+there when I created the `fred` branch to do that. I started by
+rebasing `fred` and you see the 'start', each step, and the 'finish'
+for that process. While we don't need it here, you can figure out
+exactly what happened.  Fortunately, to fix this, you can follow the
+prior answer's steps, but with the hash `869cbd3` instead of
+`pre-split`. While that set of a bit verbose, it's easy to remember
+since you're doing one thing at a time. You can also stack:
+[source,shell]
+....
+% git checkout -B wilma 869cbd3
+% git branch -D fred
+....
+and you are ready to try again. The 'checkout -B' with the hash combines
+checking out and creating a branch for it. The -B instead of -b forces
+the movement of a pre-existing branch. Either way works, which is what's
+great (and awful) about Git. One reason I tend to use `git checkout -B xxxx hash`
+instead of checking out the hash, and then creating / moving the branch
+is purely to avoid the slightly distressing message about detached heads:
+[source,shell]
+....
+% git checkout 869cbd3
+M	faq.md
+Note: checking out '869cbd3'.
+
+You are in 'detached HEAD' state. You can look around, make experimental
+changes and commit them, and you can discard any commits you make in this
+state without impacting any branches by performing another checkout.
+
+If you want to create a new branch to retain commits you create, you may
+do so (now or later) by using -b with the checkout command again. Example:
+
+  git checkout -b <new-branch-name>
+
+HEAD is now at 869cbd3 Encourage contributions
+% git checkout -B wilma
+....
+this produces the same effect, but I have to read a lot more and severed heads
+aren't an image I like to contemplate.
+
+===== Ooops! I did a 'git pull' and it created a merge commit, what do I do?
+
+**Q:** I was on autopilot and did a 'git pull' for my development tree and
+that created a merge commit on the mainline. How do I recover?
+
+**A:** This can happen when you invoke the pull with your development branch
+checked out.
+
+Right after the pull, you will have the new merge commit checked out.  Git
+supports a `HEAD^#` syntax to examine the parents of a merge commit:
+[source,shell]
+....
+git log --oneline HEAD^1   # Look at the first parent's commits
+git log --oneline HEAD^2   # Look at the second parent's commits
+....
+From those logs, you can easily identify which commit is your development
+work.  Then you simply reset your branch to the corresponding `HEAD^#`:
+[source,shell]
+....
+git reset --hard HEAD^2
+....
+
+**Q:** But I also need to fix my 'main' branch. How do I do that?
+
+**A:** Git keeps track of the remote repository branches in a `freebsd/`
+namespace.  To fix your 'main' branch, just make it point to the remote's
+'main':
+[source,shell]
+....
+git branch -f main freebsd/main
+....
+There's nothing magical about branches in git: they are just labels on a DAG
+that are automatically moved forward by making commits.  So the above works
+because you're just moving a label. There's no metadata about the branch
+that needs to be preserved due to this.
+
+===== Mixing and matching branches
+
+**Q:** So I have two branches `worker` and `async` that I'd like to combine into one branch called `feature`
+while maintaining the commits in both.
+
+**A:** This is a job for cherry pick.
+
+[source,shell]
+....
+% git checkout worker
+% git checkout -b feature	# create a new branch
+% git cherry-pick main..async	# bring in the changes
+....
+You now have one branch called `feature`. This branch combines commits
+from both branches. You can further curate it with `git rebase`.
+
+**Q:** OK Wise Guy. That was too easy. I have a branch called `driver` and I'd like
+to break it up into `kernel` and `userland` so I can evolve them separately and commit
+each branch as it becomes ready.
+
+**A:** This takes a little bit of prep work, but `git rebase` will do the heavy
+lifting here.
+
+[source,shell]
+....
+% git checkout driver		# Checkout the driver
+% git checkout -b kernel	# Create kernel branch
+% git checkout -b userland	# Create userland branch
+....
+Now you have two identical branches. So, it's time to separate out the commits.
+We'll assume first that all the commits in `driver` go into either the `kernel`
+or the `userland` branch, but not both.
+
+[source,shell]
+....
+% git rebase -i main kernel
+....
+and just include the changes you want (with a 'p' or 'pick' line) and
+just delete the commits you don't (this sounds scary, but if worse
+comes to worse, you can throw this all away and start over with the
+`driver` branch since you've not yet moved it).
+
+[source,shell]
+....
+% git rebase -i main userland
+....
+and do the same thing you did with the `kernel` branch.
+
+**Q:** Oh great! I followed the above and forgot a commit in the `kernel` branch.
+How do I recover?
+
+**A:** You can use the `driver` branch to find the hash of the commit is missing and
+cherry pick it.
+[source,shell]
+....
+% git checkout kernel
+% git log driver
+% git cherry-pick $HASH
+....
+
+**Q:** OK. I have the same situation as the above, but my commits are all mixed up. I need
+parts of one commit to go to one branch and the rest to go to the other. In fact, I have
+several. Your rebase method to select sounds tricky.
+
+**A:** In this situation, you'd be better off to curate the original branch to separate
+out the commits, and then use the above method to split the branch.
+
+So let's assume that there's just one commit with a clean tree. You
+can either use `git rebase` with an `edit` line, or you can use this
+with the commit on the tip. The steps are the same either way. The
+first thing we need to do is to back up one commit while leaving the
+changes uncommitted in the tree:
+[source,shell]
+....
+% git reset HEAD^
+....
+Note: Do not, repeat do not, add `--hard` here since that also removes the changes from your tree.
+
+Now, if you are lucky, the change needing to be split up falls entirely along file lines. In that
+case you can just do the usual `git add` for the files in each group than do a `git commit`. Note:
+when you do this, you'll lose the commit message when you do the reset, so if you need it for
+some reason, you should save a copy (though `git log $HASH` can recover it).
+
+If you are not lucky, you'll need to split apart files. There's another tool to do that which you
+can apply one file at a time.
+[source,shell]
+....
+git add -i foo/bar.c
+....
+will step through the diffs, prompting you, one at time, whether to include or exclude the hunk.
+Once you're done, `git commit` and you'll have the remainder in your tree. You can run it
+multiple times as well, and even over multiple files (though I find it easier to do one file at a time
+and use the `git rebase -i` to fold the related commits together).
+
+==== Cloning and Mirroring
+
+**Q:** I'd like to mirror the entire git repo, how do I do that?
+
+**A:** If all you want to do is mirror, then
+[source,shell]
+....
+% git clone --mirror $URL
+....
+will do the trick. However, there are two disadvantages to this if you
+want to use it for anything other than a mirror you'll reclone.
+
+First, this is a 'bare repo' which has the repository database, but no
+checked out worktree. This is great for mirroring, but terrible for day to
+day work. There's a number of ways around this with 'git worktree':
+[source,shell]
+....
+% git clone --mirror https://cgit-beta.freebsd.org/ports.git ports.git
+% cd ports.git
+% git worktree add ../ports main
+% git worktree add ../quarterly branches/2020Q4
+% cd ../ports
+....
+But if you aren't using your mirror for further local clones, then it's a poor match.
+
+The second disadvantage is that Git normally rewrites the refs (branch name, tags, etc)
+from upstream so that your local refs can evolve independently of
+upstream. This means that you'll lose changes if you are committing to
+this repo on anything other than private project branches.
+
+**Q:** So what can I do instead?
+
+**A:** Well, you can stuff all of the upstream repo's refs into a private
+namespace in your local repo. Git clones everything via a 'refspec' and
+the default refspec is:
+[source,shell]
+....
+        fetch = +refs/heads/*:refs/remotes/freebsd/*
+....
+which says just fetch the branch refs.
+
+However, the FreeBSD repo has a number of other things in it. To see
+those, you can add explicit refspecs for each ref namespace, or you
+can fetch everything. To setup your repo to do that:
+[source,shell]
+....
+git config --add remote.freebsd.fetch '+refs/*:refs/freebsd/*'
+....
+which will put everything in the upstream repo into your local repo's
+'res/freebsd/' namespace. Please note, that this
+also grabs all the unconverted vendor branches and the number of refs
+associated with them is quite large.
+
+You'll need to refer to these 'refs' with their full name because they
+aren't in and of Git's regular namespaces.
+[source,shell]
+....
+git log refs/freebsd/vendor/zlib/1.2.10
+....
+would look at the log for the vendor branch for zlib starting at 1.2.10.
+
+
 [[subversion-primer]]
 == Subversion Primer
 
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-doc-all
To unsubscribe, send any mail to "[email protected]"
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.