Week 2 Tuesday

— Christopher Genovese and Alex Reinhart

Announcements #

  • GitHub Bug and Plan B (success!?)
  • Office hours: Wed 3
  • This Week’s Plan

Plan for Today #

  • Quick Review of Version Control
  • Git demo
  • Unit Testing [next time]

Quick Review of Version Control #

  • The Three Trees
    • Staging: Working Tree => Index
    • Committing: Index => HEAD
    • Checkout: HEAD => Working Tree
  • Common Git Commands
    git status Get current state of repo and three trees
    git log Look at repository history
    git diff See what has changed between commits
    git init Initialize a new repository
    git add Stage one or more files
    git branch Create and manage branches
    git switch Switch branches
    git checkout Checkout a commit
    git commit Commit the current index
    git merge Combine changes from multiple commits
    git remote Associate repo with a remote repo
    git push Send new commits to a remote copy of this repo
    git fetch/pull Receive new commits from a remote repo
    git worktree Work on multiple branches at the same time
  • Good Practice
    • Good Commit Messages (think of future you!)
    • Commit Often
    • Use and merge branches to explore alternative lines of development

Git Demo (Learning By Doing) #

Cloning the Repo #

  1. Clone (download) an existing repository:

    cd ~/s750
    git clone https://github.com/36-750/git-demo.git
    cd git-demo/            # move into the cloned repository
    git status              # check the status
    ls -a                   # observe the .git hidden directory
    
  2. Open the git-demo folder in Explorer or Finder or whatever you use on your computer to find files. Look – it’s the same stuff.

A Quick Look at the Plumbing #

  1. Every git object (blobs, trees, commits, and tags) is stored as an indexed piece of data in the .git directory, indexed by its hash value.

    ls .git
    ls .git/objects
    echo 'We can add objects at will' | git hash-object -w --stdin  # creates a new object
    # prints hash =># 312b5389b6c43a4bf8867f6c607fcf2efd67824f
    ls .git/objects/31   # first two digits of the hash is a directory
    git cat-file -p 312b5389b6c43a4bf8867f6c607fcf2efd67824f   # use your hash if different
    
  2. HEAD points to a commit, refs

    cat .git/HEAD             # shows a "ref", a friendly alias for a commit
    cat .git/refs/heads/main  # this contains the (less friendly) hash
    git cat-file -p 655eca13a3622464056a0d5a1d60cccd582aebe6  # what is in the commit?
    
  3. We can see the tree at the current main branch

    git cat-file -p 'main^{tree}'    # Don't forget the quotes
    
  4. Immutable structure sharing in action

    echo 'version 1' > test.txt
    git hash-object -w test.txt  #=> 83baae61804e65cc73a7201a7252750c76066a30
    echo 'version 2' > test.txt
    git hash-object -w test.txt  #=> 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
    find .git/objects -type f    # both versions are there!
    rm test.txt
    git status                   # Not a visible change because no file
    git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt
    cat test.txt
    git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
    cat test.txt
    rm test.txt
    

Basic Operations #

  1. Make a branch and switch to it.

    git branch your-clever-name-here  # Create a branch
    git branch                        # Look at available branches, main still current
    git switch your-clever-name-here  # Switch to the branch
    git status
    

    We are now working on the branch, but since we haven’t changed it, main and your-clever-name-here are pointing to the same commit.

  2. Advance the your-clever-name-here branch by two commits

    git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt
    git status       # see what is highlighted
    git switch main  # test.txt is 'untracked', so when we switch it 'follows'
    git switch your-clever-name-here
    git add test.txt # staging test.txt
    git status       # note the differnce
    git commit -m 'meta: added a test version descriptor'
    git status
    git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
    git add test.txt # staging test.txt, you need to stage again (cf. Three Trees)
    git diff         # Look at the differences
    git commit -m 'meta: updated test version descriptor'
    git status
    git log --oneline --color --graph --branches
    
  3. Change your work but try to switch before committing

    echo 'version 3' > test.txt
    git switch main  # oops
    git add test.txt
    git commit       # try out your editor config, enter comit message and close the file
    git switch main  # good now
    git log --oneline --color --graph --branches
    

    Look at your working directory in this branch. Switch back and forth and compare. Go back to main.

  4. Create a new branch named YOURNAME-demo off main and switch to it. (Replace YOURNAME with your github username.)

    git branch YOURNAME-demo
    git switch YOURNAME-demo
    

    This will be our reference branch for this exercise, playing the role that main would play in practice. (Avoids conflicts

  5. Make a new branch and switch to it.

    Make some changes to your repository. Add files, edit something, whatever. Stage and commit the files. Do it one more time, two commits on the new branch.

    Switch back to YOURNAME-demo.

    git log --oneline --color --graph --branches
    
  6. Merge the your-clever-name-here branch back into YOURNAME-demo. This should have no conflicts.

    git merge will merge the specified branch into the current branch.

    git switch YOURNAME-demo
    git status  # check we are where we think we are!!
    # if we were working with a remote, git pull to ensure we have the latest version
    git merge your-clever-name-here
    git log --oneline --color --graph --branches
    

    You could delete the old branch if you like with git branch -d, though you do not have to. For today, keep it for later.

  7. Make a new branch off YOURNAME-demo, switch to it, and change an existing file, commit. Switch back to YOURNAME-demo and change that same file. Commit. Merge the new branch into YOURNAME-demo. Suppose you called the branch conflict.

    git switch YOURNAME-demo
    git status
    git merge conflict
    # Surprise... conflict?
    

    Look at the conflicting file. Edit out the markers and commit it. The merge will continue. You can abort a merge with git merge --abort. Note: you have to stage the edited file.

    git log --oneline --color --graph --branches
    
  8. In YOURNAME-demo, add some changes to the index, so they are staged to be committed. Then commit the changes.

    git status
    git add file_you_changed.py
    git commit
    
  9. Make more changes and stage them.

  10. Look at differences

    git log --oneline --color --graph --branches
    git diff 3597a84 e3f8f5d           # replace any two hashes you want to compare
    git diff # how is this diff different :?)
    

    Commit if you like.

  11. Push this branch to the remote repository (on GitHub)

    git push --set-upstream origin YOURNAME-demo
    

    The --set-upstream option is only necessary once, to tell Git that the “upstream” for this branch – the remote location for it – is the corresponding branch on GitHub, which will be created automatically.

  12. Switch back to the main branch:

    git switch main
    ls
    

    Now look at the files in your repository. Notice they’ve all changed back to what they looked like before you switched to your branch.

  13. Look at the status

    git status
    git log --oneline --color --graph --branches
    
  14. Make a pull request on GitHub

    https://github.com/36-750/git-demo

This is similar to the workflow we’ll use for submitting homework. We’ll provide you a script that automates part of it, but it’s good to know basic Git operations so you know what’s going on when you switch branches and submit homework.

Work Trees #

Working with branches can sometimes be tricky because one has changes in progress that keep you from switching branches when you want to.

git-worktree simulates working with multiple branches simultaneously by storing linked repositories in another directory. Start in main.

  1. Pick two of the branches you created in the demo above. I’ll call them foo and bar. Create work trees

    mkdir ../worktrees-git-demo
    git worktree add ../worktrees-git-demo/foo foo
    git worktree add ../worktrees-git-demo/bar bar
    

    Branches foo and bar are now checked out in those directories.

  2. Move to one and commit some changes

    cd ../worktrees-git-demo/foo   # I like to keep my worktrees elsewhere but YMMV
    # make and commit changes
    git status
    

    If you go back to the main git-demo directory and try to git switch foo. It won’t let you; foo is already spoken for.

    cd ~/s750/git-demo
    git worktree remove ../worktrees-git-demo/foo
    git switch foo
    ls  # Notice the changes (in whatever way)
    git switch main
    

This is a useful workflow for your homework. For each branch you are working on, create a worktree and work there. E.g.,

cd ~/s750
mkdir exercises  # directory for your active exercises
cd assignments-YOURNAME
git worktree add ../exercises/ex-name ex-name
cd ../exercises/ex-name
# work on it

When done, remove the worktree. Create it again if you need to work on the exercise.

Resources #

We recommend trying the git-challenge homework assignment and referring to the resources above as you do it, so you understand each step you’re taking.