10. How can git help me?

So far we have seen git add a bunch of steps to a workflow and worked to understand what it is doing. I’ve told you tracks versions and can help you out because keeping track of versions is good.

Today I will show you how it can help.

we are going to work in out github-inclass repo today. Let’s reveiw its status.

git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	a_file
	b_file
	gitlog.txt
	test_file.md

nothing added to commit but untracked files present (use "git add" to track)

In mine, I have these extra files I don’t need from testing things. How can I get rid of them since I do not want to keep them?

We see that they are in the files along with the other files that we planned to be here. Remember the “scenario” of this repo is that it has a number of mostly empty files to represent a python project.

ls
CONTRIBUTING.md	README.md	about.md	docs		mymodule	test_file.md
LICENSE.md	a_file		b_file		gitlog.txt	setup.py	tests

Could it be git pull?

git pull
Already up to date.

No git pull, gets, from the origin, the most up to date version of the git database (.git directory) from the origin (in this case github). It does not, however do anything to untracked files.

We can remove them:

rm *_file
rm gitlog.txt
rm test_file.md

Then we can confirm the git status of the repo.

git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

10.1. What if I make a file that I don’t want to commit?

Imagine we made a configuration file that contained some text (for example a personal key of some sort) that we need in the directory to make our code work locally, but that we do not want to push to GitHub?

echo "secrets" >> config.txt

The way to do that is to not put it into git at all, so that when we push it will not go.

As usual, before we do anything with git, we check the status

git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	config.txt

nothing added to commit but untracked files present (use "git add" to track)

In this case, we see that git has the file here, but it is untracked.

Recall, git status has the git program check the HEAD file to determine what in the databaes to look at and then compares the current directory status to what the corresponding last hash contained.

Git provides a special file that it will check when we add, and then not add any files that match the files in.

nano .gitignore

We will add the following to the file:

config.*

Then write and exit from nano.

Now our status has changed:

git status
On branch main
Your branch is up to date with 'origin/main'.

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)

We see the config file is not in the untracked anymore (that means it will not be added if we do git add) but the gitignore file is.

We want to commit this file becuase we would want collaborators to share the same ignored patterns.

git add .
git commit -m 'ignore configs'
[main 3c46e01] ignore configs
 1 file changed, 1 insertion(+)
 create mode 100644 .gitignore

Now, what is the advantage of using the pattern?

If we make another file that starts with “config”

echo "more secrets" >> config.yml

and then check the status

git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

git does not see it at all.

we can see though that the files are boht there in our directory on disk.

ls
CONTRIBUTING.md	README.md	config.txt	docs		setup.py
LICENSE.md	about.md	config.yml	mymodule	tests

What happens if we try to explicitly add it?

git add config.txt

Let’s see:

The following paths are ignored by one of your .gitignore files:
config.txt
hint: Use -f if you really want to add them.
hint: Turn this message off by running
hint: "git config advice.addIgnoredFile false"

git tries to warn us.

Important

In general, if something you are trying to do requires -f or -hard in git or bash (or others) do not do it unless you are very confident that it is the right thing to do.

These options are generally only required for things are either hard or impossible to undo.

10.2. What if I break my code and commit the bad code?

Let’s imagine we wrote some code that does not actually work

echo "bad code" >> mymodule/important_classes.py

but we forgot to test it, so we add

git add .

and commit it:

git commit -m 'best feature ever'

now it is in our repository

[main 2c1c3e2] best feature ever
 1 file changed, 1 insertion(+)

Now we test the code, find out that it does not work. but we remember that it worked before this code we just added. We can view the commit history to get the hash of the last commit that worked.

git log
commit 2c1c3e2d7bebdbd238e517e07142151cdf5256f4 (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:00:50 2022 -0500

    best feature ever

commit 3c46e01c5b29ffa2ed9a76ff4e8c8f334c0c1bf2
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 12:54:45 2022 -0500

    ignore configs

commit cea6a93d576ecd042823fca24553a58a6cd6565b (origin/main, origin/HEAD)
Author: Sarah Brown <brownsarahm@uri.edu>
Date:   Thu Feb 3 16:42:12 2022 -0500

    try to prevernt repeated running

commit c15cf43b6807e172aaba7cf3b57adc7214b91082 (test)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 3 13:38:42 2022 -0500

    insclass 2-3

commit 17320fc6f26806eb7d1ffc62c23b3bf1361b58b2
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Tue Feb 1 13:32:46 2022 -0500


and then we can look at the difference between that version of important_classes.py and the current version:

git diff 3c46e0 important_classes.py

Git diff is what GitHub uses on the PR files changed tab. it takes the commit reference first and then the file to look at just the one files changes.

diff --git a/mymodule/important_classes.py b/mymodule/important_classes.py
index e69de29..90baf46 100644
--- a/mymodule/important_classes.py
+++ b/mymodule/important_classes.py
@@ -0,0 +1 @@
+bad code

From this, we can confirm that the only change in the most recent commit is the bad code, there is not any other good code we want to keep.

We can look back our git log above to get the hash for that last commit and we can use git revert to “undo” it.

git revert 2c1c3e

Then git asks us to confirm a commit message, and we get output like this, which looks like after a commit.

[main 8bb2df9] Revert "best feature ever"
 1 file changed, 1 deletion(-)

To see how git revert works lets look at our commit history again.

git log
commit 8bb2df91d371ab3f9245d49aee5bd0e5f95a9294 (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:09:56 2022 -0500

    Revert "best feature ever"

    This reverts commit 2c1c3e2d7bebdbd238e517e07142151cdf5256f4.

commit 2c1c3e2d7bebdbd238e517e07142151cdf5256f4
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:00:50 2022 -0500

    best feature ever

commit 3c46e01c5b29ffa2ed9a76ff4e8c8f334c0c1bf2
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 12:54:45 2022 -0500

    ignore configs

commit cea6a93d576ecd042823fca24553a58a6cd6565b (origin/main, origin/HEAD)
Author: Sarah Brown <brownsarahm@uri.edu>
Date:   Thu Feb 3 16:42:12 2022 -0500

    try to prevernt repeated running

commit c15cf43b6807e172aaba7cf3b57adc7214b91082 (test)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 3 13:38:42 2022 -0500

    insclass 2-3

...

git did not go back in history in this case, it looked at what changes were applied in the commit we were reverting, applied the inverse of them to the current place the head points and added a new commit for that.

To illustrate this more clearly, lets make two commits. First we’ll commit some code we will later realize is not good ot helper functions and then something good to alternative classes.

echo "heleper code" >> helper_functions.py
git add .
git commit -m 'bad code'

we see the message for that

[main e9c132f] bad code
 1 file changed, 1 insertion(+)

and the second change

echo "something good" >> alternative_classes.py
git add .
git commit -m 'some code'
[main a224c42] some code
 1 file changed, 1 insertion(+)

Now we view the commit history again and the code we want to change is one commit back.

git log
commit a224c42a9fd5ba19b55d1e5e4f34c411c8d519f3 (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:12:47 2022 -0500

    some code

commit e9c132f6922a4bfae5b21793a9982fe837db6643
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:12:17 2022 -0500

    bad code

commit 8bb2df91d371ab3f9245d49aee5bd0e5f95a9294
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:09:56 2022 -0500

    Revert "best feature ever"

    This reverts commit 2c1c3e2d7bebdbd238e517e07142151cdf5256f4.

commit 2c1c3e2d7bebdbd238e517e07142151cdf5256f4
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:00:50 2022 -0500

    best feature ever

commit 3c46e01c5b29ffa2ed9a76ff4e8c8f334c0c1bf2
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 12:54:45 2022 -0500

    ignore configs


We can use revert again

git revert e9c132

it behaves just as before, having us write a commit message and then commiting the “new” changes

[main b6437b4] Revert "bad code"
 1 file changed, 1 deletion(-)

Now when we view the commit history we see the bad code, the commit we wanted to keep after it and hten the reverted bad code.

git log
commit b6437b4174f69d79e769e296c5e0db5f38c78a55 (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:14:58 2022 -0500

    Revert "bad code"

    This reverts commit e9c132f6922a4bfae5b21793a9982fe837db6643.

commit a224c42a9fd5ba19b55d1e5e4f34c411c8d519f3
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:12:47 2022 -0500

    some code

commit e9c132f6922a4bfae5b21793a9982fe837db6643
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:12:17 2022 -0500

    bad code

commit 8bb2df91d371ab3f9245d49aee5bd0e5f95a9294
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:09:56 2022 -0500

    Revert "best feature ever"

    This reverts commit 2c1c3e2d7bebdbd238e517e07142151cdf5256f4.

commit 2c1c3e2d7bebdbd238e517e07142151cdf5256f4
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:00:50 2022 -0500


Important

What does this functionality tell us about how we should commit?

we want to commit:

  • for each “thought” that is cohesive and is a unit we might want to undo

  • we want to use descriptive commit messages so that we can use them to pick out what to undo

10.3. What if I try a bunch of stuff and it doesn’t work, but I notice before I commit?

Imagine, you tried out a new idea:

echo "test idea" >> mymodule/important_classes.py

and then you test and it does not work. You want to put your working directory back to match the last commit in order to start from scratch with a new idea.

git status

We can see that our new file is not staged for commit and the git status tells us how to put it back.

On branch main
Your branch is ahead of 'origin/main' by 6 commits.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   mymodule/important_classes.py

no changes added to commit (use "git add" and/or "git commit -a")

We can use git restore with the file name (path) of what to restore

git restore mymodule/important_classes.py

Now if we try something new again

echo "test idea again" >> mymodule/important_classes.py

10.4. How can git help me remember what I’m working on?

Imagine you worked some then walked away and forget what you were working on.
A diff can help you see what changes you have made since your las commit.

git diff mymodule/important_classes.py
diff --git a/mymodule/important_classes.py b/mymodule/important_classes.py
index e69de29..a8343bc 100644
--- a/mymodule/important_classes.py
+++ b/mymodule/important_classes.py
@@ -0,0 +1 @@
+test idea again

Now if we add the file, to commit.

git add .

If you are about to commit and forget what work you are about to commit or have trouble thinking of what to write as your commit message, you can use git diff --staged to see exactly what changes you are about to add.

git diff --staged

this outputs a diff between the index and the last commit.

diff --git a/mymodule/important_classes.py b/mymodule/important_classes.py
index e69de29..a8343bc 100644
--- a/mymodule/important_classes.py
+++ b/mymodule/important_classes.py
@@ -0,0 +1 @@
+test idea again

Note that it shows you what you are comparing e69de29 to a8343b and lists the files and both a summary @@ -0,0 +1 @@

and the actual line by line changes.

This can be more useful after we ahve made more changes after we staged some.

echo "doc new feature" >> README.md

and check the diff again

git diff --staged

we get the same output

diff --git a/mymodule/important_classes.py b/mymodule/important_classes.py
index e69de29..a8343bc 100644
--- a/mymodule/important_classes.py
+++ b/mymodule/important_classes.py
@@ -0,0 +1 @@
+test idea again

Thisis the same becuase it comares the staging area to the last commmit, not the current directory to the last commit.

git status

This shows that only the important_classes file is staged, not the readme chagnes.

On branch main
Your branch is ahead of 'origin/main' by 6 commits.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   mymodule/important_classes.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   README.md


10.5. How can update my feature branch when main gets updated?

Imagine you are working on a new feature for a the project, so you create a new branch

git checkout -b new_feature
Switched to a new branch 'new_feature'

and add your work.

echo "new feature" >> mymodule/important_classes.py

git status
On branch new_feature
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   mymodule/important_classes.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   README.md
	modified:   mymodule/important_classes.py


and we will commit the code here.

git add mymodule/important_classes.py
git commit -m 'new feature'
[new_feature e7a92d2] new feature
 1 file changed, 2 insertions(+)

Then you remember you had done work on the README that was supposed to be on main already, so we go back there and commit that file. Imagine this work is a bug fix that is unrelated to new feature and you want users and your co-workers to have before you finish the new feature.

git checkout main
M	README.md
Switched to branch 'main'
Your branch is ahead of 'origin/main' by 6 commits.
  (use "git push" to publish your local commits)

git status
On branch main
Your branch is ahead of 'origin/main' by 6 commits.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

and commit the readme here.

git add .

git commit -m 'update readme'
[main 07dbc9e] update readme
 1 file changed, 1 insertion(+)

We can view commit history to see that these two branches have similar, but not the same history.

git log
commit 07dbc9e484b92cdbc7b055883fb5e1bfaee645f1 (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:27:33 2022 -0500

    update readme

commit b6437b4174f69d79e769e296c5e0db5f38c78a55
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:14:58 2022 -0500

    Revert "good code"

    This reverts commit e9c132f6922a4bfae5b21793a9982fe837db6643.

commit a224c42a9fd5ba19b55d1e5e4f34c411c8d519f3
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:12:47 2022 -0500

    some code

commit e9c132f6922a4bfae5b21793a9982fe837db6643
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:12:17 2022 -0500

    good code

commit 8bb2df91d371ab3f9245d49aee5bd0e5f95a9294
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:09:56 2022 -0500

    Revert "best feature ever"


then to the other branch

git checkout new_feature
Switched to branch 'new_feature'

and commit history again

git log
commit e7a92d24de2d70c6122e28b55cd9066b504ae8c8 (HEAD -> new_feature)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:26:40 2022 -0500

    new feature

commit b6437b4174f69d79e769e296c5e0db5f38c78a55
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:14:58 2022 -0500

    Revert "good code"

    This reverts commit e9c132f6922a4bfae5b21793a9982fe837db6643.

commit a224c42a9fd5ba19b55d1e5e4f34c411c8d519f3
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:12:47 2022 -0500

    some code

commit e9c132f6922a4bfae5b21793a9982fe837db6643
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:12:17 2022 -0500

    good code

commit 8bb2df91d371ab3f9245d49aee5bd0e5f95a9294
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Thu Feb 24 13:09:56 2022 -0500

    Revert "best feature ever"


Now we want to get the changes from main into the new feature branch because those fixes impact our ability to test the new feature to see if it works right.

We can update the new_feature branch with rebase.

git rebase main
Successfully rebased and updated refs/heads/new_feature.

Rebase gets the current status of the main branch (which has a mostly shared commit history with new_feature and then applies all of the commits that are on new_feature but not main on top of the most recent version of main.

We can see than now the history of new_feature is everything on main +1 one commit, where before they had shared except the last commit, where each one had a different most recent commit.

Now, if we’re done with the new feature and it works, we can merge it into main.

git checkout main
Switched to branch 'main'
Your branch is ahead of 'origin/main' by 7 commits.
  (use "git push" to publish your local commits)

On main we have no current thigns to commit:

git status
On branch main
Your branch is ahead of 'origin/main' by 7 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

Git merge allows us to apply the commits from new_feature to main.

git merge new_feature
Updating 07dbc9e..19d30d1
Fast-forward
 mymodule/important_classes.py | 2 ++
 1 file changed, 2 insertions(+)

This applies the commits to new_feature that are not on main to the end of main.

git status
On branch main
Your branch is ahead of 'origin/main' by 8 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

10.6. What if the content diverges

Let’s make a new branch

git checkout -b hotfix
Switched to a new branch 'hotfix'

Then switch back to main

git checkout main
Switched to branch 'main'

and add more content to our main branch

echo "bold commit to main" >> mymodule/important_classes.py
echo "slow thoughtful fix" >> mymodule/important_classes.py

We can look at the version of the file we have here

cat mymodule/important_classes.py

we have the old content and our new two lines

test idea again
new feature
bold commit to main
slower more thoughtful fix

and commit.

git add .
git commit -m "working project"
[main b832b2a] working project
 1 file changed, 2 insertions(+)

Now we’ll go back to the feature branch

git checkout -b hotfix
Switched to branch 'hotfix'

and look at what’s there.

cat mymodule/important_classes.py

we don’t have those two new lines.

test idea again
new feature

Now we fix the old bug that’s not the new things. (maybe this was a different person working on this branch)

echo "fix older bug" >> mymodule/important_classes.py

and commit that

git add .

git commit -m 'fix other bug"
> '
[hotfix 3664aa0] fix other bug"
 1 file changed, 1 insertion(+)

Now we can compare the two versions.

cat mymodule/important_classes.py

we have 3 lines on the hotfix branch

test idea again
new feature
fix older bug

git checkout main
Switched to branch 'main'
Your branch is ahead of 'origin/main' by 9 commits.
  (use "git push" to publish your local commits)

cat mymodule/important_classes.py

and 4 on the main branch and the 3rd line is different on each.

test idea again
new feature
bold commit to main
slower more thoughtful fix

Now, we want to have all of those so we try to merge.

git merge hotfix
Auto-merging mymodule/important_classes.py
CONFLICT (content): Merge conflict in mymodule/important_classes.py
Automatic merge failed; fix conflicts and then commit the result.

but we see an error

nano mymodule/important_classes.py

in nano now we see the file looks different

test idea again
new feature
>>>>> HEAD
bold commit to main
slower more thoughtful fix
=======
fix older bug
<<<<<<< hotfix

Git did not know how to merge the two files together, so it makred the part it did not know how to handle. There’s the part from the HEAD (the branch we have checked out, in this case main)

to resolve the merge conflict, we edit the file to be what we want. In this casewe want both sets of changes. SO we edit to look as follows:

test idea again
new feature
bold commit to main
slower more thoughtful fix
fix older bug

Now git tells us that we need to fix the conflicts and commit ecause we have unmerged paths.

git status
On branch main
Your branch is ahead of 'origin/main' by 9 commits.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   mymodule/important_classes.py

no changes added to commit (use "git add" and/or "git commit -a")

git commit -m "keep all changes"
U	mymodule/important_classes.py
error: Committing is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm <file>'
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.

We have to add as usual

git add .

then we can commit.

git commit -m "keep all changes"
[main 62b943f] keep all changes

10.7. Prepare for next Class

  1. Check that jupyter book is installed on your computer and the windows advice

  2. Fix any open PRs you have that need to be rebased.

  3. In your github in class repo, create a series of commits that tell as story of how you might have made a mistake and fixed it. Use git log and redirects to write that log to a file, gitstory.md in your KWL repo and then annotate your story to add in any narrative that didn't fit in the commit messages. This could be that you made a mistake in your code and used git to recover or that you got your git database to an undesirable state and got it back on track.

10.8. More Practice

  1. Find an open source repository and look at the .gitignore file. In donotcommit.md reflect on what types of content typically get ignored and why you think they are ignored. If you can't figure out why, try to look it up.

  2. Create a second git story for the other type form your first (code mistake or git mistake) in gitstory2.md

  3. add something to the glossary, cheatsheet or a history sidebar to the notes.