From 5180786782f33397db552c7ad8d0e1fd32834911 Mon Sep 17 00:00:00 2001 From: Ben Lynn Date: Thu, 6 Nov 2008 17:59:51 -0800 Subject: [PATCH] Spun off some sections into a new chapter on history operations. Added filter-branch section. --- Makefile | 2 +- branch.txt | 2 +- drawbacks.txt | 6 +-- grandmaster.txt | 113 ++--------------------------------------------- history.txt | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ intro.txt | 2 +- 6 files changed, 143 insertions(+), 116 deletions(-) create mode 100644 history.txt diff --git a/Makefile b/Makefile index 7b6114d..9b304ab 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ target: book book/default.css book.html book.pdf -TXTFILES=preface.txt intro.txt basic.txt clone.txt branch.txt grandmaster.txt secrets.txt drawbacks.txt +TXTFILES=preface.txt intro.txt basic.txt clone.txt branch.txt history.txt grandmaster.txt secrets.txt drawbacks.txt book.xml: $(TXTFILES) ( for FILE in $^ ; do cat $$FILE ; echo ; done ) | asciidoc -d book -b docbook - > $@ diff --git a/branch.txt b/branch.txt index 1151cbb..b8f8abf 100644 --- a/branch.txt +++ b/branch.txt @@ -170,7 +170,7 @@ You can have multiple stashes, and manipulate them in various ways. See Applications such as http://www.mozilla.com/[Mozilla Firefox] allow you to open multiple tabs and multiple windows. Switching tabs gives you different content in the same window. Git branching is like tabs for your working directory. Continuing this analogy, Git cloning is like opening a new window. Being able to do both easily makes for a better user experience. -On a higher level, several Linux window managers allow you to have multiple desktops, which means you can instantly view a different state of the desktop. This is similar to branching in Git, whereas Git cloning would be like attaching another monitor where you can open more windows. +On a higher level, several Linux window managers allow you to have multiple desktops: you can instantly switch to a different desktop. This is similar to branching in Git, whereas Git cloning would be like attaching another monitor to gain another desktop. Yet another example is the http://www.gnu.org/software/screen/[*screen*] utility. This gem allows you to create, destroy and switch between multiple terminal sessions in the same terminal. Instead of opening new terminals (clone), you can use the same one if you run *screen* (branch). In fact, you can do a lot more with *screen* but that's a topic for another text. diff --git a/drawbacks.txt b/drawbacks.txt index 52ade90..d8f8851 100644 --- a/drawbacks.txt +++ b/drawbacks.txt @@ -20,11 +20,11 @@ A solution is to break up your project into pieces, each consisting of related f === Who's Editing What? === -Some version control systems force you to explicitly tag a file before editing. While this is especially annoying when this involves talking to a central server, it does have two benefits: +Some version control systems force you to explicitly mark a file in some way before editing. While this is especially annoying when this involves talking to a central server, it does have two benefits: - 1. Diffs are quick because only the tagged files need be examined. + 1. Diffs are quick because only the marked files need be examined. - 2. When a central server stores the tags, one can discover who else is working on the file. + 2. One can discover who else is working on the file by asking the central server who has marked it for editing. With appropriate scripting, you can achieve the same with Git. This requires cooperation from the programmer, who should execute particular scripts when editing a file. diff --git a/grandmaster.txt b/grandmaster.txt index c9d53f1..f1f691d 100644 --- a/grandmaster.txt +++ b/grandmaster.txt @@ -50,63 +50,6 @@ You can perform the above in a single pass with: The *-z* and *-0* options prevent ill side-effects from filenames containing strange characters. Note this command adds ignored files. You may want to use the `-x` or `-X` option. -=== I Stand Corrected === - -Did you just commit, but wish you had typed a different message? Realized you forgot to add a file? Then: - - $ git commit --amend - -can help you out. - -Since this changes the history, only do this if you have yet to push your changes, otherwise your tree will diverge from other trees. Of course, if you control all the other trees too, then there is no problem since you can overwrite them. - -=== ... And Then Some === - -Let's suppose the previous problem is ten times worse. After a lengthy session you've made a bunch of commits. But you're not quite happy with the way they're organized, and some of those commit messages could use rewording. This is quite likely if you've been saving early and saving often. Then type - - $ git rebase -i HEAD~10 - -and the last 10 commits will appear in your favourite $EDITOR. A sample excerpt: - - pick 5c6eb73 Added repo.or.cz link - pick a311a64 Reordered analogies in "Work How You Want" - pick 100834f Added push target to Makefile - -Then: - -- Remove commits by deleting lines. -- Reorder commits by reordering lines. -- Replace "pick" with "edit" to mark a commit for amending. -- Replace "pick" with "squash" to merge a commit with the previous one. - -Next run *git commit \--amend* if you marked a commit for editing. Otherwise, run: - - $ git rebase --continue - -Thus tampering with the past is easily accomplished with the rebase command. -But take care: only rewrite that part of history which you alone possess. Just -as nations forever argue on who committed what atrocity, if someone else has a -clone whose version of history differs to yours, you will have trouble -reconciling when your trees interact. - -Some developers strongly feel history should be immutable, warts and all. -What's done is done. Others feel trees should be made presentable before -they are unleashed in public. Git accommodates both viewpoints. Like cloning, -branching and merging, rewriting history is fast and local, and how you want to -use this power is up to you. - -=== Local Changes Last === - -You're working on an active project. You make some local commits over time, and -then you sync with the official tree with a merge. This cycle repeats itself a few times before you're ready to push to the central tree. - -But now the history in your local Git clone is a messy jumble of your changes and the official changes. You'd prefer to see all your changes in one contiguous section, and after all the official changes. - -This is a job for *git rebase* as described above. In many cases you can use -the *\--onto* flag and avoid interaction. - -Also see the manpage for other amazing uses of this command, which really deserves a chapter of its own. You can split commits. You can even rearrange branches of a tree! - === My Commit Is Too Big! === Have you neglected to commit for too long? Been coding furiously and forgotten @@ -132,7 +75,7 @@ What if you've edited many files in many places? Reviewing each change one by one becomes frustratingly mind-numbing. In this case, use *git add -i*, whose interface is less straightforward, but more flexible. With a few keystrokes, you can stage or unstage several files at a time, or review and select changes -in particular files only. Alternatively, run *git commit --interactive* which +in particular files only. Alternatively, run *git commit \--interactive* which automatically runs commits after you're done. === Don't Lose Your HEAD === @@ -171,62 +114,12 @@ and see its manpage for more information. Eventually, you may want to run *git gc \--prune* to recover space. Be aware that doing so prevents you from recovering lost HEADs. -=== Making History === - -Want to migrate a project to Git? If it's managed with one of the more well-known systems, then chances are someone has already written a script to export the whole history to Git. - -Otherwise, take a look at *git fast-import*. This command takes text input in a specific format and creates Git history from scratch. Typically a script is cobbled together and run once to feed this command, migrating the project in a single shot. - -As an example, paste the following listing into temporary file, such as `/tmp/history`: ----------------------------------- -commit refs/heads/master -committer Alice Thu, 01 Jan 1970 00:00:00 +0000 -data < - -int main() { - printf("Hello, world!\n"); - return 0; -} -EOT - - -commit refs/heads/master -committer Bob Tue, 14 Mar 2000 01:59:26 -0800 -data < - -int main() { - write(1, "Hello, world!\n", 14); - return 0; -} -EOT - ----------------------------------- - -Then create a Git repository from this temporary file by typing: - - $ mkdir project; cd project; git init - $ git fast-import < /tmp/history - -You can checkout the latest version of the project with: - - $ git checkout master . - === Building On Git === In true UNIX fashion, Git's design allows it to be easily used as a low-level component of other programs. There are GUI interfaces, web interfaces, alternative command-line interfaces, and perhaps soon you will have a script or two of your own that calls Git. +I like browsing history with http://sourceforge.net/projects/qgit[qgit], due to its slick photogenic interface, and http://jonas.nitro.dk/tig/[tig], a text-mode interface that works well over slow connections. + One easy trick is to use built-in git aliases shorten your most frequently used commands: diff --git a/history.txt b/history.txt new file mode 100644 index 0000000..daeba0e --- /dev/null +++ b/history.txt @@ -0,0 +1,134 @@ +== Lessons of History == + +A consequence of the distributed nature of Git is that history can be edited +easily. But if you tamper with the past, take care: +only rewrite that part of history which you alone possess. Just as nations +forever argue on who committed what atrocity, if someone else has a clone whose +version of history differs to yours, you will have trouble reconciling when +your trees interact. + +Of course, if you control all the other trees too, then there is no problem +since you can overwrite them. + +Some developers strongly feel history should be immutable, warts and all. +Others feel trees should be made presentable before they are unleashed in +public. Git accommodates both viewpoints. Like cloning, branching and merging, +rewriting history is simply another power Git gives you, and it is up to you +to decide how to use it. + +=== I Stand Corrected === + +Did you just commit, but wish you had typed a different message? Then run: + + $ git commit --amend + +to change the last message. Realized you forgot to add a file? Run *git add* to +add it, and then run the above command. + +Want to include a few more edits in that last commit? Then make those edits and run: + + $ git commit --amend -a + +=== ... And Then Some === + +Let's suppose the previous problem is ten times worse. After a lengthy session you've made a bunch of commits. But you're not quite happy with the way they're organized, and some of those commit messages could use rewording. This is quite likely if you've been saving early and saving often. Then type + + $ git rebase -i HEAD~10 + +and the last 10 commits will appear in your favourite $EDITOR. A sample excerpt: + + pick 5c6eb73 Added repo.or.cz link + pick a311a64 Reordered analogies in "Work How You Want" + pick 100834f Added push target to Makefile + +Then: + +- Remove commits by deleting lines. +- Reorder commits by reordering lines. +- Replace "pick" with "edit" to mark a commit for amending. +- Replace "pick" with "squash" to merge a commit with the previous one. + +Next run *git commit \--amend* if you marked a commit for editing. Otherwise, run: + + $ git rebase --continue + +=== Local Changes Last === + +You're working on an active project. You make some local commits over time, and +then you sync with the official tree with a merge. This cycle repeats itself a few times before you're ready to push to the central tree. + +But now the history in your local Git clone is a messy jumble of your changes and the official changes. You'd prefer to see all your changes in one contiguous section, and after all the official changes. + +This is a job for *git rebase* as described above. In many cases you can use +the *\--onto* flag and avoid interaction. + +Also see *git help rebase* for detailed examples of this amazing command. You can split commits. You can even rearrange branches of a tree. + +=== Rewriting History === + +Occasionally, there are situations that call for the equivalent of the +Stalinesque practice of airbrushing people out of official photos, erasing them +from history. For example, suppose we intend to release a project, but it +involves a file that should be kept private for some reason. Perhaps I left my +credit card number in a text file and accidentally added it to the project. +Deleting the file is insufficient, for the file can be accessed from older +commits. We must remove the file from all commits: + + $ git filter-branch --tree-filter 'rm top/secret/file' HEAD + +See *git help filter-branch*, which discusses this example and gives a faster +method. In general, *filter-branch* lets you alter large sections of history with a single command. + +Afterwards, you must replace all clones of your project with your revised version if you wish to interact with them later. + +=== Making History === + +Want to migrate a project to Git? If it's managed with one of the more well-known systems, then chances are someone has already written a script to export the whole history to Git. + +Otherwise, take a look at *git fast-import*. This command takes text input in a specific format and creates Git history from scratch. Typically a script is cobbled together and run once to feed this command, migrating the project in a single shot. + +As an example, paste the following listing into temporary file, such as `/tmp/history`: +---------------------------------- +commit refs/heads/master +committer Alice Thu, 01 Jan 1970 00:00:00 +0000 +data < + +int main() { + printf("Hello, world!\n"); + return 0; +} +EOT + + +commit refs/heads/master +committer Bob Tue, 14 Mar 2000 01:59:26 -0800 +data < + +int main() { + write(1, "Hello, world!\n", 14); + return 0; +} +EOT + +---------------------------------- + +Then create a Git repository from this temporary file by typing: + + $ mkdir project; cd project; git init + $ git fast-import < /tmp/history + +You can checkout the latest version of the project with: + + $ git checkout master . diff --git a/intro.txt b/intro.txt index 8fcb184..5484069 100644 --- a/intro.txt +++ b/intro.txt @@ -46,7 +46,7 @@ A small project may only need a fraction of the features offered by such a syste === Merge Conflicts === -For this topic, our computer game analogy becomes too thinly stretched since most games never worry about this, so let us consider the case of editing a document. +For this topic, our computer game analogy becomes too thinly stretched since most games never worry about this. Instead, let us again consider the case of editing a document. Suppose Alice has inserted a line at the beginning of a file, and Bob appends one at the end. They both upload their changes. Most systems will automatically deduce a reasonable course of action: accept and merge their changes, so both Alice's and Bob's edits are applied. -- 2.11.4.GIT