Skip to content

Commit 62a1613

Browse files
committed
Added basic support for merging.
1 parent 0b435e5 commit 62a1613

File tree

5 files changed

+225
-12
lines changed

5 files changed

+225
-12
lines changed

tests/test-all.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33
./test-log.sh
44
./test-branch.sh
55
./test-branch-commit.sh
6-
./test-branch-switch.sh
6+
./test-branch-switch.sh
7+
./test-merge.sh
8+
./test-merge-up-to-date.sh
9+
./test-merge-fast-forward.sh

tests/test-merge-fast-forward.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
3+
tdir=$(mktemp -d)
4+
5+
mkdir -p ${tdir}/repo
6+
pushd ${tdir}/repo > /dev/null
7+
8+
tig init
9+
echo "Hello World" > file.txt
10+
sha1sum_1=$(tig commit "Initial content.")
11+
echo "Goodbye World" >> file.txt
12+
sha1sum_2=$(tig commit "Added goodbye world.")
13+
tig checkout -b mybranch ${sha1sum_2}
14+
15+
echo "More text" >> file.txt
16+
sha1sum_3=$(tig commit "Added more text.")
17+
18+
echo "Even More text" >> file.txt
19+
sha1sum_4=$(tig commit "Added even more text.")
20+
21+
tig checkout master
22+
tig merge mybranch > ${tdir}/output.txt
23+
24+
cat > ${tdir}/expected.txt <<EOF
25+
tig: fast-forward merge
26+
EOF
27+
28+
if diff ${tdir}/output.txt ${tdir}/expected.txt; then
29+
echo "$0: PASSED"
30+
else
31+
echo "$0: FAILED"
32+
fi
33+
34+
popd > /dev/null
35+
36+
rm -fr ${tdir}

tests/test-merge-up-to-date.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
3+
tdir=$(mktemp -d)
4+
5+
mkdir -p ${tdir}/repo
6+
pushd ${tdir}/repo > /dev/null
7+
8+
tig init
9+
echo "Hello World" > file.txt
10+
sha1sum_1=$(tig commit "Initial content.")
11+
echo "Goodbye World" >> file.txt
12+
sha1sum_2=$(tig commit "Added goodbye world.")
13+
tig checkout -b mybranch ${sha1sum_2}
14+
15+
tig checkout master
16+
echo "More text" >> file.txt
17+
sha1sum_3=$(tig commit "Added more text.")
18+
19+
echo "Even More text" >> file.txt
20+
sha1sum_4=$(tig commit "Added even more text.")
21+
22+
tig merge mybranch > ${tdir}/output.txt
23+
24+
cat > ${tdir}/expected.txt <<EOF
25+
tig: up-to-date
26+
EOF
27+
28+
if diff ${tdir}/output.txt ${tdir}/expected.txt; then
29+
echo "$0: PASSED"
30+
else
31+
echo "$0: FAILED"
32+
fi
33+
34+
popd > /dev/null
35+
36+
rm -fr ${tdir}

tests/test-merge.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
3+
tdir=$(mktemp -d)
4+
5+
mkdir -p ${tdir}/repo
6+
pushd ${tdir}/repo > /dev/null
7+
8+
tig init
9+
echo "Hello World" > file.txt
10+
sha1sum_1=$(tig commit "Initial content.")
11+
echo "Goodbye World" >> file.txt
12+
sha1sum_2=$(tig commit "Added goodbye world.")
13+
tig checkout -b mybranch ${sha1sum_1}
14+
15+
echo '0a
16+
Branched World
17+
.
18+
w' | ed file.txt 2> /dev/null
19+
sha1sum_3=$(tig commit "Added branched world.")
20+
21+
tig checkout master > /dev/null
22+
tig merge mybranch > /dev/null
23+
24+
cat > ${tdir}/expected.txt <<EOF
25+
Branched World
26+
Hello World
27+
Goodbye World
28+
EOF
29+
30+
if diff -u ${tdir}/expected.txt file.txt; then
31+
echo "$0: PASSED"
32+
else
33+
echo "$0: FAILED"
34+
fi
35+
36+
popd > /dev/null
37+
38+
rm -fr ${tdir}

tig.py

Lines changed: 111 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
tig diff
99
tig log
1010
tig branch
11+
tig merge <branch>
1112
1213
Options:
1314
-b <branch-name> Branch name to checkout.
@@ -21,6 +22,8 @@
2122
import hashlib
2223
import json
2324
import time
25+
import tempfile
26+
import subprocess
2427

2528

2629
def __sha1(content):
@@ -106,24 +109,28 @@ def branch():
106109
print "{0}{1}".format(star, branch)
107110

108111

112+
def __create_commit(content_sha1sum, parents, msg):
113+
sha1sum_commit = __storedb(json.dumps({
114+
"content": content_sha1sum,
115+
"parent": parents,
116+
"log-msg": msg,
117+
"author": os.getenv("USER"),
118+
"time": int(time.time())
119+
}, indent=4))
120+
return sha1sum_commit
121+
122+
109123
def commit(msg):
110124
branch = __get_current_branch()
111125
if branch == None:
112126
print "tig: not at tip"
113127
return
114128

115129
master_rev = __get_branch_commit(branch)
116-
117130
sha1sum_content = __storedb(__read_file("file.txt"))
118-
sha1sum_commit = __storedb(json.dumps({
119-
"content": sha1sum_content,
120-
"parent": master_rev,
121-
"log-msg": msg,
122-
"author": os.getenv("USER"),
123-
"time": int(time.time())
124-
}, indent=4))
125-
131+
sha1sum_commit = __create_commit(sha1sum_content, [master_rev], msg)
126132
__set_branch_commit(branch, sha1sum_commit)
133+
127134
print sha1sum_commit
128135

129136

@@ -138,12 +145,16 @@ def __commit_from_start_point(start_point):
138145
return start_point
139146

140147

141-
def checkout(start_point, new_branch):
148+
def __update_working_copy(start_point):
142149
commit_sha1sum = __commit_from_start_point(start_point)
143150
content_sha1sum = __content_from_commit(commit_sha1sum)
144151
content = __getdb(content_sha1sum)
145152

146153
__write_file("file.txt", content)
154+
return commit_sha1sum
155+
156+
def checkout(start_point, new_branch):
157+
commit_sha1sum = __update_working_copy(start_point)
147158

148159
if new_branch is None:
149160
__set_head(start_point)
@@ -172,11 +183,98 @@ def log():
172183
while True:
173184
commit = json.loads(__getdb(commit_sha1sum))
174185
print commit_sha1sum[:6], commit["log-msg"]
175-
commit_sha1sum = commit["parent"]
186+
commit_sha1sum = commit["parent"][0]
176187
if commit_sha1sum == "0":
177188
break
178189

179190

191+
def __get_common_ancestor(commit1, commit2):
192+
if commit1 == commit2:
193+
return
194+
195+
ancestors1 = [commit1]
196+
ancestors2 = [commit2]
197+
198+
while True:
199+
commit_obj = json.loads(__getdb(commit1))
200+
parent_commit = commit_obj["parent"][0]
201+
if parent_commit in ancestors2:
202+
return parent_commit
203+
204+
ancestors1.append(parent_commit)
205+
commit1 = parent_commit
206+
207+
commit_obj = json.loads(__getdb(commit2))
208+
parent_commit = commit_obj["parent"][0]
209+
if parent_commit in ancestors1:
210+
return parent_commit
211+
212+
ancestors2.append(parent_commit)
213+
commit2 = parent_commit
214+
215+
return None
216+
217+
218+
def __diff3(mine_sha1sum, orig_sha1sum, your_sha1sum):
219+
tmpdir = tempfile.mkdtemp()
220+
221+
diff3_files = [("mine", mine_sha1sum),
222+
("orig", orig_sha1sum),
223+
("your", your_sha1sum)]
224+
225+
for filename, sha1sum in diff3_files:
226+
path = os.path.join(tmpdir, filename)
227+
content_sha1sum = __content_from_commit(sha1sum)
228+
__write_file(path, __getdb(content_sha1sum))
229+
230+
conflicted = False
231+
merged_path = os.path.join(tmpdir, "merged")
232+
with open(merged_path, "w") as merged_file:
233+
ret = subprocess.call(["diff3", "-A", "-m", "mine", "orig", "your"],
234+
cwd=tmpdir, stdout=merged_file)
235+
if ret != 0:
236+
conflicted = True
237+
238+
return __read_file(os.path.join(merged_path)), conflicted
239+
240+
241+
def merge(branch):
242+
mine_sha1sum = __get_head_commit()
243+
your_sha1sum = __get_branch_commit(branch)
244+
245+
ca_sha1sum = __get_common_ancestor(mine_sha1sum, your_sha1sum)
246+
if not ca_sha1sum:
247+
print "tig: failed to find common ancestor"
248+
return
249+
250+
if ca_sha1sum == your_sha1sum:
251+
print "tig: up-to-date"
252+
return
253+
254+
if ca_sha1sum == mine_sha1sum:
255+
print "tig: fast-forward merge"
256+
mine_branch = __get_current_branch()
257+
__set_branch_commit(mine_branch, your_sha1sum)
258+
__update_working_copy(mine_branch)
259+
return
260+
261+
# True Merge
262+
merged, conflicted = __diff3(mine_sha1sum, ca_sha1sum, your_sha1sum)
263+
__write_file("file.txt", merged)
264+
265+
if not conflicted:
266+
content_sha1sum = __storedb(merged)
267+
commit_sha1sum = __create_commit(content_sha1sum,
268+
[mine_sha1sum, your_sha1sum],
269+
"Merged changes from '{0}'".format(branch))
270+
mine_branch = __get_current_branch()
271+
__set_branch_commit(mine_branch, commit_sha1sum)
272+
print commit_sha1sum
273+
else:
274+
# FIXME: This needs to be handled.
275+
print "tig: merge conflict occurred"
276+
277+
180278
def main():
181279
args = docopt.docopt(__doc__)
182280
if args["commit"]:
@@ -191,6 +289,8 @@ def main():
191289
log()
192290
elif args["branch"]:
193291
branch()
292+
elif args["merge"]:
293+
merge(args["<branch>"])
194294
else:
195295
# Not reached
196296
pass

0 commit comments

Comments
 (0)