Working With Patches in Git
As software developers, we often make local code changes for debugging, logging, and more. Oftentimes we are unable to
commit these changes to the master code as they may make the application run slower, or even render it unusable for
certain use cases. For lots of people that means the hacks have to be re-added every time they wish to debug something.
This is time consuming and error-prone; fortunately, git contains all the tools needed to solve this problem.
Let’s start by creating a new repository for adder
, a simple tool that adds two numbers:
git init adder
Now let’s add main.py
the file containing the full source of our application:
#!/usr/bin/env python
import os
import sys
def add(a, b):
return a + b
def main(args):
try:
sum = add(int(args[0]), int(args[1]))
print(f'the sum is {sum}')
except:
print('invalid arguments')
sys.exit(2)
if __name__ == '__main__':
main(sys.argv[1:])
Next, we stage, and commit the change:
git add main.py
git commit -m "Add application to add numbers"
Now let’s pretend that there is some sort of regression and we must add logging to investigate the issue. First, we need
to add our custom logging library located in a new file, logging.py
:
def info(msg):
print(msg)
We must also update the existing main.py
file to call into this library:
#!/usr/bin/env python
import os
import sys
import logging
def add(a, b):
logging.info(f'adding numbers {a} and {b}')
return a + b
def main(args):
try:
sum = add(int(args[0]), int(args[1]))
print(f'the sum is {sum}')
except:
print('invalid arguments')
sys.exit(2)
if __name__ == '__main__':
main(sys.argv[1:])
With our local changes complete, let’s review the differences against the master
branch.
git diff
The output of the above command is a valid patch file in of itself:
index 7efa5d6..d3aadba 100755
--- a/main.py
+++ b/main.py
@@ -2,8 +2,10 @@
import os
import sys
+import logging
def add(a, b):
+ logging.info(f'adding numbers {a} and {b}')
return a + b
def main(args):
While the changes to main.py
are shown, the contents of the newly added logging.py
file are not. To fix this, simply
add it with:
git add --intent-to-add logging.py
With this, both files are now correctly shown as changes:
diff --git a/logging.py b/logging.py
new file mode 100644
index 0000000..39b4bbc
--- /dev/null
+++ b/logging.py
@@ -0,0 +1,2 @@
+def info(msg):
+ print(msg)
diff --git a/main.py b/main.py
index 7efa5d6..d3aadba 100755
--- a/main.py
+++ b/main.py
@@ -2,8 +2,10 @@
import os
import sys
+import logging
def add(a, b):
+ logging.info(f'adding numbers {a} and {b}')
return a + b
def main(args):
With the changes looking good, it’s time to generate the patch file:
git diff --output ~/logging.patch
Now that we have performing the required debugging and saved the day, we can revert all of our local debugging changes:
git reset --hard
Let’s now pretend that some time has passed and we must debug the add
function once again. Furthermore, as a result of
feature creep, the adder
tool now also has to handle subtraction. The updated main.py
file is shown below:
import os
import sys
def add(a, b):
return a + b
def main(args):
try:
op = args[0]
a = int(args[1])
b = int(args[2])
ops = {'+': add, '-': subtract}
result = ops[op](a, b)
print(f'the result is {result}')
except:
print('invalid arguments')
sys.exit(2)
def subtract(a, b):
return a - b
if __name__ == '__main__':
main(sys.argv[1:])
As there are no conflicts in the modified chunks, we can now apply the patch:
git apply ~/logging.patch
We can see that the logging.py
file is created and main.py
has been updated with the logging from before:
import os
import sys
def add(a, b):
return a + b
def main(args):
try:
op = args[0]
a = int(args[1])
b = int(args[2])
ops = {'+': add, '-': subtract}
result = ops[op](a, b)
print(f'the result is {result}')
except:
print('invalid arguments')
sys.exit(2)
def subtract(a, b):
return a - b
if __name__ == '__main__':
main(sys.argv[1:])
If you do run into conflicts, you can use the
reject flag to apply as many changes as
possible, without failing the entire operation.