Git Training NotesRussell Bateman |
Here are my personal notes and console scrapes. The index at the right is for my convenience. Your mileage displaying it may vary depending on your host OS and browser. I'm running Ubuntu Maverick, Firefox 3.6.12 and I have installed the Candara font, the main font I use for all my technical articles. It also seems to work well on Google Chrome.
Illustration of a view of Git workflow for use in comments here...
Git supports continuous integration especially well.
.gitconfig does not contain username and e-mail for any authentification purposes. They're just for indicating at commit time who dun it. This files is "telescopic" in that it can be found in (precedence from highest to lowest):
Set up for ssh and/or Git to use a special key pair. Provide GIT_SSH to name the path to a special script to handle this. Here's the script:
#!/bin/sh exec ssh -i ~/.ssh/github_rsa "$@"
export GIT_SSH="~/.ssh/mygitssh.sh"
Git is probably the best version-control system for Agile development because of its superb life-cycle (or workflow). Git projects tend to be smaller and, for large-scale software development, more modular.
Three-stage thinking: (working, staging, repo). Repeat the following:
"Add to the staging area."
"Commit to the repo[sitory]."
Unlike Subversion's .svn, .git doesn't reside in any but the root of the project. This means you have nothing to worry about as you copy subdirectory contents to and fro. This subdirectory is full of metadata as seen during the second half of the course.
"Untracked" means Git is not in any way doing anything with the file or subdirectory.
The course was essentially practical and hands-on. What was learned must be intuited from what went on on the console.
russ@tuonela:~/.ssh> ssh-keygen -t rsa -C"GitHub Training Course" Generating public/private rsa key pair. Enter file in which to save the key (/home/russ/.ssh/id_rsa): github_rsa Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in github_rsa. Your public key has been saved in github_rsa.pub. The key fingerprint is: 95:84:c8:c5:06:21:6b:e7:5b:1e:ae:4a:b3:92:8d:be GitHub Training Course The key's randomart image is: +--[ RSA 2048]----+ | ..o*... | | oo +. . | | o .. o | | . o . | | . S | | = . | | +o . o | | +..o . | | .Eoo.. | +-----------------+ russ@tuonela:~/.ssh> ll total 39 drwx------ 2 russ russ 312 2011-08-16 09:49 . drwxr-xr-x 78 russ russ 3304 2011-08-16 09:36 .. -rw------- 1 russ russ 1679 2011-08-16 09:49 github_rsa -rw-r--r-- 1 russ russ 418 2011-08-16 09:49 github_rsa.pub -rw------- 1 russ russ 1679 2011-05-04 10:01 id_rsa -rw-r--r-- 1 russ russ 403 2011-05-04 10:01 id_rsa.pub -rw-r--r-- 1 russ russ 403 2011-05-04 10:56 id_rsa.pub.russ.tuonela -rw-r--r-- 1 russ russ 3486 2011-08-08 10:27 known_hosts -rw-r--r-- 1 russ russ 7784 2011-05-06 11:18 known_hosts.bak -rw-r--r-- 1 russ russ 403 2011-08-09 08:39 [email protected]
I have to be able to copy the key into a browser buffer.
russ@tuonela:~/git> cat ../.ssh/github_rsa.pub ssh-rsa AAAAB3NzaC1yc2EA...CiU6+7zE+wdUCH GitHub Training Course
Now, after signing up with GitHub, I can try out hitting it via ssh...
russ@tuonela:~/git> ssh -v [email protected] <mailto:[email protected]> OpenSSH_5.5p1 Debian-4ubuntu6, OpenSSL 0.9.8o 01 Jun 2010 debug1: Reading configuration data /etc/ssh/ssh_config debug1: Applying options for * debug1: Connecting to github.com <http://github.com> [207.97.227.239] port 22. debug1: Connection established. debug1: identity file /home/russ/.ssh/id_rsa type 1 debug1: Checking blacklist file /usr/share/ssh/blacklist.RSA-2048 debug1: Checking blacklist file /etc/ssh/blacklist.RSA-2048 debug1: identity file /home/russ/.ssh/id_rsa-cert type -1 debug1: identity file /home/russ/.ssh/id_dsa type -1 debug1: identity file /home/russ/.ssh/id_dsa-cert type -1 debug1: Remote protocol version 2.0, remote software version OpenSSH_5.1p1 Debian-5github2 debug1: match: OpenSSH_5.1p1 Debian-5github2 pat OpenSSH* debug1: Enabling compatibility mode for protocol 2.0 debug1: Local version string SSH-2.0-OpenSSH_5.5p1 Debian-4ubuntu6 debug1: SSH2_MSG_KEXINIT sent debug1: SSH2_MSG_KEXINIT received debug1: kex: server->client aes128-ctr hmac-md5 none debug1: kex: client->server aes128-ctr hmac-md5 none debug1: SSH2_MSG_KEX_DH_GEX_REQUEST(1024<1024<8192) sent debug1: expecting SSH2_MSG_KEX_DH_GEX_GROUP debug1: SSH2_MSG_KEX_DH_GEX_INIT sent debug1: expecting SSH2_MSG_KEX_DH_GEX_REPLY debug1: Host 'github.com <http://github.com>' is known and matches the RSA host key. debug1: Found key in /home/russ/.ssh/known_hosts:8 debug1: ssh_rsa_verify: signature correct debug1: SSH2_MSG_NEWKEYS sent debug1: expecting SSH2_MSG_NEWKEYS debug1: SSH2_MSG_NEWKEYS received debug1: Roaming not allowed by server debug1: SSH2_MSG_SERVICE_REQUEST sent debug1: SSH2_MSG_SERVICE_ACCEPT received debug1: Authentications that can continue: publickey debug1: Next authentication method: publickey debug1: Offering public key: /home/russ/.ssh/id_rsa debug1: Authentications that can continue: publickey debug1: Offering public key: GitHub Training Course debug1: Remote: Forced command: gerve russellbateman debug1: Remote: Port forwarding disabled. debug1: Remote: X11 forwarding disabled. debug1: Remote: Agent forwarding disabled. debug1: Remote: Pty allocation disabled. debug1: Server accepts key: pkalg ssh-rsa blen 279 debug1: Remote: Forced command: gerve russellbateman debug1: Remote: Port forwarding disabled. debug1: Remote: X11 forwarding disabled. debug1: Remote: Agent forwarding disabled. debug1: Remote: Pty allocation disabled. debug1: Authentication succeeded (publickey). debug1: channel 0: new [client-session] debug1: Requesting [email protected] <mailto:[email protected]> debug1: Entering interactive session. debug1: Sending environment. debug1: Sending env LANG = en_US.utf8 PTY allocation request failed on channel 0 debug1: client_input_channel_req: channel 0 rtype exit-status reply 0 debug1: client_input_channel_req: channel 0 rtype [email protected] <mailto:[email protected]> reply 0 Hi russellbateman! You've successfully authenticated, but GitHub does not provide shell access. debug1: channel 0: free: client-session, nchannels 1 Connection to github.com <http://github.com> closed. - Show quoted text - Transferred: sent 3000, received 2936 bytes, in 0.2 seconds Bytes per second: sent 18246.3, received 17857.0 debug1: Exit status 1
This didn't work, of course, but it does get me far enough to learn that all is well and I can hit it using GitHub's accepted protocol (HTTPS).
Here's how to create a local project and do some rudimentary Git operations in it.
russ@tuonela:~/git> git init project1 Initialized empty Git repository in /home/russ/git/project1/.git/ russ@tuonela:~/git> cd project1/ russ@tuonela:~/git/project1> ll total 0 drwxr-xr-x 3 russ russ 72 2011-08-16 10:23 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. drwxr-xr-x 7 russ russ 248 2011-08-16 10:23 .git russ@tuonela:~/git/project1> git status # On branch master # # Initial commit # nothing to commit (create/copy files and use "git add" to track)
Here's our first file to check in. This is called "staging" in Git and it uses the command git add. We'll add it. This moves the file from the working to the staged state. The notion of "add" is overloaded in Git:
Watch out for the second above. It will stage everything in the current subdirectory and all subdirectories below that. Ensure this is what you want. Remember, there are files (temporary editor and other files) that you probably don't want staged.
We'll commit the new file as well. This moves it from the (merely) staged state into the repository where it is saved. Unless you enter a message, there will be no commit. The brief status is what's retained using the -m option (very quick) or what's on the first line of the file if you don't use this option (and an editory is launched instead).
We use git status, our favorite command, to see what's happening along the way.
russ@tuonela:~/git/project1> vim first.html russ@tuonela:~/git/project1> git status # On branch master # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # first.html nothing added to commit but untracked files present (use "git add" to track) russ@tuonela:~/git/project1> git add first.html russ@tuonela:~/git/project1> git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: first.html # russ@tuonela:~/git/project1> git commit -m"My first commit" [master (root-commit) c5e7bd0] My first commit 1 files changed, 12 insertions(+), 0 deletions(-) create mode 100644 first.html russ@tuonela:~/git/project1> git status # On branch master nothing to commit (working directory clean)
Habit: the goal when working in Git is to keep git status saying "working directory clean." Commit frequently. Remember the model of local - cached/remote - upstream as illustrated above. Even if the code doesn't compile, doesn't make sense, etc., it's your code on the way to being written and it's your repository where it will be safely kept. Later, we'll learn how this is cheaply accomplished (especially as compared to other version-control systems).
russ@tuonela:~/git/project1> git log commit c5e7bd058c3fcb84b0027006956c1bf11fa87875 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 10:31:07 2011 -0600 My first commit russ@tuonela:~/git/project1> vim first.html russ@tuonela:~/git/project1> git add first.html russ@tuonela:~/git/project1> git commit [master 5a27632] Here I'm prompted for the message instead of passing it on the command line using the -m option. 1 files changed, 1 insertions(+), 1 deletions(-) russ@tuonela:~/git/project1> git log --stat commit 5a276328b7946554616344da968964caff1c3a6a Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 10:41:11 2011 -0600 Here I'm prompted for the message instead of passing it on the command line using the -m option. first.html | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commit c5e7bd058c3fcb84b0027006956c1bf11fa87875 Author: Russell Bateman <[email protected] <mailto:[email protected]>> - Show quoted text - Date: Tue Aug 16 10:31:07 2011 -0600 My first commit first.html | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-) russ@tuonela:~/git/project1> vim first.html russ@tuonela:~/git/project1> git add first.html russ@tuonela:~/git/project1> git commit Aborting commit due to empty commit message. russ@tuonela:~/git/project1> cat first.html <html> <head> <!-- Head stuff that I want to change. --> </head> <body> <p> Body stuff. </p> <p> Another paragraph. </p> </body> </html> russ@tuonela:~/git/project1> git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: first.html #
Let's create a new subdirectory and put something in it. Notice how Git:
git diff shows differences between the working copy and what's staged. Add the HEAD argument and it's between the working copy and what's committed to the branch whose HEAD is current. (More on this later.)
russ@tuonela:~/git/project1> mkdir subfolder russ@tuonela:~/git/project1> cp first.html subfolder/second.html russ@tuonela:~/git/project1> cd subfolder/ russ@tuonela:~/git/project1/subfolder> git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: ../first.html # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # ./ russ@tuonela:~/git/project1/subfolder> git status -u # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: ../first.html # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # second.html russ@tuonela:~/git/project1/subfolder> git add . russ@tuonela:~/git/project1/subfolder> git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: ../first.html # new file: second.html # russ@tuonela:~/git/project1/subfolder> git commit Aborting commit due to empty commit message. russ@tuonela:~/git/project1/subfolder> ll total 4 drwxr-xr-x 2 russ russ 80 2011-08-16 10:48 . drwxr-xr-x 4 russ russ 136 2011-08-16 10:47 .. -rw-r--r-- 1 russ russ 140 2011-08-16 10:48 second.html russ@tuonela:~/git/project1/subfolder> git commit [master c6a7f28] This is my fourth commit. 2 files changed, 20 insertions(+), 0 deletions(-) create mode 100644 subfolder/second.html russ@tuonela:~/git/project1/subfolder> git status -u # On branch master nothing to commit (working directory clean) russ@tuonela:~/git/project1/subfolder> git status # On branch master nothing to commit (working directory clean) russ@tuonela:~/git/project1/subfolder> vim second.html russ@tuonela:~/git/project1/subfolder> git status # On branch master # Changed but not updated: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: second.html # no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1/subfolder> git diff diff --git a/subfolder/second.html b/subfolder/second.html index c4f22a1..3171d1b 100644 --- a/subfolder/second.html +++ b/subfolder/second.html @@ -5,11 +5,11 @@ <body> <p> -Body stuff. +Body stuff. (second file) </p> <p> -Another paragraph. +Another paragraph. (second file) </p> </body> russ@tuonela:~/git/project1/subfolder> git add second.html
git diff shows what's changed since the last commit.
git diff HEAD shows what's changed respective to HEAD.
git log --diff-filter={A,M,D} where A is for added, M is for modified and D for deleted. What you filter is what you see (and not the other sorts of differences).
russ@tuonela:~/git/project1/subfolder> git diff russ@tuonela:~/git/project1/subfolder> git diff HEAD diff --git a/subfolder/second.html b/subfolder/second.html index c4f22a1..22aeb43 100644 --- a/subfolder/second.html +++ b/subfolder/second.html @@ -5,11 +5,11 @@ <body> <p> -Body stuff. +Body . (second file) </p> <p> -Another paragraph. +Another paragraph. (second file) </p> </body> russ@tuonela:~/git/project1/subfolder> git commit -a [master 6c65892] All the changes made. 1 files changed, 2 insertions(+), 2 deletions(-) russ@tuonela:~/git/project1/subfolder> git status # On branch master nothing to commit (working directory clean) russ@tuonela:~/git/project1/subfolder> git log -2 commit 6c65892602b287f22691034f8aa8769afdeb3cb5 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 10:59:21 2011 -0600 All the changes made. commit c6a7f289d8b0cb767f8247365e412c9f3b76bfd7 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 10:52:34 2011 -0600 This is my fourth commit. russ@tuonela:~/git/project1/subfolder> git log -1 commit 6c65892602b287f22691034f8aa8769afdeb3cb5 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 10:59:21 2011 -0600 All the changes made. russ@tuonela:~/git/project1/subfolder> git log -1 -p -w commit 6c65892602b287f22691034f8aa8769afdeb3cb5 Author: Russell Bateman <[email protected] <mailto:[email protected]>> - Show quoted text - Date: Tue Aug 16 10:59:21 2011 -0600 All the changes made. diff --git a/subfolder/second.html b/subfolder/second.html index c4f22a1..22aeb43 100644 --- a/subfolder/second.html +++ b/subfolder/second.html @@ -5,11 +5,11 @@ <body> <p> -Body stuff. +Body . (second file) </p> <p> -Another paragraph. +Another paragraph. (second file) </p> </body>
Create some more files and subdirectories. We're going to examine the behavior of .gitignore. "!<file-or-subdirectory>" means "don't ignore" this.
There is a global (machine-wide) .gitignore, probably more properly referred to as system, that is not "turned on" by default. It's a little pernicious, but useful in specifying the sort of temporary/uncommittable files that a specific OS environment might tend to have.
russ@tuonela:~/git/project1/subfolder> echo JUNK > build.log russ@tuonela:~/git/project1/subfolder> cd .. russ@tuonela:~/git/project1> echo JUNK > >build2.tmp russ@tuonela:~/git/project1> echo JUNK > >build2.log russ@tuonela:~/git/project1> git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # build2.log # build2.tmp # subfolder/build.log nothing added to commit but untracked files present (use "git add" to track) russ@tuonela:~/git/project1> mkdir target output russ@tuonela:~/git/project1> cp build* target russ@tuonela:~/git/project1> cp build* output russ@tuonela:~/git/project1> tree . |-- build2.log |-- build2.tmp |-- first.html |-- output | |-- build2.log | `-- build2.tmp |-- subfolder | |-- build.log | `-- second.html `-- target |-- build2.log `-- build2.tmp 3 directories, 9 files russ@tuonela:~/git/project1> vim .gitignore *.log *.tmp junk.txt !special.log russ@tuonela:~/git/project1> git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .gitignore nothing added to commit but untracked files present (use "git add" to track) russ@tuonela:~/git/project1> echo GOODSTUFF > special.log russ@tuonela:~/git/project1> mv special.log target russ@tuonela:~/git/project1> git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .gitignore nothing added to commit but untracked files present (use "git add" to track) russ@tuonela:~/git/project1> cd target russ@tuonela:~/git/project1/target> gvim .gitignore Xlib: extension "RANDR" missing on display ":0.0". russ@tuonela:~/git/project1/target> vim .gitignore russ@tuonela:~/git/project1/target> git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # ../.gitignore # ./ nothing added to commit but untracked files present (use "git add" to track) russ@tuonela:~/git/project1/target> git status -u # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # ../.gitignore # .gitignore # special.log nothing added to commit but untracked files present (use "git add" to track) russ@tuonela:~/git/project1/target> git add .gitignore russ@tuonela:~/git/project1/target> git commit -m"Making an exception for special.log" [master 43fb8a0] Making an exception for special.log 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 target/.gitignore russ@tuonela:~/git/project1/target> git add special.log russ@tuonela:~/git/project1/target> git commit -m"Tracking contents of special.lot" [master a66006a] Tracking contents of special.lot 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 target/special.log russ@tuonela:~/git/project1/target> git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # ../.gitignore nothing added to commit but untracked files present (use "git add" to track) russ@tuonela:~/git/project1/target> cd .. russ@tuonela:~/git/project1> git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .gitignore nothing added to commit but untracked files present (use "git add" to track) russ@tuonela:~/git/project1> git commit -a .gitignore fatal: Paths with -a does not make sense. russ@tuonela:~/git/project1> git commit .gitignore error: pathspec '.gitignore' did not match any file(s) known to git. russ@tuonela:~/git/project1> git status -u # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .gitignore nothing added to commit but untracked files present (use "git add" to track) russ@tuonela:~/git/project1> ll total 16 drwxr-xr-x 6 russ russ 280 2011-08-16 11:21 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.log -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.tmp -rw-r--r-- 1 russ russ 140 2011-08-16 10:44 first.html drwxr-xr-x 8 russ russ 328 2011-08-16 11:24 .git -rw-r--r-- 1 russ russ 12 2011-08-16 11:20 .gitignore drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 output drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 subfolder drwxr-xr-x 2 russ russ 176 2011-08-16 11:22 target russ@tuonela:~/git/project1> git add .gitignore russ@tuonela:~/git/project1> git commit .gitignore -m"Ignore this" [master 6dabf76] Ignore this 1 files changed, 2 insertions(+), 0 deletions(-) create mode 100644 .gitignore russ@tuonela:~/git/project1> git status # On branch master nothing to commit (working directory clean) russ@tuonela:~/git/project1> ll total 16 drwxr-xr-x 6 russ russ 280 2011-08-16 11:21 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.log -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.tmp -rw-r--r-- 1 russ russ 140 2011-08-16 10:44 first.html drwxr-xr-x 8 russ russ 328 2011-08-16 11:24 .git -rw-r--r-- 1 russ russ 12 2011-08-16 11:20 .gitignore drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 output drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 subfolder drwxr-xr-x 2 russ russ 176 2011-08-16 11:22 target
git rm <filename>: what if a tool deleted a file as a by-product of its function? For example, Eclipse's superb refactoring mechanism? What does Git think in that case?
It's no different than if it was done explicitly from the command line (rm) or using the Git command. What's more, Git will probably recognize, in the case of refactoring, that the file was renamed and not merely deleted.
Formally speaking, you should stage any removal using git add -u ..
And, what if you didn't want to delete the file after all? Follow along here through the deletions and renames (hoping the scrape is still clear).
russ@tuonela:~/git/project1> git rm first.html rm 'first.html' russ@tuonela:~/git/project1> git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # deleted: first.html # russ@tuonela:~/git/project1> ll total 12 drwxr-xr-x 6 russ russ 248 2011-08-16 11:29 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.log -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.tmp drwxr-xr-x 8 russ russ 328 2011-08-16 11:29 .git -rw-r--r-- 1 russ russ 12 2011-08-16 11:20 .gitignore drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 output drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 subfolder drwxr-xr-x 2 russ russ 176 2011-08-16 11:22 target russ@tuonela:~/git/project1> git reset HEAD first.html Unstaged changes after reset: M first.html russ@tuonela:~/git/project1> git status # On branch master # Changed but not updated: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: first.html # no changes added to commit (use "git add" and/or "git commit -a")
We then unstaged the file and unstaged the deletion, but haven't committed the unstaging of the deletion. We'll use git reset --hard.
russ@tuonela:~/git/project1> git reset --hard # be careful with this one! HEAD is now at 6dabf76 Ignore this russ@tuonela:~/git/project1> ll total 16 drwxr-xr-x 6 russ russ 280 2011-08-16 11:31 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.log -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.tmp -rw-r--r-- 1 russ russ 140 2011-08-16 11:31 first.html drwxr-xr-x 8 russ russ 360 2011-08-16 11:31 .git -rw-r--r-- 1 russ russ 12 2011-08-16 11:20 .gitignore drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 output drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 subfolder drwxr-xr-x 2 russ russ 176 2011-08-16 11:22 target russ@tuonela:~/git/project1> git status # On branch master nothing to commit (working directory clean) russ@tuonela:~/git/project1> rm first.html russ@tuonela:~/git/project1> git status # On branch master # Changed but not updated: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: first.html # no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1> git rm first.html rm 'first.html' russ@tuonela:~/git/project1> git reset --hard HEAD is now at 6dabf76 Ignore this russ@tuonela:~/git/project1> rm first.html russ@tuonela:~/git/project1> git status # On branch master # Changed but not updated: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: first.html # no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1> rm subfolder/second.html russ@tuonela:~/git/project1> git status # On branch master # Changed but not updated: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: first.html # deleted: subfolder/second.html # no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1> git add -u . russ@tuonela:~/git/project1> git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # deleted: first.html # deleted: subfolder/second.html # russ@tuonela:~/git/project1> git reset --hard HEAD is now at 6dabf76 Ignore this russ@tuonela:~/git/project1> git status # On branch master nothing to commit (working directory clean) russ@tuonela:~/git/project1> ll total 16 drwxr-xr-x 6 russ russ 280 2011-08-16 11:34 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.log -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.tmp -rw-r--r-- 1 russ russ 140 2011-08-16 11:34 first.html drwxr-xr-x 8 russ russ 360 2011-08-16 11:34 .git -rw-r--r-- 1 russ russ 12 2011-08-16 11:20 .gitignore drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 output drwxr-xr-x 2 russ russ 112 2011-08-16 11:34 subfolder drwxr-xr-x 2 russ russ 176 2011-08-16 11:22 target russ@tuonela:~/git/project1> clear russ@tuonela:~/git/project1> ll total 16 drwxr-xr-x 6 russ russ 280 2011-08-16 11:34 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.log -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.tmp -rw-r--r-- 1 russ russ 140 2011-08-16 11:34 first.html drwxr-xr-x 8 russ russ 360 2011-08-16 11:34 .git -rw-r--r-- 1 russ russ 12 2011-08-16 11:20 .gitignore drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 output drwxr-xr-x 2 russ russ 112 2011-08-16 11:34 subfolder drwxr-xr-x 2 russ russ 176 2011-08-16 11:22 target
Git is superb (as already alluded to) in renaming files using git mv <file1> <file2>. This is a "macro" operation that is composed of git add <file2> followed by git rm <file1>.
You can also do this from the console command line (using mv), then follow it up by staging everything, git add -A . .
russ@tuonela:~/git/project1> git mv first.html firstredux.html russ@tuonela:~/git/project1> git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # renamed: first.html -> firstredux.html # russ@tuonela:~/git/project1> git commit -m"Moved this file to redux." [master 280e8da] Moved this file to redux. 1 files changed, 0 insertions(+), 0 deletions(-) rename first.html => firstredux.html (100%) russ@tuonela:~/git/project1> git status # On branch master nothing to commit (working directory clean) russ@tuonela:~/git/project1> mv firstredux.html unoredux.html russ@tuonela:~/git/project1> git status # On branch master # Changed but not updated: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: firstredux.html # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # unoredux.html no changes added to commit (use "git add" and/or "git commit -a")
Git is in the dark, right?
russ@tuonela:~/git/project1> git add -A . russ@tuonela:~/git/project1> git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # renamed: firstredux.html -> unoredux.html # russ@tuonela:~/git/project1> git commit -m"Renamed it again." [master c1adc88] Renamed it again. 1 files changed, 0 insertions(+), 0 deletions(-) rename firstredux.html => unoredux.html (100%) russ@tuonela:~/git/project1> git log --stat -3 commit c1adc88d620587a095535309aeb2945a8eec12b1 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:41:24 2011 -0600 Renamed it again. firstredux.html | 16 ---------------- unoredux.html | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) commit 280e8dac5899f1e532d652d6eb2d37a50b7c1602 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:39:44 2011 -0600 Moved this file to redux. first.html | 16 ---------------- firstredux.html | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) commit 6dabf7651a63c616b072ec9ca3239678de772928 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:24:37 2011 -0600 Ignore this .gitignore | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
See how the conception of mv is a display-level thing while there's add and rm underneath. This is Linus' "similarity index."
russ@tuonela:~/git/project1> git log --stat -3 -M commit c1adc88d620587a095535309aeb2945a8eec12b1 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:41:24 2011 -0600 Renamed it again. firstredux.html => unoredux.html | 0 1 files changed, 0 insertions(+), 0 deletions(-) commit 280e8dac5899f1e532d652d6eb2d37a50b7c1602 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:39:44 2011 -0600 Moved this file to redux. first.html => firstredux.html | 0 1 files changed, 0 insertions(+), 0 deletions(-) commit 6dabf7651a63c616b072ec9ca3239678de772928 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:24:37 2011 -0600 Ignore this .gitignore | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
By default, this option is off. It's expensive in many cases. Git moved tracking of renames, copies and similar copies to display layer rather than the history which has the deletion and addition of another file. You can never "git" it wrong on rename/move! Option -M looks across a single commit. What if you wanted to see it across more than one commit? Use option --find-copies-harder. It's much more work for Git, but it's a possibility.
russ@tuonela:~/git/project1> git log --stat -3 -M --find-copies-harder commit c1adc88d620587a095535309aeb2945a8eec12b1 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:41:24 2011 -0600 Renamed it again. firstredux.html => unoredux.html | 0 1 files changed, 0 insertions(+), 0 deletions(-) commit 280e8dac5899f1e532d652d6eb2d37a50b7c1602 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:39:44 2011 -0600 Moved this file to redux. first.html => firstredux.html | 0 1 files changed, 0 insertions(+), 0 deletions(-) commit 6dabf7651a63c616b072ec9ca3239678de772928 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:24:37 2011 -0600 Ignore this .gitignore | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) russ@tuonela:~/git/project1> ll total 16 drwxr-xr-x 6 russ russ 280 2011-08-16 11:40 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.log -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.tmp drwxr-xr-x 8 russ russ 360 2011-08-16 11:41 .git -rw-r--r-- 1 russ russ 12 2011-08-16 11:20 .gitignore drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 output drwxr-xr-x 2 russ russ 112 2011-08-16 11:34 subfolder drwxr-xr-x 2 russ russ 176 2011-08-16 11:22 target -rw-r--r-- 1 russ russ 140 2011-08-16 11:34 unoredux.html russ@tuonela:~/git/project1> mv unoredux.html unoredux2.html russ@tuonela:~/git/project1> git status # On branch master # Changed but not updated: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: unoredux.html # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # unoredux2.html no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1> mv unoredux2.html target russ@tuonela:~/git/project1> vim target/unoredux2.html russ@tuonela:~/git/project1> git status # On branch master # Changed but not updated: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: unoredux.html # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # target/unoredux2.html no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1> git add . russ@tuonela:~/git/project1> git add -A . russ@tuonela:~/git/project1> git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # renamed: unoredux.html -> target/unoredux2.html #
Wow! It realizes it's a rename. Score is 77%! That is, 77% the same content despite changes.
russ@tuonela:~/git/project1> git commit -m"Renamed and changes code" [master 8577fa5] Renamed and changes code 1 files changed, 1 insertions(+), 1 deletions(-) rename unoredux.html => target/unoredux2.html (77%) russ@tuonela:~/git/project1> cp target/unoredux2.html target/duoredux.html russ@tuonela:~/git/project1> vim target/duoredux.html russ@tuonela:~/git/project1> vim target/unoredux2.html russ@tuonela:~/git/project1> git add -A . russ@tuonela:~/git/project1> git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # new file: target/duoredux.html # modified: target/unoredux2.html # russ@tuonela:~/git/project1> git commit -m"Copied and changed." [master c7bbcce] Copied and changed. 2 files changed, 23 insertions(+), 0 deletions(-) create mode 100644 target/duoredux.html russ@tuonela:~/git/project1> git status # On branch master nothing to commit (working directory clean) russ@tuonela:~/git/project1> git log -2 --stat -C commit c7bbcce9dfcb5f737b573875963283cd1bdf2a27 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:50:06 2011 -0600 Copied and changed. target/{unoredux2.html => duoredux.html} | 2 ++ target/unoredux2.html | 5 +++++ 2 files changed, 7 insertions(+), 0 deletions(-) commit 8577fa50835f896a075a3bc5a80c6edbdfe2b892 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:48:23 2011 -0600 Renamed and changes code unoredux.html => target/unoredux2.html | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) russ@tuonela:~/git/project1> git log -2 --stat -C --find-copies-harder commit c7bbcce9dfcb5f737b573875963283cd1bdf2a27 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:50:06 2011 -0600 Copied and changed. target/{unoredux2.html => duoredux.html} | 2 ++ target/unoredux2.html | 5 +++++ 2 files changed, 7 insertions(+), 0 deletions(-) commit 8577fa50835f896a075a3bc5a80c6edbdfe2b892 Author: Russell Bateman <[email protected] <mailto:[email protected]>> Date: Tue Aug 16 11:48:23 2011 -0600 Renamed and changes code unoredux.html => target/unoredux2.html | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
Look at curly-brace syntax: Git knows what it's doing! Similarity index: also works across branches and moving blocks of code.
russ@tuonela:~/git/project1> ll total 12 drwxr-xr-x 6 russ russ 248 2011-08-16 11:47 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.log -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.tmp drwxr-xr-x 8 russ russ 360 2011-08-16 11:50 .git -rw-r--r-- 1 russ russ 12 2011-08-16 11:20 .gitignore drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 output drwxr-xr-x 2 russ russ 112 2011-08-16 11:34 subfolder drwxr-xr-x 2 russ russ 240 2011-08-16 11:49 target russ@tuonela:~/git/project1> cd subfolder/ russ@tuonela:~/git/project1/subfolder> ll total 8 drwxr-xr-x 2 russ russ 112 2011-08-16 11:34 . drwxr-xr-x 6 russ russ 248 2011-08-16 11:47 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build.log -rw-r--r-- 1 russ russ 163 2011-08-16 11:34 second.html russ@tuonela:~/git/project1/subfolder> vim second.html russ@tuonela:~/git/project1/subfolder> git status # On branch master # Changed but not updated: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: second.html # no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1/subfolder> git add -p second.html # reminder: log -p show patches/changes diff --git a/subfolder/second.html b/subfolder/second.html index 22aeb43..3e024c7 100644 --- a/subfolder/second.html +++ b/subfolder/second.html @@ -1,6 +1,8 @@ <html> <head> <!-- Head stuff that I want to change. --> +<!-- Part one --> + </head> <body> Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y @@ -10,6 +12,7 @@ Body . (second file) <p> Another paragraph. (second file) +Part Two </p> </body> Stage this hunk [y,n,q,a,d,/,K,g,e,?]? y
Asked us which block of content to stage! (Matthew said not to stage #2, but I missed it). This is a level of precision not understood by other VCSs.
russ@tuonela:~/git/project1/subfolder> git diff error: invalid option: --stage russ@tuonela:~/git/project1/subfolder> git diff --staged diff --git a/subfolder/second.html b/subfolder/second.html index 22aeb43..3e024c7 100644 --- a/subfolder/second.html +++ b/subfolder/second.html @@ -1,6 +1,8 @@ <html> <head> <!-- Head stuff that I want to change. --> +<!-- Part one --> + </head> <body> @@ -10,6 +12,7 @@ Body . (second file) <p> Another paragraph. (second file) +Part Two </p> </body> russ@tuonela:~/git/project1/subfolder> git commit-m"First part" fatal: cannot exec 'git-commit-mFirst part': Not a directory russ@tuonela:~/git/project1/subfolder> git commit -m"First part" [master bac532a] First part 1 files changed, 3 insertions(+), 0 deletions(-)
Could have used git commit -a -m"Second part" if we had not mistakenly staged the second change. Git's diff won't track changes across a git diff move? ¡Si!
git log --follow-renames <filename> will walk back across the renaming!
russ@tuonela:~> cd git russ@tuonela:~/git> ll total 7 drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 . drwxr-xr-x 78 russ russ 3304 2011-08-16 11:52 .. -rwxr-xr-x 1 russ russ 45 2011-08-16 10:07 mygitssh.sh drwxr-xr-x 6 russ russ 248 2011-08-16 11:47 project1 russ@tuonela:~/git> cd project1/ russ@tuonela:~/git/project1> cd subfolder/ russ@tuonela:~/git/project1/subfolder> echo JUNK > junk.txt russ@tuonela:~/git/project1/subfolder> git add junk.txt russ@tuonela:~/git/project1/subfolder> git commit -m"junk" [master d7a2c19] junk 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 subfolder/junk.txt russ@tuonela:~/git/project1/subfolder> git status # On branch master nothing to commit (working directory clean) russ@tuonela:~/git/project1/subfolder> cd .. russ@tuonela:~/git/project1> vim .gitignore russ@tuonela:~/git/project1> gvim subfolder/junk.txt Xlib: extension "RANDR" missing on display ":0.0". russ@tuonela:~/git/project1> vim .gitignore russ@tuonela:~/git/project1> git commit -m"Ignoring junk.txt # On branch master # Changed but not updated: # (use "git add..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: .gitignore # modified: subfolder/junk.txt # no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1> git mv subfolder/junk.txt . russ@tuonela:~/git/project1> ll total 16 drwxr-xr-x 6 russ russ 272 2011-08-16 13:06 . drwxr-xr-x 3 russ russ 104 2011-08-16 10:23 .. -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.log -rw-r--r-- 1 russ russ 5 2011-08-16 11:19 build2.tmp drwxr-xr-x 8 russ russ 360 2011-08-16 13:06 .git -rw-r--r-- 1 russ russ 21 2011-08-16 13:05 .gitignore -rw-r--r-- 1 russ russ 15 2011-08-16 13:05 junk.txt drwxr-xr-x 2 russ russ 112 2011-08-16 11:19 output drwxr-xr-x 2 russ russ 112 2011-08-16 13:06 subfolder drwxr-xr-x 2 russ russ 240 2011-08-16 11:49 target russ@tuonela:~/git/project1> git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # renamed: subfolder/junk.txt -> junk.txt # # Changed but not updated: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: .gitignore # modified: junk.txt # russ@tuonela:~/git/project1> git rm junk.txt -f rm 'junk.txt' russ@tuonela:~/git/project1> git commit -m"Removing junk.txt" [master 7ab48ed] Removing junk.txt 1 files changed, 0 insertions(+), 1 deletions(-) delete mode 100644 subfolder/junk.txt russ@tuonela:~/git/project1> git status # On branch master # Changed but not updated: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: .gitignore # no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1> git commit # On branch master # Changed but not updated: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: .gitignore # no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/project1> git add .gitignore russ@tuonela:~/git/project1> git commit .gitignore -m"ignore" [master 73ef254] ignore 1 files changed, 1 insertions(+), 0 deletions(-) russ@tuonela:~/git/project1> git status # On branch master nothing to commit (working directory clean)
Now we're going to see remote repositories.
What's off-line? Everything we did this morning: no network activity.
Clone a repository: retrieve full copy of entire history of a project. Even though we grab that copy, all operations remain unchanged. We work, commit, etc., but then push the changes.
The model is the mobile device: compose an e-mail locally, when connectivity available, push "changes."
There's a huge performance win in batching these changes. Git does file-by-file operations locally, batches the push on the network.
Git clones over what protocol?
These aren't the same. The second command hard-links; it doesn't make a second copy. So, beware!
Across "Git" protocol: git clone git://server/project[.git]
russ@tuonela:~/git/project1> cd .. russ@tuonela:~/git> git clone [email protected]:matthew/hellogitworld.git Initialized empty Git repository in /home/russ/git/hellogitworld/.git/ remote: Counting objects: 61, done. remote: Compressing objects: 100% (46/46), done. remote: Total 61 (delta 23), reused 43 (delta 8) Receiving objects: 100% (61/61), 6.19 KiB, done. Resolving deltas: 100% (23/23), done.
Strictly speaking, you should have the .git extension on the repository name.
russ@tuonela:~/git> ll total 7 drwxr-xr-x 4 russ russ 136 2011-08-16 13:19 . drwxr-xr-x 78 russ russ 3304 2011-08-16 13:05 .. drwxr-xr-x 5 russ russ 216 2011-08-16 13:19 hellogitworld -rwxr-xr-x 1 russ russ 45 2011-08-16 10:07 mygitssh.sh drwxr-xr-x 6 russ russ 248 2011-08-16 13:06 project1 russ@tuonela:~/git> tree hellogitworld/ hellogitworld/ |-- README.txt |-- resources | `-- labels.properties |-- runme.sh `-- src |-- Division.groovy |-- Main.groovy |-- Square.groovy |-- Subtract.groovy `-- Sum.groovy 2 directories, 8 files
Do git clone
russ@tuonela:~/git> git clone [email protected]:matthew/hellogitworld.git hgw Initialized empty Git repository in /home/russ/git/hgw/.git/ remote: Counting objects: 61, done. remote: Compressing objects: 100% (46/46), done. remote: Total 61 (delta 23), reused 43 (delta 8) Receiving objects: 100% (61/61), 6.19 KiB, done. Resolving deltas: 100% (23/23), done.
It's possible to clone into an existing folder. There are strange incantations to do this. Just avoid the problem.
russ@tuonela:~/git> cd hgw russ@tuonela:~/git/hgw> ll total 12 drwxr-xr-x 5 russ russ 216 2011-08-16 13:22 . drwxr-xr-x 5 russ russ 160 2011-08-16 13:21 .. drwxr-xr-x 8 russ russ 328 2011-08-16 13:22 .git -rw-r--r-- 1 russ russ 7 2011-08-16 13:22 .gitignore -rw-r--r-- 1 russ russ 768 2011-08-16 13:22 README.txt drwxr-xr-x 2 russ russ 88 2011-08-16 13:22 resources -rwxr-xr-x 1 russ russ 63 2011-08-16 13:22 runme.sh drwxr-xr-x 2 russ russ 208 2011-08-16 13:22 src russ@tuonela:~/git/hgw> git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/gh-pages remotes/origin/master
The master branch is the local one we're working with. Here we're in branch master. We have a copy of the repository, hellogitworld.git.
russ@tuonela:~/git/hgw> echo Hello > matthew.txt russ@tuonela:~/git/hgw> git add matthew.txt russ@tuonela:~/git/hgw> git commit matthew.txt -m"Matthew added text" [master acc4e03] Matthew added text 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 matthew.txt russ@tuonela:~/git/hgw> git status # On branch master # Your branch is ahead of 'origin/master' by 1 commit. # nothing to commit (working directory clean)
Now push the change back.
russ@tuonela:~/git/hgw> git push ERROR: Permission to matthew/hellogitworld.git denied to russellbateman. fatal: The remote end hung up unexpectedly russ@tuonela:~/git/hgw> git push origin ERROR: Permission to matthew/hellogitworld.git denied to russellbateman. fatal: The remote end hung up unexpectedly # Big concept: Git versions at the repository level; it locks at this level; it advances at this level.
A single change, even though with no conflict, preempts us. We must pick it up and integrate it with our code before we can push our changes there. Intelligent things happen in your local copy—not on the server: this saves the server from processing!
russ@tuonela:~/git/hgw> git pull remote: Counting objects: 21, done. remote: Compressing objects: 100% (11/11), done. remote: Total 19 (delta 10), reused 16 (delta 7) Unpacking objects: 100% (19/19), done. From github.com:matthew/hellogitworld 8d2636d..122cd98 master -> origin/master Merge made by recursive. resources/chris.txt | 1 + resources/davedkg.txt | 1 + resources/endris.txt | 1 + resources/matthew.txt | 1 + 4 files changed, 4 insertions(+), 0 deletions(-) create mode 100644 resources/chris.txt create mode 100644 resources/davedkg.txt create mode 100644 resources/endris.txt create mode 100644 resources/matthew.txt
(These guys knew to merge.)
russ@tuonela:~/git/hgw> git pull remote: Counting objects: 14, done. remote: Compressing objects: 100% (6/6), done. remote: Total 10 (delta 3), reused 10 (delta 3) Unpacking objects: 100% (10/10), done. From github.com:matthew/hellogitworld 122cd98..77c0026 master -> origin/master Merge made by recursive. damien.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 damien.txt russ@tuonela:~/git/hgw> git push ERROR: Permission to matthew/hellogitworld.git denied to russellbateman. fatal: The remote end hung up unexpectedly russ@tuonela:~/git/hgw> git pull remote: Counting objects: 11, done. remote: Compressing objects: 100% (3/3), done. remote: Total 7 (delta 4), reused 7 (delta 4) Unpacking objects: 100% (7/7), done. From github.com:matthew/hellogitworld 77c0026..f16c9eb master -> origin/master Merge made by recursive. resources/william.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 resources/william.txt russ@tuonela:~/git/hgw> git push ERROR: Permission to matthew/hellogitworld.git denied to russellbateman. fatal: The remote end hung up unexpectedly russ@tuonela:~/git/hgw> git pull Already up-to-date. russ@tuonela:~/git/hgw> git push ERROR: Permission to matthew/hellogitworld.git denied to russellbateman. fatal: The remote end hung up unexpectedly
Of course, I don't have rights to do this? No, not until he gives them to me on GitHub.
russ@tuonela:~/git/hgw> git push ERROR: Permission to matthew/hellogitworld.git denied to russellbateman. fatal: The remote end hung up unexpectedly
In Subversion, one never actually has a copy of the branch. Everyone works on the (same) remote copy. The intelligence happens at the server. With Git, I have a lease on my own branch. Later, when I have connectivity, I reconcile with the server. The intelligence is local; the conceptual model is the mobile device.
russ@tuonela:~/git/hgw> git push To [email protected]:matthew/hellogitworld.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to '[email protected]:matthew/hellogitworld.git' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes before pushing again. See the 'Note about fast-forwards' section of 'git push --help' for details. russ@tuonela:~/git/hgw> git pull remote: Counting objects: 25, done. remote: Compressing objects: 100% (9/9), done. remote: Total 18 (delta 10), reused 16 (delta 8) Unpacking objects: 100% (18/18), done. From github.com:matthew/hellogitworld f16c9eb..dfa5ab2 master -> origin/master Merge made by recursive. resources/sally.txt | 1 + resources/valerio.txt | 1 + 2 files changed, 2 insertions(+), 0 deletions(-) create mode 100644 resources/sally.txt create mode 100644 resources/valerio.txt russ@tuonela:~/git/hgw> git push Counting objects: 15, done. Delta compression using up to 4 threads. Compressing objects: 100% (10/10), done. Writing objects: 100% (10/10), 1.29 KiB, done. Total 10 (delta 5), reused 0 (delta 0) To [email protected]:matthew/hellogitworld.git dfa5ab2..6e78e20 master -> master
Now that I have write-access...)
russ@tuonela:~/git/hgw> mkdir threethousand russ@tuonela:~/git/hgw> cd threethousand/
The instructor generated 3,000 text files, added and committed them to observe the speed impact. It was near zero.
He pushed them, then showed how long it took the browser to display them (very long time) in opposition to how long it took to push them (instantaneous).
Local and remote branches live on the local box. Think of cached pages you can edit and see in Firefox. You could push these or not to the server. The remote is the cached copy—what it saw the last time it went to the server. The local copy is like the in-memory copy. We are NOT talking about branches here, but local and remote.
push means push to the cache.
pull means make the cache as recent as the upstream.
pull populates our remote (our cache).
pull does too much!?
What if we deconstructed it? Let's say we're leaving office, want everybody's code, but don't want to contaminate our own local stuff.
git fetch populates with updated cache, but doesn't do the merge that pull does. Commit -> Push -> Pull -> (Commit)
You can push to more than one repository. (Try that with Subversion.)
russ@tuonela:~/git/hgw> git log -1 commit 6e78e200bdf09cd15154fd235d9b5f53fe939b80 Merge: fde95f2 dfa5ab2 Author: Russell BatemanDate: Tue Aug 16 13:33:49 2011 -0600 Merge branch 'master' of github.com:matthew/hellogitworld russ@tuonela:~/git/hgw> git pull remote: Counting objects: 3752, done. remote: Compressing objects: 100% (5/5), done. remote: Total 3748 (delta 4), reused 3747 (delta 3) Receiving objects: 100% (3748/3748), 215.51 KiB, done. Resolving deltas: 100% (4/4), completed with 2 local objects. From github.com:matthew/hellogitworld 6e78e20..7133c1e master -> origin/master Updating 6e78e20..7133c1e Fast-forward resources/chris.txt | 1 + threethousand/sample1.txt | 1 + threethousand/sample10.txt | 1 + threethousand/sample100.txt | 1 + threethousand/sample1000.txt | 1 + ... threethousand/sample994.txt | 1 + threethousand/sample995.txt | 1 + threethousand/sample996.txt | 1 + threethousand/sample997.txt | 1 + threethousand/sample998.txt | 1 + threethousand/sample999.txt | 1 + 4001 files changed, 4001 insertions(+), 0 deletions(-) create mode 100644 threethousand/sample1.txt create mode 100644 threethousand/sample10.txt create mode 100644 threethousand/sample100.txt create mode 100644 threethousand/sample1000.txt create mode 100644 threethousand/sample1001.txt create mode 100644 threethousand/sample1002.txt create mode 100644 threethousand/sample1003.txt create mode 100644 threethousand/sample1004.txt create mode 100644 threethousand/sample1005.txt create mode 100644 threethousand/sample1006.txt create mode 100644 threethousand/sample1007.txt create mode 100644 threethousand/sample1008.txt create mode 100644 threethousand/sample1009.txt ... create mode 100644 threethousand/sample994.txt create mode 100644 threethousand/sample995.txt create mode 100644 threethousand/sample996.txt create mode 100644 threethousand/sample997.txt create mode 100644 threethousand/sample998.txt create mode 100644 threethousand/sample999.txt russ@tuonela:~/git/hgw> git log -1 commit 7133c1ed093d71fc65c375d70479958e00f79a14 Author: ArlingtonHouse Date: Tue Aug 16 16:45:41 2011 -0300 Edited resources/chris.txt via GitHub russ@tuonela:~/git/hgw> git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/gh-pages remotes/origin/master
(Show me all the branches: in the console, when color is enabled, the master is local (green); the others (red) are remotes.
russ@tuonela:~/git/hgw> git diff master remotes/origin/master
Should show what's different between my branch and whatever else someone has pushed to origin. (Nothing in my case here.)
russ@tuonela:~/git/hgw> git status # On branch master nothing to commit (working directory clean) # Remotes are just symbolic names. # origin is a remote that just comes for free if you cloned. russ@tuonela:~/git/hgw> git remote -v origin [email protected]:matthew/hellogitworld.git (fetch) origin [email protected]:matthew/hellogitworld.git (push) russ@tuonela:~/git/hgw> git remote origin russ@tuonela:~/git/hgw> git remote add githubhttps \ https://[email protected]/matthewmcculloug/hellogitworld.git russ@tuonela:~/git/hgw> cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = [email protected]:matthew/hellogitworld.git [branch "master"] remote = origin merge = refs/heads/master [remote "githubhttps"] url = https://[email protected]/matthewmcculloug/hellogitworld.git fetch = +refs/heads/*:refs/remotes/githubhttps/*
See? We have a new remote (that happens to be the same as origin). origin is the default, but we've created an explicit one. Imagine that your client has a crazy blocking firewall; you'd do this to use HTTPS instead of SSL:
russ@tuonela:~/git/hgw> git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/gh-pages remotes/origin/gethubhttps remotes/origin/master russ@tuonela:~/git/hgw> git update-server-info russ@tuonela:~/git/hgw> cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = [email protected]:matthew/hellogitworld.git [branch "master"] remote = origin merge = refs/heads/master [remote "githubhttps"] url = https://[email protected]/matthewmcculloug/hellogitworld.git fetch = +refs/heads/*:refs/remotes/githubhttps/* russ@tuonela:~/git/hgw> git fetch githubhttps Password: remote: Counting objects: 5, done. remote: Compressing objects: 100% (1/1), done. remote: Total 3 (delta 1), reused 3 (delta 1) Unpacking objects: 100% (3/3), done. From https://github.com/matthew/hellogitworld * [new branch] gh-pages -> githubhttps/gh-pages * [new branch] master -> githubhttps/master
He covered git config --global alias.___ "command" which I could not make work locally. Also, git config --global alias.sync "!git pull && git push".
russ@tuonela:~/git/hgw> git config --global alias.s "status" russ@tuonela:~/git/hgw> git s fatal: cannot exec 'git-s': Not a directory
(Hmmm... not what I was expecting. Maybe have to turn something on?)
You can create shell prompt enhancements that augment the prompt with remote repository indications, etc.
Use commits as your "source-code journal." This can be done because deltas aren't stored; Git doesn't have to replay them. Git stores files, not deltas. Actually, it stores everything at every commit, including directories.
But, anything that hasn't changed is stored as a hard link instead of a copy. And the whole thing is just compressed as one file (blob) under a SHA-1 hash as seen below. The SHA-1 hashes are our commits.
russ@tuonela:~/git/hgw> tree .git .git |-- branches |-- COMMIT_EDITMSG |-- config |-- description |-- FETCH_HEAD |-- HEAD |-- hooks | |-- applypatch-msg.sample | |-- commit-msg.sample | |-- post-commit.sample | |-- post-receive.sample | |-- post-update.sample | |-- pre-applypatch.sample | |-- pre-commit.sample | |-- prepare-commit-msg.sample | |-- pre-rebase.sample | `-- update.sample |-- index |-- info | |-- exclude | `-- refs |-- logs | |-- HEAD | `-- refs | |-- heads | | `-- master | `-- remotes | |-- githubhttps | | |-- gh-pages | | `-- master | `-- origin | `-- master |-- objects | |-- 03 | | `-- 98668fa53269e72ab20b6687221dc259739b5a | |-- 05 | | `-- 9486ecf74be5a9ad23d78737e78fc8c7fd5ef9 | |-- 12 | | `-- 2cd985d68ff3612a30e208234eb59a6be1b21b ... | | `-- 151b5c78f5ed4be7e9b17ba86e056585e44d69 | |-- fd | | |-- 19d4e292d09b6631fe6b2bb38c64a38b829334 | | |-- 6b1976f77fdacbd85a8e1901e69188c657a4e9 | | `-- e95f299fee67851062c7dbdb9166199fd9d7c5 | |-- fe | | `-- 2feec822a87a65ea27080b6a117f15719dfaa2 | |-- info | | `-- packs | `-- pack | |-- pack-1e37075506d7c4323a19251172aa898172a396cb.idx | |-- pack-1e37075506d7c4323a19251172aa898172a396cb.pack | |-- pack-9301a627663f4fec6aab3698d97c4e7e2023ac3b.idx | `-- pack-9301a627663f4fec6aab3698d97c4e7e2023ac3b.pack |-- ORIG_HEAD |-- packed-refs `-- refs |-- heads | `-- master |-- remotes | |-- githubhttps | | |-- gh-pages | | `-- master | `-- origin | |-- HEAD | `-- master `-- tags 77 directories, 100 files
This garbage-collects explicitly (by default it's every 5K commits). The hashed files are collapsed to only two (tiny) files.
russ@tuonela:~/git/hgw> git gc Counting objects: 3874, done. Delta compression using up to 4 threads. Compressing objects: 100% (96/96), done. Writing objects: 100% (3874/3874), done. Total 3874 (delta 60), reused 3808 (delta 27) russ@tuonela:~/git/hgw> tree .git .git |-- branches |-- COMMIT_EDITMSG |-- config |-- description |-- FETCH_HEAD |-- HEAD |-- hooks | |-- applypatch-msg.sample | |-- commit-msg.sample | |-- post-commit.sample | |-- post-receive.sample | |-- post-update.sample | |-- pre-applypatch.sample | |-- pre-commit.sample | |-- prepare-commit-msg.sample | |-- pre-rebase.sample | `-- update.sample |-- index |-- info | |-- exclude | `-- refs |-- logs | |-- HEAD | `-- refs | |-- heads | | `-- master | `-- remotes | |-- githubhttps | | |-- gh-pages | | `-- master | `-- origin | `-- master |-- objects | |-- info | | `-- packs | `-- pack | |-- pack-ff01cac82df40eff8a3fa3699a9289e0c2989703.idx | `-- pack-ff01cac82df40eff8a3fa3699a9289e0c2989703.pack |-- ORIG_HEAD |-- packed-refs `-- refs |-- heads |-- remotes | |-- githubhttps | `-- origin | `-- HEAD `-- tags 18 directories, 29 files russ@tuonela:~/git/hgw> ll .git/objects/pack/pack-ff01cac82df40eff8a3fa3699a9289e0c2989703.idx -r--r--r-- 1 russ russ 109544 2011-08-16 14:31 .git/objects/pack/pack-ff01cac82df40eff8a3fa3699a9289e0c2989703.idx russ@tuonela:~/git/hgw> ll .git/objects/pack/pack-ff01cac82df40eff8a3fa3699a9289e0c2989703.pack -r--r--r-- 1 russ russ 233670 2011-08-16 14:31 .git/objects/pack/pack-ff01cac82df40eff8a3fa3699a9289e0c2989703.pack russ@tuonela:~/git/hgw> git show 6e fatal: ambiguous argument '6e': unknown revision or path not in the working tree. Use '--' to separate paths from revisions russ@tuonela:~/git/hgw> git show 6e7a fatal: ambiguous argument '6e7a': unknown revision or path not in the working tree. Use '--' to separate paths from revisions russ@tuonela:~/git/hgw> git log --stat -3 commit 7133c1ed093d71fc65c375d70479958e00f79a14 Author: ArlingtonHouse <[email protected]> Date: Tue Aug 16 16:45:41 2011 -0300 Edited resources/chris.txt via GitHub resources/chris.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) commit 7d295ba130441171c586d09909089c77ed47eb47 Merge: e6e4cdd 6e78e20 Author: Matthew <[email protected]> Date: Tue Aug 16 13:38:11 2011 -0600 Merge branch 'master' of github.com:matthew/hellogitworld commit e6e4cdd2e238bc892d9aa956b5b966c48ab05acf Author: Matthew <[email protected]> Date: Tue Aug 16 13:38:02 2011 -0600 4000 threethousand/sample1.txt | 1 + threethousand/sample10.txt | 1 + threethousand/sample100.txt | 1 + ... threethousand/sample1031.txt | 1 + threethousand/sample1032.txt | 1 + russ@tuonela:~/git/hgw> git log -3 commit 7133c1ed093d71fc65c375d70479958e00f79a14 Author: ArlingtonHouse <[email protected]> Date: Tue Aug 16 16:45:41 2011 -0300 Edited resources/chris.txt via GitHub commit 7d295ba130441171c586d09909089c77ed47eb47 Merge: e6e4cdd 6e78e20 Author: Matthew <[email protected]> Date: Tue Aug 16 13:38:11 2011 -0600 Merge branch 'master' of github.com:matthew/hellogitworld commit e6e4cdd2e238bc892d9aa956b5b966c48ab05acf Author: Matthew <[email protected]> Date: Tue Aug 16 13:38:02 2011 -0600 4000 russ@tuonela:~/git/hgw> git show e6e4 commit e6e4cdd2e238bc892d9aa956b5b966c48ab05acf Author: Matthew <[email protected]> Date: Tue Aug 16 13:38:02 2011 -0600 4000 diff --git a/threethousand/sample1.txt b/threethousand/sample1.txt new file mode 100644 index 0000000..9b1c541 --- /dev/null +++ b/threethousand/sample1.txt @@ -0,0 +1 @@ +Some random text: 24004 diff --git a/threethousand/sample10.txt b/threethousand/sample10.txt new file mode 100644 index 0000000..6054a49 --- /dev/null +++ b/threethousand/sample10.txt @@ -0,0 +1 @@ +Some random text: 7695 diff --git a/threethousand/sample100.txt b/threethousand/sample100.txt new file mode 100644 index 0000000..2734e7d --- /dev/null +++ b/threethousand/sample100.txt @@ -0,0 +1 @@ +Some random text: 13966 diff --git a/threethousand/sample1000.txt b/threethousand/sample1000.txt new file mode 100644 index 0000000..ecabd15 --- /dev/null +++ b/threethousand/sample1000.txt @@ -0,0 +1 @@ +Some random text: 18559 diff --git a/threethousand/sample1001.txt b/threethousand/sample1001.txt new file mode 100644 index 0000000..2b113a4 --- /dev/null +++ b/threethousand/sample1001.txt @@ -0,0 +1 @@ +Some random text: 30180 diff --git a/threethousand/sample1002.txt b/threethousand/sample1002.txt new file mode 100644 index 0000000..e2b4cfd --- /dev/null +++ b/threethousand/sample1002.txt @@ -0,0 +1 @@ +Some random text: 3556 diff --git a/threethousand/sample1003.txt b/threethousand/sample1003.txt new file mode 100644 index 0000000..74e546f --- /dev/null +++ b/threethousand/sample1003.txt @@ -0,0 +1 @@ +Some random text: 31791 diff --git a/threethousand/sample1004.txt b/threethousand/sample1004.txt new file mode 100644 index 0000000..8126780 --- /dev/null +++ b/threethousand/sample1004.txt @@ -0,0 +1 @@ russ@tuonela:~/git/hgw>
Here we examine git rev-parse.
russ@tuonela:~/git/hgw> git rev-parse HEAD 7133c1ed093d71fc65c375d70479958e00f79a14 russ@tuonela:~/git/hgw> git rev-parse master 7133c1ed093d71fc65c375d70479958e00f79a14 russ@tuonela:~/git/hgw> git rev-parse remotes/origin/master 7133c1ed093d71fc65c375d70479958e00f79a14 russ@tuonela:~/git/hgw> git rev-parse e6e4 e6e4cdd2e238bc892d9aa956b5b966c48ab05acf
If a single bit changes anywhere, it changes the "version." Hashes are linked together in a list in branch. What's a tag? HEAD? Etc.? Just a hash!
russ@tuonela:~/git/hgw> git branch feature1 # Created a feature branch russ@tuonela:~/git/hgw> git rev-parse feature1 7133c1ed093d71fc65c375d70479958e00f79a14 russ@tuonela:~/git/hgw> git rev-parse master 7133c1ed093d71fc65c375d70479958e00f79a14 russ@tuonela:~/git/hgw> git rev-parse HEAD 7133c1ed093d71fc65c375d70479958e00f79a14
Stored in a plain text file 50 characters long.
russ@tuonela:~/git/hgw> cat .git/refs/heads/feature1 7133c1ed093d71fc65c375d70479958e00f79a14 russ@tuonela:~/git/hgw> echo 7133c1ed093d71fc65c375d70479958e00f79a14 > .git/refs/heads/feature2 russ@tuonela:~/git/hgw> git branch -a feature1 feature2 * master remotes/githubhttps/gh-pages remotes/githubhttps/master remotes/origin/HEAD -> origin/master remotes/origin/gh-pages remotes/origin/master
See new branch feature2. Plain text files represent everything.
russ@tuonela:~/git/hgw> git log -1 commit 7133c1ed093d71fc65c375d70479958e00f79a14 Author: ArlingtonHouseDate: Tue Aug 16 16:45:41 2011 -0300 Edited resources/chris.txt via GitHub russ@tuonela:~/git/hgw> git cat-file -p 7133c1ed093d71fc65c375d70479958e00f79a14 tree 393c34014c9bc2f53b1fe62e18dff8147024f4f6 parent 7d295ba130441171c586d09909089c77ed47eb47 author ArlingtonHouse 1313523941 -0300 committer ArlingtonHouse 1313523941 -0300 Edited resources/chris.txt via GitHubruss@tuonela:~/git/hgw> git cat-file -p 393c34014c9bc2f53b1fe62e18dff8147024f4f6 100644 blob 5241a7220a93c9db3e5d0cb642586fd917393c39 .gitignore 100644 blob 04d4072a557debb18cb0382deac91e969db92eba README.txt 100644 blob b14df6442ea5a1b382985a6549b85d435376c351 damien.txt 100644 blob e965047ad7c57865823c7d992b1d046ea66edf78 matthew.txt 040000 tree 0ef783762fad8b9248c9c140b63cb3611847ea05 resources 100755 blob fb4bd4ec9220ed4fe0d9526d1b77147490ce8842 runme.sh 040000 tree 42103128ceaebabff8f50cf408903d12e14c21d9 src 040000 tree 7618824166ce7b0f1e5f47ca3c847cc0b6393405 threethousand russ@tuonela:~/git/hgw> git cat-file -p e965047ad Hello
(We just walked structure from a commit.)
master is only a default—there for every repository.
Commands:
Any tiny thing you'd like to try out, create a branch. If it works out, merge it back. Agile: Product -> Integration -> Feature -> Story -> Idea. Antipattern? Maybe.
russ@tuonela:~/git/hgw> git branch feature1 fatal: A branch named 'feature1' already exists. russ@tuonela:~/git/hgw> git checkout feature1 Switched to branch 'feature1' russ@tuonela:~/git/hgw> git branch * feature1 feature2 master russ@tuonela:~/git/hgw> vim matthew.txt russ@tuonela:~/git/hgw> git add matthew.txt russ@tuonela:~/git/hgw> git commit -m"Matthew feature work" [feature1 a6b4d9d] Matthew feature work 1 files changed, 2 insertions(+), 0 deletions(-) russ@tuonela:~/git/hgw> git status On branch feature1 nothing to commit (working directory clean) russ@tuonela:~/git/hgw> git push origin feature1:featureruss # Called it feature1 locally, but calling it featureruss out there in the big world Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 295 bytes, done. Total 3 (delta 1), reused 0 (delta 0) To [email protected]:matthew/hellogitworld.git * [new branch] feature1 -> featureruss
End of the week. Features good? Fetch 'em down to your branch.
russ@tuonela:~/git/hgw> git fetch remote: Counting objects: 18, done. remote: Compressing objects: 100% (5/5), done. remote: Total 11 (delta 5), reused 10 (delta 4) Unpacking objects: 100% (11/11), done. From github.com:matthew/hellogitworld * [new branch] featuredavedkg -> origin/featuredavedkg * [new branch] featurematthew -> origin/featurematthew * [new branch] featurewilliam -> origin/featurewilliam 7133c1e..6a8265b master -> origin/master russ@tuonela:~/git/hgw> git merge remotes/origin/featurewilliam Merge made by recursive. resources/william.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) russ@tuonela:~/git/hgw> git merge origin/featuredavedkg Merge made by recursive. resources/davedkg.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) russ@tuonela:~/git/hgw> git status # On branch feature1 nothing to commit (working directory clean) russ@tuonela:~/git/hgw> git fetch remote: Counting objects: 23, done. remote: Compressing objects: 100% (7/7), done. remote: Total 12 (delta 5), reused 10 (delta 3) Unpacking objects: 100% (12/12), done. From github.com:matthew/hellogitworld * [new branch] featuredamien -> origin/featuredamien 6a8265b..b0b59d2 master -> origin/master russ@tuonela:~/git/hgw> git merge origin/featuredmatthew fatal: 'origin/featuredmatthew' does not point to a commit russ@tuonela:~/git/hgw> git merge origin Merge made by recursive. matthew.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
git merge origin :featureruss will delete branch russ because (the instructor) integrated russ' changes to matthew.txt.
This is superior to dragging files out of your branch temporarily while you look at a bug, you just use git stash. It's a stack you can push and pop.
russ@tuonela:~/git/hgw> echo bigchange >> matthew.txt russ@tuonela:~/git/hgw> git status # On branch feature1 # Changed but not updated: # (use "git add..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: matthew.txt # no changes added to commit (use "git add" and/or "git commit -a") russ@tuonela:~/git/hgw> git stash Saved working directory and index state WIP on feature1: 45f1da9 Merge remote branch 'origin' into feature1 HEAD is now at 45f1da9 Merge remote branch 'origin' into feature1
All modified files are committed and captured to this number (45f1da9).
russ@tuonela:~/git/hgw> vim matthew.txt russ@tuonela:~/git/hgw> git add matthew.txt russ@tuonela:~/git/hgw> git commit -m"Bug fix" [feature1 79e6049] Bug fix 1 files changed, 1 insertions(+), 0 deletions(-) russ@tuonela:~/git/hgw> git status # On branch feature1 nothing to commit (working directory clean) russ@tuonela:~/git/hgw> git stash pop Auto-merging matthew.txt # On branch feature1 # Changed but not updated: # (use "git add..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: matthew.txt # no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (688ae82eae3e5190e31d9bf16c82ec1d52b6c0e6)
It merges back into what's there (say, because I did fix a bug and want to keep it).
russ@tuonela:~/git/hgw> cat matthew.txt Bug fix Hello Matthew again from the web//newchange asdf;lkjasdf asd fasdf asd fasd fas df bigchange russ@tuonela:~/git/hgw> git stash list russ@tuonela:~/git/hgw> git stash Saved working directory and index state WIP on feature1: 79e6049 Bug fix HEAD is now at 79e6049 Bug fix russ@tuonela:~/git/hgw> git stash list stash@{0}: WIP on feature1: 79e6049 Bug fix russ@tuonela:~/git/hgw> git stash pop # On branch feature1 # Changed but not updated: # (use "git add..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: matthew.txt # no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (e7ed522a98942295e02b874394336a731b4fb9b1) russ@tuonela:~/git/hgw> git add matthew.txt russ@tuonela:~/git/hgw> git commit -m"Kept bug fix" [feature1 ec52f49] Kept bug fix 1 files changed, 1 insertions(+), 0 deletions(-) russ@tuonela:~/git/hgw> git status # On branch feature1 nothing to commit (working directory clean)
This is where you wait as long as you can to branch, work as hard as you can to finish before there's much deviation from what you started with, then commit quickly to avoid having so much to merge afterward. rebase allows you to pretend that nothing changed while you were working separately.
russ@tuonela:~/git/hgw> git pull remote: Counting objects: 7, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 1), reused 3 (delta 1) Unpacking objects: 100% (3/3), done. From github.com:matthew/hellogitworld b0b59d2..afd2bf1 master -> origin/master You asked me to pull without telling me which branch you want to merge with, and 'branch.feature1.merge' in your configuration file does not tell me, either. Please specify which branch you want to use on the command line and try again (e.g. 'git pull'). See git-pull(1) for details. If you often merge with the same branch, you may want to use something like the following in your configuration file: [branch "feature1"] remote = merge = [remote " "] url = fetch = See git-config(1) for details. russ@tuonela:~/git/hgw> cd .. russ@tuonela:~/git> ll total 7 drwxr-xr-x 5 russ russ 160 2011-08-16 13:21 . drwxr-xr-x 78 russ russ 3304 2011-08-16 15:14 .. drwxr-xr-x 5 russ russ 216 2011-08-16 13:19 hellogitworld drwxr-xr-x 6 russ russ 312 2011-08-16 15:16 hgw -rwxr-xr-x 1 russ russ 45 2011-08-16 10:07 mygitssh.sh drwxr-xr-x 6 russ russ 248 2011-08-16 13:06 project1 russ@tuonela:~/git> rm -rf hellogitworld/ russ@tuonela:~/git> git clone [email protected]:matthew/hellogitworld.git git clone [email protected]:matthew/hellogitworld.git Initialized empty Git repository in /home/russ/git/hellogitworld/.git/ remote: Counting objects: 3909, done. remote: Compressing objects: 100% (100/100), done. remote: Total 3909 (delta 80), reused 3876 (delta 53) Receiving objects: 100% (3909/3909), 231.03 KiB, done. Resolving deltas: 100% (80/80), done. russ@tuonela:~/git> ll total 7 drwxr-xr-x 5 russ russ 160 2011-08-16 15:26 . drwxr-xr-x 78 russ russ 3304 2011-08-16 15:14 .. drwxr-xr-x 6 russ russ 312 2011-08-16 15:26 hellogitworld drwxr-xr-x 6 russ russ 312 2011-08-16 15:16 hgw -rwxr-xr-x 1 russ russ 45 2011-08-16 10:07 mygitssh.sh drwxr-xr-x 6 russ russ 248 2011-08-16 13:06 project1 russ@tuonela:~/git> cd hellogitworld/ russ@tuonela:~/git/hellogitworld> vim matthew.txt russ@tuonela:~/git/hellogitworld> git add . russ@tuonela:~/git/hellogitworld> git commit -m"Matthews change" [master cd02e29] Matthews change 1 files changed, 2 insertions(+), 1 deletions(-) russ@tuonela:~/git/hellogitworld> git pull --rebase remote: Counting objects: 12, done. remote: Compressing objects: 100% (4/4), done. remote: Total 8 (delta 4), reused 6 (delta 2) Unpacking objects: 100% (8/8), done. From github.com:matthew/hellogitworld 4d6232e..3077ac8 master -> origin/master First, rewinding head to replay your work on top of it... Applying: Matthews change
(We just re-cloned to start afresh for this example.)
russ@tuonela:~/git/hellogitworld> git checkout master Already on 'master' Your branch is ahead of 'origin/master' by 1 commit. russ@tuonela:~/git/hellogitworld> git branch * master russ@tuonela:~/git/hellogitworld> git stash No local changes to save russ@tuonela:~/git/hellogitworld> git checkout master Already on 'master' Your branch is ahead of 'origin/master' by 1 commit. russ@tuonela:~/git/hellogitworld> git pull --rebase remote: Counting objects: 7, done. remote: Compressing objects: 100% (1/1), done. remote: Total 4 (delta 2), reused 4 (delta 2) Unpacking objects: 100% (4/4), done. From github.com:matthew/hellogitworld 3077ac8..8f17bb1 master -> origin/master First, rewinding head to replay your work on top of it... Applying: Matthews change russ@tuonela:~/git/hellogitworld> git checkout master Already on 'master' Your branch is ahead of 'origin/master' by 1 commit. russ@tuonela:~/git/hellogitworld> git checkout -f master Already on 'master' Your branch is ahead of 'origin/master' by 1 commit.
Is rebasing dangerous? Not for what we're doing here. We're changing history, pulling changes off to the side, retrieving changes from the server, then layering our changes on top.
No one else on the team has seen our changes.
pull --rebase calculates what hasn't been published, pulls those off to the side, grabs the new stuff... It's like bumpers at the bowling alley.
Don't do this against code history already shared with the team.
russ@tuonela:~/git/hellogitworld> git pull rebase fatal: 'rebase' does not appear to be a git repository fatal: The remote end hung up unexpectedly russ@tuonela:~/git/hellogitworld> git pull --rebase remote: Counting objects: 40, done. remote: Compressing objects: 100% (20/20), done. remote: Total 33 (delta 14), reused 24 (delta 5) Unpacking objects: 100% (33/33), done. From github.com:matthew/hellogitworld 8f17bb1..ed193ca master -> origin/master First, rewinding head to replay your work on top of it... Applying: Matthews change russ@tuonela:~/git/hellogitworld> get pull The program 'get' is currently not installed. You can install it by typing: sudo apt-get install astk russ@tuonela:~/git/hellogitworld> git pull Already up-to-date. russ@tuonela:~/git/hellogitworld> git log commit 650c28f94f654ffcc5588cc7ffef07992feaa0c2 Author: Russell BatemanDate: Tue Aug 16 15:27:23 2011 -0600 Matthews change commit ed193cadf299ee7bd2f4bc2538a59b136e53c040 Merge: cc5007b feb6909 Author: Matthew Date: Tue Aug 16 14:43:22 2011 -0700 Merge pull request #26 from damienf/master please merge in my file commit cc5007b139d42c1dc034a481ea16b447f7c31927 Merge: 1057a9e 79bb2ee Author: Matthew Date: Tue Aug 16 14:41:04 2011 -0700 Merge pull request #29 from davedkg/master yea commit 79bb2eeb7fe227d8553ecd15102cd1725c76888d Author: David Guilfoyle Date: Tue Aug 16 14:38:47 2011 -0700 Edited resources/davedkg.txt via GitHub commit 1057a9eafce9f1841ee224559abeb39cf127505b Author: Matthew Date: Tue Aug 16 15:38:32 2011 -0600 Edited README.txt via GitHub commit feb690915cabc14b82c061602644446de0ad9a91 Author: Damien Filiatrault Date: Tue Aug 16 14:38:13 2011 -0700 Edited damien.txt via GitHub commit fd68bc396df9836b3fab3bbf2a4f681a7fef887c Author: Sally Shepard Date: Tue Aug 16 22:29:45 2011 +0100 modified sally.txt commit 09b51b0cbda6de4a1be81252582c5f8b1a54a74b Author: David Guilfoyle Date: Tue Aug 16 14:26:22 2011 -0700 DKG change commit ab1be5ba5e4209b4d9143cf66ede447f8defb920 Author: Chris Kinnan Date: Tue Aug 16 17:29:23 2011 -0400 Chris More Changes commit 3276490e9b57d466042ad383a7bff94c3763aaba russ@tuonela:~/git/hellogitworld> clear ; git log -3 commit 650c28f94f654ffcc5588cc7ffef07992feaa0c2 Author: Russell Bateman Date: Tue Aug 16 15:27:23 2011 -0600 Matthews change commit ed193cadf299ee7bd2f4bc2538a59b136e53c040 Merge: cc5007b feb6909 Author: Matthew Date: Tue Aug 16 14:43:22 2011 -0700 Merge pull request #26 from damienf/master please merge in my file commit cc5007b139d42c1dc034a481ea16b447f7c31927 Merge: 1057a9e 79bb2ee Author: Matthew Date: Tue Aug 16 14:41:04 2011 -0700 Merge pull request #29 from davedkg/master yea russ@tuonela:~/git/hellogitworld> git revert 650c28 Finished one revert. [master 4777cf3] Revert "Matthews change" Don't like Matthew's change. 1 files changed, 1 insertions(+), 2 deletions(-) russ@tuonela:~/git/hellogitworld> git mergetool merge tool candidates: meld opendiff kdiff3 tkdiff xxdiff tortoisemerge gvimdiff diffuse ecmerge p4merge araxis emerge vimdiff No files need merging russ@tuonela:~/git/hellogitworld> git status # On branch master # Your branch is ahead of 'origin/master' by 2 commits. # nothing to commit (working directory clean) russ@tuonela:~/git/hellogitworld> git push To [email protected]:matthew/hellogitworld.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to '[email protected]:matthew/hellogitworld.git' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes before pushing again. See the 'Note about fast-forwards' section of 'git push --help' for details.
Here's how to amend a bad commit message.
russ@tuonela:~/git/hellogitworld> echo stuff > file1.txt russ@tuonela:~/git/hellogitworld> echo stuff > file2.txt russ@tuonela:~/git/hellogitworld> echo stuff > file3.txt russ@tuonela:~/git/hellogitworld> git add file1.txt file2.txt russ@tuonela:~/git/hellogitworld> git commit -m "Adduh teh changes" [master 073684c] Adduh teh changes 2 files changed, 2 insertions(+), 0 deletions(-) create mode 100644 file1.txt create mode 100644 file2.txt russ@tuonela:~/git/hellogitworld> git commit amend error: pathspec 'amend' did not match any file(s) known to git. russ@tuonela:~/git/hellogitworld> git commit --amend [master 6d1a996] Added the changes 2 files changed, 2 insertions(+), 0 deletions(-) create mode 100644 file1.txt create mode 100644 file2.txt
amend let's you fix the comments; don't do this to a pushed repository.
russ@tuonela:~/git/hellogitworld> git add file3.txt russ@tuonela:~/git/hellogitworld> git commit --amend [master 58fc6d2] Added the changes 3 files changed, 3 insertions(+), 0 deletions(-) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt
Merged the contents of the commit we forgot with the earlier commit.
russ@tuonela:~/git/hellogitworld> git push To [email protected]:matthew/hellogitworld.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to '[email protected]:matthew/hellogitworld.git' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes before pushing again. See the 'Note about fast-forwards' section of 'git push --help' for details.
Never change history that's already been written. So, don't amend a commit that have already been pushed. Now we can push changes...
russ@tuonela:~/git/hellogitworld> git reflog 58fc6d2 HEAD@{0}: commit (amend): Added the changes 6d1a996 HEAD@{1}: commit (amend): Added the changes 073684c HEAD@{2}: commit: Adduh teh changes 4777cf3 HEAD@{3}: commit: Revert "Matthews change" 650c28f HEAD@{4}: pull --rebase: Matthews change ed193ca HEAD@{5}: checkout: moving from master to ed193cadf299ee7bd2f4bc2538a59b136e53c040^0 612002f HEAD@{6}: checkout: moving from master to master 612002f HEAD@{7}: checkout: moving from master to master 612002f HEAD@{8}: pull --rebase: Matthews change 8f17bb1 HEAD@{9}: checkout: moving from master to 8f17bb160a4092855bd6006feae4c2f6063ca54d^0 e385ecd HEAD@{10}: checkout: moving from master to master e385ecd HEAD@{11}: checkout: moving from master to master e385ecd HEAD@{12}: pull --rebase: Matthews change 3077ac8 HEAD@{13}: checkout: moving from master to 3077ac8753267de70101afb8396df7668fca320b^0 cd02e29 HEAD@{14}: commit: Matthews change 4d6232e HEAD@{15}: clone: from [email protected]:matthew/hellogitworld.git
Thinking:
Last 60 days of goings-on.
Just MY things.
See two amends at top.
Roll back to that place in history?
russ@tuonela:~/git/hellogitworld> git reset --soft 6d1a996
I just reset my entire working tree to how it was back then.
russ@tuonela:~/git/hellogitworld> git reflog 6d1a996 HEAD@{0}: 6d1a996: updating HEAD 58fc6d2 HEAD@{1}: commit (amend): Added the changes 6d1a996 HEAD@{2}: commit (amend): Added the changes 073684c HEAD@{3}: commit: Adduh teh changes 4777cf3 HEAD@{4}: commit: Revert "Matthews change" 650c28f HEAD@{5}: pull --rebase: Matthews change ed193ca HEAD@{6}: checkout: moving from master to ed193cadf299ee7bd2f4bc2538a59b136e53c040^0 612002f HEAD@{7}: checkout: moving from master to master 612002f HEAD@{8}: checkout: moving from master to master 612002f HEAD@{9}: pull --rebase: Matthews change 8f17bb1 HEAD@{10}: checkout: moving from master to 8f17bb160a4092855bd6006feae4c2f6063ca54d^0 e385ecd HEAD@{11}: checkout: moving from master to master e385ecd HEAD@{12}: checkout: moving from master to master e385ecd HEAD@{13}: pull --rebase: Matthews change 3077ac8 HEAD@{14}: checkout: moving from master to 3077ac8753267de70101afb8396df7668fca320b^0 cd02e29 HEAD@{15}: commit: Matthews change 4d6232e HEAD@{16}: clone: from [email protected]:matthew/hellogitworld.git
(Even though it doesn't show it.)
russ@tuonela:~/git/hellogitworld> git reset --hard 6d1a996 HEAD is now at 6d1a996 Added the changes russ@tuonela:~/git/hellogitworld> git reflog 6d1a996 HEAD@{0}: 6d1a996: updating HEAD 58fc6d2 HEAD@{1}: commit (amend): Added the changes 6d1a996 HEAD@{2}: commit (amend): Added the changes 073684c HEAD@{3}: commit: Adduh teh changes 4777cf3 HEAD@{4}: commit: Revert "Matthews change" 650c28f HEAD@{5}: pull --rebase: Matthews change ed193ca HEAD@{6}: checkout: moving from master to ed193cadf299ee7bd2f4bc2538a59b136e53c040^0 612002f HEAD@{7}: checkout: moving from master to master 612002f HEAD@{8}: checkout: moving from master to master 612002f HEAD@{9}: pull --rebase: Matthews change 8f17bb1 HEAD@{10}: checkout: moving from master to 8f17bb160a4092855bd6006feae4c2f6063ca54d^0 e385ecd HEAD@{11}: checkout: moving from master to master e385ecd HEAD@{12}: checkout: moving from master to master e385ecd HEAD@{13}: pull --rebase: Matthews change 3077ac8 HEAD@{14}: checkout: moving from master to 3077ac8753267de70101afb8396df7668fca320b^0 cd02e29 HEAD@{15}: commit: Matthews change 4d6232e HEAD@{16}: clone: from [email protected]:matthew/hellogitworld.git russ@tuonela:~/git/hellogitworld> git log commit 6d1a9967a4518cf9d059d4f672467c9dc601a9f6 Author: Russell BatemanDate: Tue Aug 16 15:50:39 2011 -0600 Added the changes commit 4777cf31ab7ade9dea27910a9d6347b522dca58f Author: Russell Bateman Date: Tue Aug 16 15:48:17 2011 -0600 Revert "Matthews change" Don't like Matthew's change. This reverts commit 650c28f94f654ffcc5588cc7ffef07992feaa0c2. commit 650c28f94f654ffcc5588cc7ffef07992feaa0c2 Author: Russell Bateman Date: Tue Aug 16 15:27:23 2011 -0600 Matthews change commit ed193cadf299ee7bd2f4bc2538a59b136e53c040 Merge: cc5007b feb6909 Author: Matthew Date: Tue Aug 16 14:43:22 2011 -0700 Merge pull request #26 from damienf/master please merge in my file commit cc5007b139d42c1dc034a481ea16b447f7c31927 Merge: 1057a9e 79bb2ee Author: Matthew Date: Tue Aug 16 14:41:04 2011 -0700 Merge pull request #29 from davedkg/master yea commit 79bb2eeb7fe227d8553ecd15102cd1725c76888d Author: David Guilfoyle Date: Tue Aug 16 14:38:47 2011 -0700 Edited resources/davedkg.txt via GitHub commit 1057a9eafce9f1841ee224559abeb39cf127505b Author: Matthew Date: Tue Aug 16 15:38:32 2011 -0600 Edited README.txt via GitHub commit feb690915cabc14b82c061602644446de0ad9a91 Author: Damien Filiatrault Date: Tue Aug 16 14:38:13 2011 -0700 Edited damien.txt via GitHub commit fd68bc396df9836b3fab3bbf2a4f681a7fef887c Author: Sally Shepard Date: Tue Aug 16 22:29:45 2011 +0100 modified sally.txt commit 09b51b0cbda6de4a1be81252582c5f8b1a54a74b Author: David Guilfoyle Date: Tue Aug 16 14:26:22 2011 -0700 DKG change commit ab1be5ba5e4209b4d9143cf66ede447f8defb920 Author: Chris Kinnan Date: Tue Aug 16 17:29:23 2011 -0400 Chris More Changes commit 3276490e9b57d466042ad383a7bff94c3763aaba Author: Valerio Bettini Date: Tue Aug 16 22:29:59 2011 +0100 valerio edit commit 236e56ddadddcdc7aea0ac8cdd6b67da9359bfac Author: Damien Filiatrault Date: Tue Aug 16 14:28:55 2011 -0700 little change commit 8f17bb160a4092855bd6006feae4c2f6063ca54d Author: Chris Kinnan Date: Tue Aug 16 17:27:08 2011 -0400 Chris Change commit 3077ac8753267de70101afb8396df7668fca320b Author: Endris Taylor Date: Tue Aug 16 16:26:41 2011 -0500 made a change commit f106535bf94a193de5e7d494f4257ea8bc7280ef Author: William Himmelstoss Date: Tue Aug 16 17:26:36 2011 -0400 William's change commit 4d6232e611a7604305ed0d1176b2d7e5e46e0a21 Author: Matthew Date: Tue Aug 16 15:26:04 2011 -0600 Matthews change commit d097ca448d3b612d7c245b26644b71e27abe1bdc Author: Matthew Date: Tue Aug 16 15:14:41 2011 -0600 Big fix commit afd2bf1f3536a58adf60e9e913e91e39a08b561c Merge: b0b59d2 2a0ddd5 Author: Matthew Date: Tue Aug 16 15:00:59 2011 -0600
reflog is a forward-rolling log. What if I didn't mean to reset?
russ@tuonela:~/git/hellogitworld> git reflog 6d1a996 HEAD@{0}: 6d1a996: updating HEAD 58fc6d2 HEAD@{1}: commit (amend): Added the changes 6d1a996 HEAD@{2}: commit (amend): Added the changes 073684c HEAD@{3}: commit: Adduh teh changes 4777cf3 HEAD@{4}: commit: Revert "Matthews change" 650c28f HEAD@{5}: pull --rebase: Matthews change ed193ca HEAD@{6}: checkout: moving from master to ed193cadf299ee7bd2f4bc2538a59b136e53c040^0 612002f HEAD@{7}: checkout: moving from master to master 612002f HEAD@{8}: checkout: moving from master to master 612002f HEAD@{9}: pull --rebase: Matthews change 8f17bb1 HEAD@{10}: checkout: moving from master to 8f17bb160a4092855bd6006feae4c2f6063ca54d^0 e385ecd HEAD@{11}: checkout: moving from master to master e385ecd HEAD@{12}: checkout: moving from master to master e385ecd HEAD@{13}: pull --rebase: Matthews change 3077ac8 HEAD@{14}: checkout: moving from master to 3077ac8753267de70101afb8396df7668fca320b^0 cd02e29 HEAD@{15}: commit: Matthews change 4d6232e HEAD@{16}: clone: from [email protected]:matthew/hellogitworld.git russ@tuonela:~/git/hellogitworld> git reset --hard 4d6232e HEAD is now at 4d6232e Matthews change
Untracked files would be damaged if I merged in this thing from history. Git always tries to be safe:
Git reasons, "If I merge this in, I can't help you get back to the right state."
Matthew authors $30 videos through O'Reilly on topics such as Git.